FreeGridv0.8.1English

편집 규칙과 수식

셀 잠금, 시간 편집기, 고를 수 없는 옵션, 중복 값 검증, 셀 수식, 변경 취소 이벤트, 전체 검사.

이 데모의 코드

import { createGrid, type ColumnDef, type Row } from '@freegridjs/core';
import { button, separator, text, toggle, 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 data: Row[] = Array.from({ length: 30 }, (_, i) => ({
    sku: `SKU-${String(100 + i)}`,
    name: [h('Pen', '펜'), h('Notebook', '노트'), h('Stapler', '스테이플러'), h('Marker', '마커'), h('Tape', '테이프')][i % 5],
    qty: 1 + ((i * 7) % 12),
    price: 2 + ((i * 13) % 40),
    // a formula: the cell shows the result
    total: i % 3 === 0 ? '=ROUND(qty*price*1.1, 0)' : '=qty*price',
    pickup: ['09:30', '13:00', '16:45'][i % 3],
    status: ['open', 'hold', 'closed'][i % 3],
    locked: i % 4 === 3,
  }));
  // a duplicate to find with validate()
  data[5].sku = data[2].sku;

  const columns: ColumnDef[] = [
    { field: 'sku', header: 'SKU', width: 110, editable: true, unique: true, required: true },
    { field: 'name', header: h('Item', '품목'), width: 120, editable: true },
    // locked rows cannot be edited
    { field: 'qty', header: h('Qty', '수량'), type: 'number', width: 90, editable: ({ row }) => !row.locked,
      cellClass: ({ row }) => (row.locked ? 'demo-locked' : undefined) },
    { field: 'price', header: h('Price', '단가'), type: 'number', width: 90, editable: ({ row }) => !row.locked },
    { field: 'total', header: h('Total (formula)', '금액 (수식)'), type: 'number', format: '#,##0', width: 140, editable: true, formulas: true },
    { field: 'pickup', header: h('Pickup', '수령 시각'), width: 110, editable: true, editor: { type: 'time' } },
    { field: 'status', header: h('Status', '상태'), width: 120, editable: true,
      editor: { type: 'select', options: [
        { value: 'open', label: h('Open', '진행') },
        { value: 'hold', label: h('On hold', '보류') },
        { value: 'closed', label: h('Closed (admin only)', '종료 (관리자 전용)'), disabled: true },
      ] } },
  ];
  const grid = createGrid(ctx.host, { columns, data, locale: lang });

  let blockNegative = true;
  grid.on('beforeCellChange', e => {
    if (blockNegative && typeof e.newValue === 'number' && e.newValue < 0) {
      e.cancel();
      ctx.log(h(`beforeCellChange → refused ${e.newValue} in ${e.colId}`, `beforeCellChange → ${e.colId}의 ${e.newValue} 거부`));
    }
  });
  grid.on('cellValueChanged', e => ctx.log(`cellValueChanged → ${e.colId}: ${String(e.oldValue)} → ${String(e.newValue)}`));
  grid.on('validationFailed', e => ctx.log(`validationFailed → ${e.colId}: ${e.message}`));

  text(ctx, h('Gray rows are locked. Total holds formulas like =qty*price: edit it to see the formula. Closed is a disabled option.',
    '회색 행은 수정할 수 없습니다. 금액 칸에는 =qty*price 같은 수식이 들어 있어 편집하면 수식이 보입니다. "종료"는 고를 수 없는 옵션입니다.'));
  separator(ctx);
  button(ctx, h('Check all rows (validate)', '전체 검사 (validate)'), () => {
    const bad = grid.validate();
    ctx.log(bad.length ? h(`${bad.length} problem(s):`, `문제 ${bad.length}건:`) : h('all rows are valid', '모든 행이 올바릅니다'));
    for (const b of bad) ctx.log(`  ${h('row', '행')} ${b.rowIndex + 1} · ${b.colId}: ${b.message}`);
  });
  toggle(ctx, h('Refuse negative numbers', '음수 입력 막기'), true, on => { blockNegative = on; });
  return grid;
}