FreeGridv1.6.0한국어

Spreadsheet tools

Set merge: true on a column and consecutive rows with the same value become one tall cell, which is handy for reports grouped by region or team. Sort by…

Live demoSpreadsheet tools

Merged cells, a fill handle, a status bar with totals, pinned rows and Ctrl+F find.

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 });
  // Sorted by country so identical countries sit together and merge into one tall cell.
  const data = makeRows(400).sort((a, b) => a.country.localeCompare(b.country) || a.company.localeCompare(b.company));
  const money = { type: 'number', format: '#,##0', width: 110, editable: true } as const;
  const columns: ColumnDef[] = [
    { field: 'country', header: h('Country', '국가'), width: 140, pinned: 'left', merge: true },
    { field: 'company', header: h('Company', '회사'), width: 140, merge: true },
    { field: 'name', header: h('Name', '이름'), width: 160, editable: true },
    { field: 'q1', header: h('Q1 revenue', '1분기 매출'), ...money, wrapHeaderText: true },
    { field: 'q2', header: h('Q2 revenue', '2분기 매출'), ...money, wrapHeaderText: true },
    { field: 'q3', header: h('Q3 revenue', '3분기 매출'), ...money, wrapHeaderText: true },
    { field: 'q4', header: h('Q4 revenue', '4분기 매출'), ...money, wrapHeaderText: true },
    { field: 'joined', header: h('Joined', '가입일'), type: 'date', width: 120, editable: true },
  ];
  const grid = createGrid(ctx.host, {
    columns,
    data,
    locale: lang,
    theme: 'erp',
    statusBar: true, // counts and totals of the selected cells
    fillHandle: true, // drag the blue corner to fill
    pinnedTopRows: [{ country: h('Target', '목표'), name: '—', q1: 50000, q2: 55000, q3: 60000, q4: 65000 }],
  });
  grid.on('cellValueChanged', e => ctx.log(`cellValueChanged → ${e.colId}: ${String(e.oldValue)} → ${String(e.newValue)}`));

  text(ctx, h('Drag across Q1–Q4 cells to see totals below. Pull the blue corner down to fill. Ctrl+F finds.',
    '1~4분기 셀을 드래그하면 아래에 합계가 나옵니다. 파란 모서리를 끌어내려 채워 보세요. Ctrl+F로 찾기.'));
  separator(ctx);
  button(ctx, h('Find "Granite"', '"Granite" 찾기'), () => ctx.log(`find → ${grid.find('Granite')} matches`));
  button(ctx, h('Next match', '다음 찾기'), () => grid.findNext());
  button(ctx, h('Clear find', '찾기 지우기'), () => grid.clearFind());
  button(ctx, h('Fill Q1 of the first 10 rows with a series', '첫 10행 1분기를 연속값으로 채우기'), () => {
    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;
}

Set merge: true on a column and consecutive rows with the same value become one tall cell, which is handy for reports grouped by region or team. Sort by that column first. statusBar: true adds a bar that shows row counts and the count, sum, average, minimum and maximum of the selected cells. Drag the small square at the corner of a selection to fill editable cells: numbers and dates continue their step, text ending in a number counts up, and anything else repeats.

Use pinnedTopRows and pinnedBottomRows for rows that should never scroll away, such as targets or references. Ctrl+F opens a find bar inside the grid; find() does the same from code.

createGrid(el, {
  columns: [
    { field: 'region', merge: true },                 // identical neighbours become one tall cell
    { field: 'team', merge: (a, b, ra, rb) => a === b && ra.region === rb.region },
    { field: 'q1', type: 'number', editable: true, wrapHeaderText: true },
  ],
  data: rowsSortedByRegion,
  statusBar: true,     // "1,000 rows · Count 12  Sum 48,000  Average 4,000 …"
  fillHandle: true,    // drag the corner: 1, 2 → 3, 4, 5 …; Mon → copies; "item 7" → "item 8"
  pinnedTopRows: [{ region: 'Target', q1: 50000 }],
});

grid.find('Seoul');    // highlights matches, focuses the first; Ctrl+F opens the same as a bar
grid.findNext();