헤더와 컬럼
comins-table@0.2.2
아래 Playground 경로는 해당 모듈 저장소에서 개발 서버를 실행한 뒤 사용하는 로컬 경로입니다. 현재 문서 사이트의 실행 링크는 아닙니다.
0.2.2부터 렌더링의 표준 경로는 .render입니다. 기존 속성은 0.x에서 유지하며 1.0.0 제거 대상으로 안내합니다. 전환 규칙을 참고하십시오.
Header는 표시/숨김, DOM props, label, column boundary resize, column move, keyboard sort, aria-sort, animated sort indicator, layout save/load를 제공합니다. Layout save/load는 ref method로 처리합니다.
const tableRef = useRef<CominsTableRef<Row>>(null);
<CominsTable ref={tableRef} columns={[ { field: "name", header: { props: { className: "name-header", title: "Full name" } }, label: "Full Name", sort: true, }, { field: "age", label: "Age", sort: true }, ]} data={data} getRowId={(row) => row.id} onChangeColumnLayout={(layout) => setLayout(layout)} onChangeSort={(sort) => setSort(sort)} showHeader={showHeader}/>
const saved = tableRef.current?.getColumnLayout();tableRef.current?.setColumnLayout(saved);tableRef.current?.setSortState({ columnId: "age", direction: "desc" });tableRef.current?.clearSort();Multi-column Sort
섹션 제목: “Multi-column Sort”기본값은 기존 단일 정렬입니다. multiSort를 명시하면 Shift 조작으로 우선순위가 있는 복수 정렬 조건을 만들 수 있고 onChangeSortModel로 전체 모델을 확인할 수 있습니다.
const [sortModel, setSortModel] = useState<CominsSortModel>([]);
<CominsTable ref={tableRef} columns={columns} data={data} multiSort onChangeSortModel={setSortModel}/>
tableRef.current?.setSortModel([ { columnId: "department", direction: "asc" }, { columnId: "salary", direction: "desc" },]);동작 기준:
- 일반 click 또는
Enter/Space는 기존 단일none -> asc -> desc -> nonecycle을 유지합니다. Shift와 click 또는Enter/Space를 함께 사용하면 새asc조건 추가, 기존 조건 방향 변경,desc조건 제거를 수행합니다.- Header 숫자 badge는 1부터 시작하는 비교 우선순위입니다. 중간 조건을 제거하면 이후 우선순위가 자동으로 당겨집니다.
getSortModel()/setSortModel(model)은 전체 모델을 조회·복원합니다. 기존getSortState()/setSortState(rule)은 단일 호환 API이며setSortState는 전체 모델을 하나의 조건으로 교체합니다.clearSort()는 전체 조건을 제거합니다.onChangeSort는 첫 번째 조건을,onChangeSortModel은 전체 모델 변경을 관찰합니다.- 숨겨진 sortable Column의 조건은 유지하고, 제거되거나
sort: false가 된 Column의 조건은 모델에서 제거합니다. - 2 Depth Parent Header는 정렬되지 않으며 sortable child Column은 동일하게 복수 정렬에 참여합니다.
- Tree Grid는 parent와 descendant를 평탄화하지 않고 각 sibling 집합에 같은 복합 comparator를 적용합니다.
Multi-column Sort 중에는 첫 번째 조건에만 aria-sort="ascending" | "descending"를 적용합니다. ARIA는 복수 정렬 우선순위를 직접 표현하지 않으므로 2순위 이후 Header에는 접근 가능한 priority 설명을 함께 제공합니다.
2 Depth Header
섹션 제목: “2 Depth Header”2 Depth Header는 기존 flat columns를 유지하고, 별도 columnGroups로 부모 header를 정의합니다.
<CominsTable columns={[ { id: "name", field: "name", label: "이름", sort: true }, { id: "age", field: "age", label: "나이", sort: true }, { id: "role", field: "role", label: "역할" }, ]} columnGroups={[ { id: "profile", label: "프로필", children: ["name", "age"], }, ]} data={data}/>동작 기준:
- 최대 2 Depth만 지원합니다. Group 안에 group을 넣는 N-depth 구조는 지원하지 않습니다.
- Parent header는
field,sort, Cell/Header component slot을 갖지 않습니다. - Parent resize는 child column width 비율을 유지하면서 child widths를 함께 변경합니다.
- Child
minWidth/maxWidth에 걸리면 clamp 후 남은 width delta를 다른 child column에 재분배합니다. - Parent move는 child columns를 하나의 block으로 이동합니다.
- Child column을 다른 group으로 이동하거나 group 밖으로 이동하는 동작은 지원하지 않습니다.
- Parent group hide/show는 header만 숨기는 것이 아니라 child columns 자체의 effective visibility를 변경합니다.
- Parent group을 다시 표시해도 child column의 개별 hidden 상태는 유지됩니다.
- Group 없는 column은 parent row에서
rowSpan=2로 표시됩니다. - Header 전체 hide/show(
showHeader)는 2 Depth와 별개이며 전체 header area를 표시하거나 제거합니다. - 2 Depth parent header cell은 child header 상단 border와 선이 겹치지 않도록 하단 border를 출력하지 않습니다.
Header custom UI는 header.render 또는 header.components로 렌더링합니다. renderer가 있으면 label과 components를 모두 대체합니다.
{ field: "name", label: "Name", header: { render: ({ column }) => <span>{column.label}</span>, },}{ field: "role", label: "Role", header: { components: [ { type: "select", direction: "right", options: [ { label: "Owner", value: "Owner" }, { label: "Viewer", value: "Viewer" }, ], props: { value: "Owner" }, onValueChange: ({ value }) => setHeaderFilter(value), }, ], },}Header components는 배열 순서대로 렌더링되며 direction으로 label 왼쪽 또는 오른쪽에 붙입니다. 기본값은 direction: "left", align: "center"입니다. Built-in component 이벤트는 Header sort, resize, move 이벤트로 전파되지 않습니다. input은 Cell input과 동일하게 Enter 또는 Blur 시점에만 변경 값을 commit합니다.
Phase 2 Header components는 button, input, checkbox, radio, select, toggle, progress, menu를 지원합니다. menu는 Header 전용이며 document.body portal과 fixed position으로 버튼 바로 아래에 popover를 표시합니다. popup은 built-in으로 제공하지 않습니다.
{ field: "status", label: "상태", header: { components: [ { type: "menu", direction: "right", items: [ { label: "상태 확인", value: "status-check" }, { type: "divider" }, { label: "도움말", type: "label" }, ], onBeforeChange: ({ open }) => open, onOpenChange: ({ open }) => setMenuOpen(open), onSelect: ({ value }) => handleHeaderMenu(value), }, ], },}동작 기준:
- 컬럼과 컬럼 경계는 resize 영역이며 cursor는
col-resize입니다. - Resize line은 평소에는 숨겨지고, 컬럼 경계에 hover하거나 resize 중일 때만 표시됩니다.
- 최초 resize width는 현재 렌더링된
TH의 실제 너비를 기준으로 계산합니다. - TH body 영역은 column move 후보 영역이며 cursor는
grab입니다. - 마우스 왼쪽 버튼을 누른 상태에서 수평 이동이 수직 이동보다 크고 6픽셀에 도달하면 column move mode가 즉시 활성화되며 cursor는
grabbing입니다. - 6픽셀 미만에서 Pointer Up하면 일반 click과 sort 동작을 유지합니다. 수직 이동 의도가 확인되면 대기 중인 column move와 sort를 모두 취소합니다.
- Column move mode에서는 원본 Header를 현재 Header 배경보다 더 어두운 점선 source placeholder로 표시하고, plain Column 또는 Group 이름을 계속 보이게 하며 이동 중인 header ghost와 drop marker를 함께 표시합니다. 이 presentation-only source label은 custom Header renderer를 호출하지 않습니다.
- 기본적으로 Header 좌측에 24px 이동 Handle과 15px 장식용 Radix SVG icon(
aria-hidden="true")을 표시하며 Handle은 즉시 이동 mode를 활성화합니다. Header 전체의 기존 6픽셀 gesture도 유지합니다.showColumnMoveHandle={false}이면 Handle만 숨깁니다. - Column 또는 Group에
lockPosition: true를 지정하면 해당 위치를 고정합니다. 잠긴 Header는 Handle을 표시하지 않고 직접 이동할 수 없으며, 다른 Header도 잠긴 위치를 가로지르거나 밀어낼 수 없습니다. - 같은 depth와 parent의 유효한 target은 파란색 2px 테두리, marker, 저투명도 배경으로 표시합니다. depth, parent 또는 position lock을 위반한 target은 같은 형태의 붉은색 표시와
not-allowedcursor를 사용합니다. Header content 자체의 opacity는 변경하지 않습니다. - 유효한 이동을 반영하면 Header, 현재 렌더링된 Body Cell과 Summary Cell이 새 수평 위치로 짧게 이동합니다.
prefers-reduced-motion: reduce에서는 transition을 적용하지 않습니다. - built-in Header control을 유지하기 위한 기본 최소 Column 너비는 88px입니다. Label은 말줄임될 수 있고, 더 넓은 custom Header content는 별도
minWidth를 지정합니다. - 유효한 target 위에서 Pointer Up한 경우에만 이동을 반영합니다. Pointer cancel,
Escape, window blur는 layout 변경 없이 취소합니다. - non-mouse pointer 입력은 1초 long-press 호환 동작을 유지합니다.
- Parent group도 동일한 동작을 사용하며 child columns를 하나의 block으로 이동합니다.
- Sort cycle은
none -> asc -> desc -> none입니다. - Sort 가능한 Header는 focus 가능하며
Enter또는Space로 sort cycle을 실행합니다. - Sort 가능한 Header는
aria-sort="none" | "ascending" | "descending"상태를 노출합니다. - 정렬 표시는
asc,desc, 미정렬 상태별 장식용 Radix SVG icon(aria-hidden="true")을 사용합니다. Header의 기존 click 및Enter/Spacekeyboard cycle과aria-sort노출 계약은 유지합니다. - Header menu 버튼 클릭은 sort, resize, column move를 발생시키지 않습니다.
- Header menu는 바깥 클릭,
Escape, item 선택 시 닫히며onBeforeChange가false를 반환하면 open/close를 취소합니다. - Multi-column Sort는
multiSort를 명시한 경우Shift조작으로 활성화되며 각 Header에 우선순위 badge를 표시합니다.
Column Filter
섹션 제목: “Column Filter”columns[].filter를 설정하고 controlled columnFiltering state를 전달하면 Header 우측의 sort metadata 뒤와 resize 앞에 Filter control을 렌더링합니다. Semantic button과 fixed popover는 pointer, click, double-click, keyboard event를 Header sort, resize, move에서 격리합니다. Application이 전체 Filter model과 현재 열린 Column ID를 모두 소유합니다.
지원 operator, read-only 동작, 정렬, Summary와 Row Grouping 결합은 Column Filtering guide와 /examples/column-filtering (/examples/column-filtering)에서 확인합니다.
Playground 검증 기준:
헤더예제는 resize와 move를 같은 pointer 흐름에서 검증합니다.- Header 숨김/표시 예제는 전체 Header area toggle과 컬럼별 Checkbox Select Box를 함께 제공합니다. Select Box에서 선택 해제된 column id는
columnsprop에서 제외되어 해당 컬럼 전체가 숨겨집니다. - Header 설정 저장/불러오기 예제는 column layout과 Select Box의 표시 column id를 함께 저장합니다. 불러오기 시 column order/width와 숨김/표시 상태가 같이 복원됩니다.
- 2 Depth group 표시/숨김은 parent Group별 Checkbox로 검증합니다. Parent를 끄면 child 전체가 숨겨지고 다시 켜면 끄기 전 child 선택이 복원됩니다.
- 컬럼 동적 표시 예제는 child Column MultiSelect와 parent Group Checkbox를 조합합니다. 최종 표시 Column은 선택된 child와 활성 parent의 교집합이며 1Depth와 2Depth 모두 같은 동작을 확인합니다.
- Header sort 접근성은 mouse click, keyboard
Enter/Space,aria-sort, sort indicator 상태를 함께 검증합니다. - Multi-column Sort 예제는 2 Depth child Column의
Shiftclick/keyboard 조작, priority badge, 전체 Sort Model JSON을 함께 검증합니다. - Resize는 width 변경, 최초 drag jump 없음, column move 미발생을 함께 확인합니다.
- Move는 마우스 수평 6픽셀 활성화, source placeholder, 이동 ghost, drop marker, 의도한 column order 변경을 함께 확인합니다.
- 2 Depth Header는 parent resize 비율 유지, parent block move, child group 밖 이동 미지원, ungrouped
rowSpan=2, header/body leaf cell geometry alignment를 함께 확인합니다. - Virtualized mode에서는 header/body가 다른 table이어도 resize 후 column left/width가 같아야 합니다.
- 사용자가 header 위치, resize, sort 표시 문제를 지적한 경우 Playwright assertion 외에 screenshot 또는 DOM geometry evidence를 report에 남깁니다.
/api/ref (/api/ref) live 예제는 Flat Table에서 setSortModel, clearSort, getColumnLayout, setColumnLayout을 실행합니다. Layout method는 현재 Column 순서, 표시 상태, width를 저장하고 복원합니다.