Fill handle
Drag the corner of a selection to fill editable cells: numbers and dates continue their step and numbered text counts up, in one undo step.
Paid feature · Fill handle. Free for 30 days on a new domain; after that it needs a free personal key or a company key. See pricing
Live demoFill handle
Drag the corner of a selection to continue numbers, dates and numbered text.
Code for this demo
import { createGrid, type ColumnDef } from '@freegridjs/core';
import { makeRows } from '../data';
import { button, separator, text, tr, type DemoContext } from '../ui';
export default function mount(ctx: DemoContext) {
const { lang } = ctx;
const h = (en: string, ko: string) => tr(lang, { en, ko });
const money = { type: 'number', format: '#,##0', width: 110, editable: true } as const;
const columns: ColumnDef[] = [
{ field: 'name', header: h('Name', '이름'), width: 160, editable: true },
{ field: 'q1', header: h('Q1 revenue', '1분기 매출'), ...money },
{ field: 'q2', header: h('Q2 revenue', '2분기 매출'), ...money },
{ field: 'q3', header: h('Q3 revenue', '3분기 매출'), ...money },
{ field: 'q4', header: h('Q4 revenue', '4분기 매출'), ...money },
{ field: 'joined', header: h('Joined', '가입일'), type: 'date', width: 120, editable: true },
{ field: 'country', header: h('Country (read-only)', '국가 (읽기 전용)'), width: 150 },
];
const grid = createGrid(ctx.host, {
columns,
data: makeRows(400),
locale: lang,
theme: 'excel',
fillHandle: true, // drag the square at the corner of the selection
});
grid.on('cellValueChanged', e => ctx.log(`cellValueChanged → ${e.colId}: ${String(e.oldValue)} → ${String(e.newValue)}`));
text(ctx, h('Select one or two cells and pull the small square at the corner down or across. Read-only cells are left alone.',
'셀 한두 개를 고른 뒤 모서리의 작은 사각형을 아래나 옆으로 끌어 보세요. 읽기 전용 셀은 그대로 둡니다.'));
separator(ctx);
button(ctx, h('Fill Q1 of the first 10 rows with 1,000, 2,000 …', '첫 10행 1분기를 1,000, 2,000 …으로 채우기'), () => {
grid.setRange({ start: { rowIndex: 0, colId: 'q1' }, end: { rowIndex: 0, colId: 'q1' } });
grid.pasteText('1000\n2000');
grid.setRange({ start: { rowIndex: 0, colId: 'q1' }, end: { rowIndex: 1, colId: 'q1' } });
grid.fillRange({ start: { rowIndex: 0, colId: 'q1' }, end: { rowIndex: 9, colId: 'q1' } });
});
button(ctx, h('Undo', '실행 취소'), () => grid.undo());
return grid;
}Select one or more cells and a small square appears at the corner of the selection. Drag it down, up or sideways and the cells you pass are filled, the way a spreadsheet does it: numbers and dates continue their step (1, 2 → 3, 4, 5; Jan 1, Jan 8 → Jan 15), text that ends in a number counts up ("item 7" → "item 8"), and anything else repeats. Only editable cells change; ids and computed columns are left alone.
A fill is one undo step and fires cellValueChanged for every cell, like an edit, so the change log and your save logic see it. Values that break a validation rule are skipped. fillRange() does the same from code, and fillHandle: false hides the square.
createGrid(el, {
columns: [
{ field: 'month', editable: true }, // "Jan", "Feb" → copies; "item 7" → "item 8"
{ field: 'q1', type: 'number', editable: true }, // 1, 2 → 3, 4, 5 …
{ field: 'due', type: 'date', editable: true }, // dates keep their step
],
data,
fillHandle: true, // default; false hides the square at the corner of the selection
});
// the same from code: from the selected cells into a larger range (one undo step)
grid.setRange({ start: { rowIndex: 0, colId: 'q1' }, end: { rowIndex: 1, colId: 'q1' } });
grid.fillRange({ start: { rowIndex: 0, colId: 'q1' }, end: { rowIndex: 9, colId: 'q1' } });The fill handle works on cell ranges.
Options and methods
| Name | Type | Description |
|---|---|---|
fillHandle | boolean | The small square at the corner of the selection (default true). |
fillRange(target) | void | Fill from the selected cells (or the focused cell) into target, which must contain them; one undo step. |