에디터 아이콘 (Editor Icons)

셀 값 옆에 드롭다운·달력·검색 아이콘을 표시하고 editor 시작 또는 독립 callback을 연결하는 방법을 설명합니다.

#editorIcon#visibility#icon-callback#accessibility
검토일: 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, CheckIcon, ChevronDownIcon } from './editing/editorIcons';
import {
  applyEditingDataChange,
  cloneEditingOrders,
  type EditingOrder,
  withEditingCellClasses,
} from './editing/shared';

const statusEditor = createSelectEditorPlugin<EditingOrder, EditingOrder['status']>({
  id: 'icon-status',
  options: [
    { value: '접수', label: '접수' },
    { value: '진행', label: '진행' },
    { value: '완료', label: '완료' },
  ],
});

const dateEditor = createDateEditorPlugin<EditingOrder>({ id: 'icon-date' });

export default function EditorIconExample() {
  const [data, setData] = React.useState(cloneEditingOrders);
  const [lastAction, setLastAction] = React.useState('아이콘을 눌러 동작을 확인하세요.');
  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: 'status',
        label: '항상 표시',
        width: 145,
        editable: true,
        editor: statusEditor,
        editTrigger: 'click',
        editorIcon: { render: <ChevronDownIcon />, ariaLabel: '상태 선택', visibility: 'always' },
      },
      {
        key: 'deliveryDate',
        label: 'hover 표시',
        width: 165,
        editable: true,
        editor: dateEditor,
        editorIcon: { render: <CalendarIcon />, ariaLabel: '납기일 선택', visibility: 'hover' },
      },
      {
        key: 'note',
        label: 'callback 아이콘',
        width: 210,
        editable: true,
        editor: { type: 'text' },
        editorIcon: {
          render: <CheckIcon />,
          ariaLabel: '메모 확인 완료',
          visibility: 'active',
          onClick: async ({ index, commit }) => {
            setLastAction(`${index + 1}행 메모에 확인 표시를 추가했습니다.`);
            await commit([{ key: 'note', value: '확인 완료' }]);
          },
        },
      },
    ]),
    [],
  );

  return (
    <div className='flex min-h-0 flex-col gap-3'>
      <div className='rounded-lg border border-slate-200 bg-slate-50 p-3 text-sm leading-6 text-slate-700'>
        <code>onClick</code>이 없는 아이콘은 연결된 editor를 시작합니다. callback 아이콘은 editor 대신 자체 작업을 실행하며
        동일한 <code>commit(changes[])</code>으로 값을 저장합니다.
        <output aria-live='polite' className='mt-1 block text-xs text-blue-700'>{lastAction}</output>
      </div>
      <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>
  );
}

editorIcon은 편집 중이 아닐 때도 셀 값 옆에 입력 가능성을 보여주는 핸들입니다. Select 화살표와 lookup 검색 아이콘을 별도 API로 나누지 않고 같은 설정을 사용합니다.

Editor를 여는 아이콘

onClick을 생략하면 아이콘 클릭이 기존 column.editor를 시작합니다.

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

Callback을 실행하는 아이콘

onClick이 있으면 기본 editor 대신 callback 세션을 시작합니다. callback에는 DOM 이벤트가 아니라 셀 문맥과 공통 commit/cancel이 전달됩니다.

editorIcon: {
  render: <SearchIcon />,
  ariaLabel: ({ values }) => `${values.customerName} lookup 열기`,
  onClick: ({ commit, cancel }) => {
    openLookup({
      onSelect: customer => commit([
        { key: 'customerCode', value: customer.code },
        { key: 'customerName', value: customer.name },
      ]),
      onClose: cancel,
    });

    return () => closeLookup();
  },
}

반환 함수는 commit, cancel, 새 상호작용, unmount로 세션이 끝날 때 한 번 호출되는 cleanup입니다.

표시 조건

visibility 동작
always 항상 표시, 기본값
hover 셀을 가리킬 때 표시
active 활성 셀일 때 표시

아이콘 종류를 editor에서 자동 추론하지 않습니다. 같은 Select라도 제품마다 아이콘과 접근성 이름이 다르므로 render는 필수입니다. 편집과 무관한 삭제·상세 이동 버튼은 itemRender에 두는 편이 역할이 명확합니다.