내장·기본 제공 에디터 (Built-in Editors)

내장 text와 기본 제공 Select·Date plugin의 설정, 값 변환, 아이콘 연결 방법을 설명합니다.

#text-editor#select-editor#date-editor#parseValue#formatValue
검토일: 2026-08-20
GitHub
import * as React from 'react';
import { AXDataGrid, type AXDGColumn } from '@axboot/datagrid';
import { createDateEditorPlugin, createSelectEditorPlugin } from '@axboot/datagrid/editors';
import DataGridContainer from '../components/DataGridContainer';
import { useContainerSize } from '../hooks/useContainerSize';
import { CalendarIcon, ChevronDownIcon } from './editing/editorIcons';
import {
  applyEditingDataChange,
  cloneEditingOrders,
  type EditingOrder,
  withEditingCellClasses,
} from './editing/shared';

function formatDate(value: unknown) {
  if (typeof value !== 'string') return '';
  const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
  return match ? `${match[1]}.${match[2]}.${match[3]}` : value;
}

const statusEditor = createSelectEditorPlugin<EditingOrder, EditingOrder['status']>({
  id: 'built-in-status',
  ariaLabel: '주문 상태 선택',
  options: [
    { value: '접수', label: '접수' },
    { value: '진행', label: '진행' },
    { value: '완료', label: '완료' },
  ],
});

const deliveryDateEditor = createDateEditorPlugin<EditingOrder>({
  id: 'built-in-delivery-date',
  ariaLabel: '납기일 선택',
  min: '2026-08-01',
  max: '2026-12-31',
});

export default function BuiltInEditorsExample() {
  const [data, setData] = React.useState(cloneEditingOrders);
  const containerRef = React.useRef<HTMLDivElement>(null);
  const { width, height } = useContainerSize(containerRef);

  const columns = React.useMemo<AXDGColumn<EditingOrder>[]>(
    () => withEditingCellClasses<EditingOrder>([
      { key: 'orderCode', label: '주문 코드', width: 145, editable: false },
      {
        key: 'customerName',
        label: '내장 text',
        width: 180,
        editable: true,
        editor: {
          type: 'text',
          inputProps: { maxLength: 50, autoComplete: 'off' },
        },
      },
      {
        key: 'status',
        label: '기본 Select',
        width: 150,
        editable: true,
        editTrigger: 'click',
        editor: statusEditor,
        editorIcon: { render: <ChevronDownIcon />, ariaLabel: '주문 상태 선택', visibility: 'always' },
      },
      {
        key: 'deliveryDate',
        label: '기본 Date',
        width: 170,
        editable: true,
        editTrigger: 'click',
        editor: deliveryDateEditor,
        itemRender: ({ value }) => formatDate(value),
        editorIcon: { render: <CalendarIcon />, ariaLabel: '납기일 선택', visibility: 'always' },
      },
    ]),
    [],
  );

  return (
    <div className='flex min-h-0 flex-col gap-3'>
      <p className='m-0 rounded-lg border border-slate-200 bg-slate-50 p-3 text-sm leading-6 text-slate-700'>
        text 입력은 라이브러리 내장 편집기이며 Select와 Date는 <code>@axboot/datagrid/editors</code>가 제공하는 의존성
        없는 plugin입니다. 화살표와 달력 아이콘을 누르거나 셀을 한 번 클릭해 선택하세요.
      </p>
      <DataGridContainer ref={containerRef} style={{ height: 340 }}>
        <AXDataGrid<EditingOrder>
          width={width}
          height={height}
          data={data}
          columns={columns}
          rowKey='id'
          editable
          variant='vertical-bordered'
          onChangeData={(sourceIndex, _columnIndex, values, _column, meta) => {
            setData(current => applyEditingDataChange(current, sourceIndex, values, meta));
          }}
        />
      </DataGridContainer>
    </div>
  );
}

문자열 입력은 내장 text editor를, 정해진 값과 날짜 선택은 @axboot/datagrid/editors의 기본 plugin을 사용합니다. 기본 plugin은 별도 UI 프레임워크 의존성을 추가하지 않습니다.

Text

{
  key: 'quantity',
  editable: true,
  editor: {
    type: 'text',
    inputProps: { inputMode: 'numeric', autoComplete: 'off' },
    formatValue: value => String(value ?? ''),
    parseValue: text => {
      const value = Number(text);
      if (!Number.isFinite(value)) throw new Error('숫자를 입력하세요.');
      return value;
    },
  },
}

parseValue가 예외를 던지면 저장하지 않고 editor를 유지하며 aria-invalid="true"가 설정됩니다. commitOnBlur: false이면 외부 포커스 이동 시 저장하지 않고 취소합니다.

Select와 Date

const statusEditor = createSelectEditorPlugin<Order, Order['status']>({
  id: 'order-status',
  options: [
    { value: 'ready', label: '준비' },
    { value: 'done', label: '완료' },
  ],
});

const dateEditor = createDateEditorPlugin<Order>({
  id: 'delivery-date',
  min: '2026-01-01',
  max: '2026-12-31',
});

factory는 컴포넌트 바깥이나 useMemo 안에서 한 번만 생성하세요. 컬럼 렌더마다 새 plugin 객체를 만들면 입력 컴포넌트가 다시 마운트될 수 있습니다.

기본 Select는 셀 또는 아이콘 클릭으로 editor가 마운트되면 네이티브 옵션 picker를 즉시 엽니다. 자동 열기를 원하지 않으면 factory에 openOnMount: false를 지정할 수 있습니다.

기본 Date는 셀 본문을 클릭하면 숫자 날짜 입력만 활성화하고, editorIcon을 클릭해서 진입한 경우에만 네이티브 달력 picker를 엽니다. 편집 플러그인은 activation 값('cell' | 'editorIcon')으로 두 진입 경로를 구분할 수 있습니다.

{
  key: 'status',
  editable: true,
  editTrigger: 'click',
  editor: statusEditor,
  editorIcon: { render: <ChevronDownIcon />, ariaLabel: '상태 선택' },
}

아이콘의 모양은 editor 종류로 자동 추론하지 않습니다. 제품 디자인 시스템에 맞는 아이콘을 editorIcon.render로 명시합니다.