@topgrid/grid-features 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +85 -0
- package/dist/index.cjs +835 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +655 -0
- package/dist/index.d.ts +655 -0
- package/dist/index.mjs +797 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/multi-sort/useMultiSort.ts","../src/filter-ui/FilterPopover.tsx","../src/filter-ui/FilterIndicator.tsx","../src/filter-ui/TextFilter.tsx","../src/filter-ui/filterFns.ts","../src/filter-ui/NumberFilter.tsx","../src/filter-ui/DateFilter.tsx","../src/filter-ui/SelectFilter.tsx","../src/filter-ui/GlobalSearchInput.tsx","../src/filter-ui/FilterResetButton.tsx"],"names":["useState","useRef","useEffect","useCallback","jsxs","jsx","startOfDay","endOfDay","isWithinInterval","filterFns","FunnelIcon","Fragment","registerLocale","ko","DatePicker","useId"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { UseMultiSortOptions, UseMultiSortResult } from './types';\r\n\r\n/**\r\n * useReactTable 직접 사용자가 다중 정렬 옵션을 구성할 때 사용하는 헬퍼.\r\n *\r\n * @remarks\r\n * `<Grid enableMultiSort />` wrapper 사용자는 이 훅 불필요 — Grid.tsx 내부에서\r\n * `buildTableOptions.ts`가 props.enableMultiSort를 직접 처리. (D6)\r\n *\r\n * C-31 wiring audit: Grid.tsx는 이 훅을 import하지 않음. 외부 소비자용 유틸.\r\n *\r\n * @example\r\n * const { enableMultiSort, isMultiSortEvent } = useMultiSort({ enableMultiSort: true });\r\n * const table = useReactTable({\r\n * data,\r\n * columns,\r\n * getCoreRowModel: getCoreRowModel(),\r\n * getSortedRowModel: getSortedRowModel(),\r\n * enableMultiSort,\r\n * isMultiSortEvent,\r\n * });\r\n */\r\nexport function useMultiSort(opts?: UseMultiSortOptions): UseMultiSortResult {\r\n const enableMultiSort = opts?.enableMultiSort ?? false;\r\n const result: UseMultiSortResult = {\r\n enableMultiSort,\r\n isMultiSortEvent: (e: unknown): boolean => {\r\n if (e !== null && typeof e === 'object' && 'shiftKey' in e) {\r\n return (e as { shiftKey: boolean }).shiftKey === true;\r\n }\r\n return false;\r\n },\r\n };\r\n // C-29: exactOptionalPropertyTypes — undefined를 전달하면 TanStack이 무제한으로 처리하지 않을 수 있음.\r\n // 값이 있을 때만 result에 포함.\r\n if (opts?.maxMultiSortColCount !== undefined) {\r\n result.maxMultiSortColCount = opts.maxMultiSortColCount;\r\n }\r\n return result;\r\n}\r\n","/**\r\n * @topgrid/grid-features — FilterPopover 컴포넌트.\r\n *\r\n * MOD-GRID-09 G-001 AC-003:\r\n * 네이티브 div position:absolute 팝오버 구현 (@radix-ui 없음, D2).\r\n *\r\n * 필수 동작 (D4):\r\n * 1. 외부 클릭 해제 (mousedown on document)\r\n * 2. Escape 키 해제 + trigger 포커스 복귀\r\n * 3. open 시 첫 번째 input 포커스\r\n * 4. close 시 trigger 버튼 포커스 복귀\r\n * 5. z-index z-[50] — sticky header(z-[10], MOD-GRID-02)보다 높음 (D7)\r\n *\r\n * @remarks\r\n * `verbatimModuleSyntax: true` — import type 분리.\r\n * `exactOptionalPropertyTypes: true` — FilterPopoverProps align prop 전달 시\r\n * spread-skip 패턴 사용 (C-29, Section 4.6).\r\n * Tailwind className 전용 (C-5). style={{}} 없음.\r\n */\r\n\r\nimport { useEffect, useRef, useState, useCallback } from 'react';\r\nimport type { FilterPopoverProps } from './types';\r\n\r\n/**\r\n * 텍스트 필터용 Popover 컨테이너.\r\n *\r\n * trigger prop으로 트리거 요소를 받고, children으로 팝오버 내용을 렌더.\r\n * open/close 상태를 내부적으로 관리 (외부 제어 불필요).\r\n *\r\n * @param props.trigger - 팝오버를 열/닫는 트리거 요소 (아이콘 버튼 등)\r\n * @param props.children - 팝오버 내부 콘텐츠 (TextFilter 내용)\r\n * @param props.align - 팝오버 정렬 방향. 'left'(기본) | 'right'\r\n */\r\nexport function FilterPopover({ trigger, children, align = 'left' }: FilterPopoverProps): JSX.Element {\r\n const [open, setOpen] = useState(false);\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n const triggerRef = useRef<HTMLSpanElement>(null);\r\n const contentRef = useRef<HTMLDivElement>(null);\r\n\r\n // 외부 클릭 → 닫기 (D4-1)\r\n useEffect(() => {\r\n if (!open) return;\r\n\r\n const handleMouseDown = (e: MouseEvent): void => {\r\n if (containerRef.current && !containerRef.current.contains(e.target as Node)) {\r\n setOpen(false);\r\n }\r\n };\r\n\r\n document.addEventListener('mousedown', handleMouseDown);\r\n return () => {\r\n document.removeEventListener('mousedown', handleMouseDown);\r\n };\r\n }, [open]);\r\n\r\n // Escape 키 → 닫기 + trigger 포커스 복귀 (D4-2, D4-4)\r\n useEffect(() => {\r\n if (!open) return;\r\n\r\n const handleKeyDown = (e: KeyboardEvent): void => {\r\n if (e.key === 'Escape') {\r\n setOpen(false);\r\n // trigger 안의 버튼 요소에 포커스 복귀\r\n const btn = triggerRef.current?.querySelector<HTMLButtonElement>('button');\r\n btn?.focus();\r\n }\r\n };\r\n\r\n document.addEventListener('keydown', handleKeyDown);\r\n return () => {\r\n document.removeEventListener('keydown', handleKeyDown);\r\n };\r\n }, [open]);\r\n\r\n // open 시 첫 번째 input에 포커스 (D4-3)\r\n useEffect(() => {\r\n if (!open) return;\r\n\r\n // 다음 microtask에서 DOM이 안정된 후 포커스\r\n const id = setTimeout(() => {\r\n const firstInput = contentRef.current?.querySelector<HTMLInputElement>('input, select');\r\n firstInput?.focus();\r\n }, 0);\r\n return () => clearTimeout(id);\r\n }, [open]);\r\n\r\n const handleToggle = useCallback((): void => {\r\n setOpen((prev) => !prev);\r\n }, []);\r\n\r\n // 팝오버 정렬 클래스 (D4-4, Section 4.1)\r\n const alignClass = align === 'right' ? 'right-0' : 'left-0';\r\n\r\n return (\r\n <div ref={containerRef} className=\"relative inline-block\">\r\n {/* 트리거 래퍼 — trigger ReactNode를 감싸 클릭 핸들링 */}\r\n <span\r\n ref={triggerRef}\r\n onClick={handleToggle}\r\n className=\"inline-flex\"\r\n // 트리거 버튼에 포커스 복귀 시 span 내 button을 직접 타겟\r\n >\r\n {trigger}\r\n </span>\r\n\r\n {/* 팝오버 콘텐츠 (D4, D7) */}\r\n {open && (\r\n <div\r\n ref={contentRef}\r\n role=\"dialog\"\r\n aria-label=\"텍스트 필터\"\r\n aria-modal=\"false\"\r\n className={`absolute top-full mt-1 ${alignClass} z-[50] min-w-[220px] overflow-hidden rounded border border-gray-200 bg-white shadow-lg`}\r\n >\r\n {children}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n","/**\r\n * @topgrid/grid-features — FilterIndicator 컴포넌트.\r\n *\r\n * MOD-GRID-09 G-001 AC-004:\r\n * 활성 필터 상태를 헤더에 표시하는 파란 dot 인디케이터.\r\n * `column.getIsFiltered() === true` 일 때만 렌더.\r\n *\r\n * Section 5.2 truth table:\r\n * - isFiltered=true → 파란 dot (w-2 h-2 rounded-full bg-blue-500)\r\n * - isFiltered=false → null (렌더 없음)\r\n *\r\n * @remarks\r\n * Tailwind className 전용 (C-5). style={{}} 없음.\r\n * `jsx: \"react-jsx\"` 환경 — `import React` 불필요.\r\n */\r\n\r\nimport type { FilterIndicatorProps } from './types';\r\n\r\n/**\r\n * 활성 필터 인디케이터 — 파란 dot.\r\n *\r\n * `column.getIsFiltered()` 결과값을 isFiltered prop으로 전달.\r\n * 필터 비활성 시 null 반환 (DOM 요소 없음).\r\n *\r\n * @example\r\n * ```tsx\r\n * <FilterIndicator isFiltered={column.getIsFiltered()} />\r\n * ```\r\n */\r\nexport function FilterIndicator({ isFiltered }: FilterIndicatorProps): JSX.Element | null {\r\n if (!isFiltered) return null;\r\n\r\n return (\r\n <span\r\n aria-label=\"필터 활성\"\r\n aria-hidden=\"false\"\r\n className=\"inline-block w-2 h-2 rounded-full bg-blue-500 flex-shrink-0\"\r\n />\r\n );\r\n}\r\n","/**\r\n * @topgrid/grid-features — TextFilter 메인 컴포넌트.\r\n *\r\n * MOD-GRID-09 G-001:\r\n * 컬럼 헤더 Popover + contains/equals/startsWith/endsWith 연산자 + 필터 인디케이터.\r\n *\r\n * AC-001: TextFilterValue 타입 + column.setFilterValue / getFilterValue 사용\r\n * AC-002: filterFn 등록 패턴 문서화 (Section 12 예시)\r\n * AC-003: FilterPopover 사용 (네이티브 div, D2)\r\n * AC-004: FilterIndicator (파란 dot, column.getIsFiltered())\r\n * AC-005: clear 버튼 → column.setFilterValue(undefined)\r\n * AC-006: tsc 0 error (C-29 spread-skip 패턴 적용)\r\n *\r\n * @remarks\r\n * `verbatimModuleSyntax: true` — import type 분리.\r\n * `exactOptionalPropertyTypes: true` — popoverAlign → FilterPopover align 전달 시\r\n * C-29 spread-skip 패턴 (Section 4.6).\r\n * `noUnusedLocals/noUnusedParameters: true` — 모든 변수 사용.\r\n * Tailwind className 전용 (C-5).\r\n */\r\n\r\nimport { useState, useEffect } from 'react';\r\nimport type { TextFilterProps, TextFilterOperator, TextFilterValue } from './types';\r\nimport { FilterPopover } from './FilterPopover';\r\nimport { FilterIndicator } from './FilterIndicator';\r\n\r\n// ---------------------------------------------------------------------------\r\n// FunnelIcon — 인라인 SVG (신규 dep 없음, D2)\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction FunnelIcon(): JSX.Element {\r\n return (\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 20 20\"\r\n fill=\"currentColor\"\r\n aria-hidden=\"true\"\r\n className=\"w-4 h-4\"\r\n >\r\n <path\r\n fillRule=\"evenodd\"\r\n d=\"M2.628 1.601C5.028 1.206 7.49 1 10 1s4.973.206 7.372.601a.75.75 0 0 1 .628.74v2.288a2.25 2.25 0 0 1-.659 1.59l-4.682 4.683a2.25 2.25 0 0 0-.659 1.59v3.037c0 .684-.31 1.33-.844 1.757l-1.937 1.55A.75.75 0 0 1 9 18.25v-5.757a2.25 2.25 0 0 0-.659-1.591L3.659 6.22A2.25 2.25 0 0 1 3 4.629V2.34a.75.75 0 0 1 .628-.74z\"\r\n clipRule=\"evenodd\"\r\n />\r\n </svg>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// TextFilter 컴포넌트\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 텍스트 필터 UI — 연산자 select + 값 input + clear 버튼.\r\n *\r\n * `FilterPopover` + `FilterIndicator`를 조합한 메인 컴포넌트.\r\n * `column.setFilterValue`로 TanStack columnFilters에 연결.\r\n * 디바운스 300ms (D6, Section 4.5).\r\n *\r\n * @template TData - TanStack Row data 타입.\r\n *\r\n * @example\r\n * ```tsx\r\n * // columnDef header에 렌더:\r\n * header: ({ column }) => (\r\n * <div className=\"flex items-center gap-1\">\r\n * <span>이름</span>\r\n * <TextFilter column={column} defaultOperator=\"contains\" />\r\n * </div>\r\n * ),\r\n * filterFn: textFilterFn,\r\n * ```\r\n */\r\nexport function TextFilter<TData>({\r\n column,\r\n defaultOperator,\r\n popoverAlign,\r\n}: TextFilterProps<TData>): JSX.Element {\r\n // 현재 TanStack 필터 값 읽기 (unknown → TextFilterValue | undefined 캐스트, C-4)\r\n const currentValue = column.getFilterValue() as TextFilterValue | undefined;\r\n\r\n // 연산자 로컬 상태\r\n const [operator, setOperator] = useState<TextFilterOperator>(\r\n currentValue?.operator ?? defaultOperator ?? 'contains',\r\n );\r\n\r\n // 입력값 로컬 상태 (디바운스 300ms, D6)\r\n const [inputValue, setInputValue] = useState<string>(currentValue?.value ?? '');\r\n\r\n // 디바운스 effect: inputValue 또는 operator 변경 시 300ms 후 setFilterValue (Section 4.5)\r\n useEffect(() => {\r\n const timer = setTimeout(() => {\r\n if (inputValue.trim() === '') {\r\n column.setFilterValue(undefined);\r\n } else {\r\n column.setFilterValue({ operator, value: inputValue } satisfies TextFilterValue);\r\n }\r\n }, 300);\r\n return () => clearTimeout(timer);\r\n }, [inputValue, operator, column]);\r\n\r\n // clear 버튼 핸들러 (AC-005)\r\n const handleClear = (): void => {\r\n setInputValue('');\r\n setOperator(defaultOperator ?? 'contains');\r\n column.setFilterValue(undefined);\r\n };\r\n\r\n // 연산자 변경 핸들러\r\n const handleOperatorChange = (e: React.ChangeEvent<HTMLSelectElement>): void => {\r\n setOperator(e.target.value as TextFilterOperator);\r\n };\r\n\r\n // 입력값 변경 핸들러\r\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>): void => {\r\n setInputValue(e.target.value);\r\n };\r\n\r\n // C-29 spread-skip: popoverAlign → FilterPopover align 전달\r\n const alignProp = popoverAlign !== undefined ? { align: popoverAlign } : {};\r\n\r\n const isFiltered = column.getIsFiltered();\r\n\r\n return (\r\n <span className=\"inline-flex items-center gap-0.5\">\r\n {/* FilterIndicator: 활성 필터 파란 dot (AC-004) */}\r\n <FilterIndicator isFiltered={isFiltered} />\r\n\r\n {/* FilterPopover: 아이콘 버튼 트리거 + 내용 (AC-003) */}\r\n <FilterPopover\r\n trigger={\r\n <button\r\n type=\"button\"\r\n aria-label=\"필터\"\r\n aria-pressed={isFiltered}\r\n className=\"inline-flex items-center justify-center w-5 h-5 rounded text-gray-400 hover:text-blue-500 hover:bg-gray-100 focus:outline-none focus:ring-1 focus:ring-blue-400 transition-colors\"\r\n >\r\n <FunnelIcon />\r\n </button>\r\n }\r\n {...alignProp}\r\n >\r\n {/* 팝오버 내용 */}\r\n <div className=\"p-3 space-y-2\">\r\n {/* 연산자 선택 */}\r\n <select\r\n aria-label=\"연산자\"\r\n value={operator}\r\n onChange={handleOperatorChange}\r\n className=\"w-full px-2 py-1 text-sm border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-400 bg-white\"\r\n >\r\n <option value=\"contains\">포함</option>\r\n <option value=\"equals\">같음</option>\r\n <option value=\"startsWith\">시작</option>\r\n <option value=\"endsWith\">끝</option>\r\n </select>\r\n\r\n {/* 값 입력 */}\r\n <input\r\n type=\"text\"\r\n aria-label=\"필터 값\"\r\n value={inputValue}\r\n onChange={handleInputChange}\r\n placeholder=\"값 입력...\"\r\n className=\"w-full px-2 py-1 text-sm border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-400\"\r\n />\r\n\r\n {/* 초기화 버튼 (AC-005) */}\r\n <button\r\n type=\"button\"\r\n onClick={handleClear}\r\n className=\"w-full px-2 py-1 text-xs text-gray-600 border border-gray-200 rounded hover:bg-gray-50 hover:text-gray-900 transition-colors\"\r\n >\r\n 초기화\r\n </button>\r\n </div>\r\n </FilterPopover>\r\n </span>\r\n );\r\n}\r\n","/**\r\n * @topgrid/grid-features — TextFilter 커스텀 filterFn.\r\n *\r\n * MOD-GRID-09 G-001 AC-002: TanStack FilterFn<unknown> 구현.\r\n *\r\n * 동작 정의 (Section 4.4, D5):\r\n * - 대소문자 무시 (case-insensitive toLowerCase 변환)\r\n * - 공백 trim (filterValue.value.trim())\r\n * - empty value → autoRemove 트리거 → 필터 자동 해제\r\n * - null/undefined cellValue → false (null-safe)\r\n *\r\n * @remarks\r\n * `verbatimModuleSyntax: true` — import type 필수.\r\n * `noUnusedParameters: true` — row.getValue(columnId) 로 columnId 사용.\r\n */\r\n\r\nimport { filterFns } from '@tanstack/react-table';\r\nimport type { FilterFn, Row } from '@tanstack/react-table';\r\nimport { isWithinInterval, startOfDay, endOfDay } from 'date-fns';\r\nimport type { TextFilterValue, NumberFilterValue, DateFilterValue } from './types';\r\n\r\n// ---------------------------------------------------------------------------\r\n// textFilterFn 구현 (AC-002, Section 4.3)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * TanStack 커스텀 filterFn — 4가지 텍스트 연산자 지원.\r\n *\r\n * columnDef에 `filterFn: textFilterFn` 으로 직접 참조 방식 등록.\r\n * (타입 안전 + tree-shaking 친화적 — Section 4.3)\r\n *\r\n * `autoRemove`: filterValue.value.trim() === '' 일 때 TanStack이 필터를 자동 제거.\r\n *\r\n * @example\r\n * ```typescript\r\n * columnHelper.accessor('name', {\r\n * filterFn: textFilterFn,\r\n * header: ({ column }) => <TextFilter column={column} />,\r\n * });\r\n * ```\r\n */\r\nexport const textFilterFn: FilterFn<unknown> = <TData>(\r\n row: Row<TData>,\r\n columnId: string,\r\n filterValue: TextFilterValue,\r\n): boolean => {\r\n const rawCell: unknown = row.getValue(columnId);\r\n\r\n // null/undefined cell → false (null-safe, Section 4.4 edge case)\r\n if (rawCell == null) return false;\r\n\r\n const cellStr = String(rawCell).toLowerCase();\r\n const filterStr = filterValue.value.trim().toLowerCase();\r\n\r\n // empty filterStr 는 autoRemove 가 처리하지만 방어적 check\r\n if (filterStr === '') return true;\r\n\r\n switch (filterValue.operator) {\r\n case 'contains':\r\n return cellStr.includes(filterStr);\r\n case 'equals':\r\n return cellStr === filterStr;\r\n case 'startsWith':\r\n return cellStr.startsWith(filterStr);\r\n case 'endsWith':\r\n return cellStr.endsWith(filterStr);\r\n default: {\r\n // exhaustive check — never 도달\r\n const _exhaustive: never = filterValue.operator;\r\n return _exhaustive;\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * TanStack autoRemove 등록.\r\n * filterValue.value.trim() === '' 이면 TanStack이 자동으로 해당 컬럼 필터를 제거.\r\n * (D5, Section 4.3)\r\n */\r\ntextFilterFn.autoRemove = (val: TextFilterValue | undefined): boolean =>\r\n !val || val.value.trim() === '';\r\n\r\n// ---------------------------------------------------------------------------\r\n// numberFilterFn 구현 (MOD-GRID-09 G-002 AC-002)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * TanStack 커스텀 filterFn — 7가지 숫자 연산자 지원.\r\n *\r\n * 연산자: = / != / > / < / >= / <= / between\r\n * - between: min <= cell <= max (양끝 inclusive, inNumberRange semantics — D2)\r\n * - null-safe: rawCell == null → false\r\n * - NaN-safe: Number(rawCell) isNaN → false\r\n *\r\n * `autoRemove`:\r\n * - 단항 연산자: value === undefined 또는 isNaN(value)\r\n * - between: (min === undefined || isNaN(min)) && (max === undefined || isNaN(max))\r\n *\r\n * columnDef에 `filterFn: numberFilterFn` 으로 직접 참조 방식 등록.\r\n *\r\n * @example\r\n * ```typescript\r\n * columnHelper.accessor('price', {\r\n * filterFn: numberFilterFn,\r\n * header: ({ column }) => <NumberFilter column={column} />,\r\n * });\r\n * ```\r\n */\r\nexport const numberFilterFn: FilterFn<unknown> = <TData>(\r\n row: Row<TData>,\r\n columnId: string,\r\n filterValue: NumberFilterValue,\r\n): boolean => {\r\n const rawCell: unknown = row.getValue(columnId);\r\n\r\n // null/undefined cell → false (null-safe, D11)\r\n if (rawCell == null) return false;\r\n\r\n const cell = Number(rawCell);\r\n\r\n // NaN cell → false (NaN-safe, D11)\r\n if (isNaN(cell)) return false;\r\n\r\n const { operator, value, min, max } = filterValue;\r\n\r\n switch (operator) {\r\n case '=':\r\n return value !== undefined && !isNaN(value) ? cell === value : true;\r\n case '!=':\r\n return value !== undefined && !isNaN(value) ? cell !== value : true;\r\n case '>':\r\n return value !== undefined && !isNaN(value) ? cell > value : true;\r\n case '<':\r\n return value !== undefined && !isNaN(value) ? cell < value : true;\r\n case '>=':\r\n return value !== undefined && !isNaN(value) ? cell >= value : true;\r\n case '<=':\r\n return value !== undefined && !isNaN(value) ? cell <= value : true;\r\n case 'between': {\r\n const minOk = min !== undefined && !isNaN(min) ? cell >= min : true;\r\n const maxOk = max !== undefined && !isNaN(max) ? cell <= max : true;\r\n return minOk && maxOk;\r\n }\r\n default: {\r\n // exhaustive check — never 도달\r\n const _exhaustive: never = operator;\r\n return _exhaustive;\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * TanStack autoRemove 등록 (D6, Section 4.3).\r\n * - 단항: value undefined 또는 NaN → 필터 자동 해제.\r\n * - between: min AND max 모두 undefined/NaN → 자동 해제 (단방향 bound은 유지).\r\n */\r\nnumberFilterFn.autoRemove = (val: NumberFilterValue | undefined): boolean => {\r\n if (!val) return true;\r\n if (val.operator === 'between') {\r\n return (\r\n (val.min === undefined || isNaN(val.min)) &&\r\n (val.max === undefined || isNaN(val.max))\r\n );\r\n }\r\n return val.value === undefined || isNaN(val.value);\r\n};\r\n\r\n// ---------------------------------------------------------------------------\r\n// dateRangeFilterFn 구현 (MOD-GRID-09 G-003 AC-002)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * TanStack 커스텀 filterFn — 날짜 범위(from/to) 필터.\r\n *\r\n * - cell value: Date instance | ISO string | number(epoch ms) — `new Date(cell)` 변환\r\n * - startOfDay(from) / endOfDay(to) 자정 정규화 (D6 로컬 타임존 기준)\r\n * - 단일 bound 지원: from-only → cell ≥ startOfDay(from), to-only → cell ≤ endOfDay(to)\r\n * - isWithinInterval RangeError (from > to) → try-catch → false (E1)\r\n * - null-safe / NaN-safe / empty-string-safe (T1~T4, E4)\r\n *\r\n * `autoRemove`: from과 to 모두 undefined 일 때 TanStack이 필터를 자동 제거.\r\n *\r\n * @example\r\n * ```typescript\r\n * columnHelper.accessor('orderDate', {\r\n * filterFn: dateRangeFilterFn,\r\n * header: ({ column }) => <DateFilter column={column} />,\r\n * });\r\n * ```\r\n */\r\nexport const dateRangeFilterFn: FilterFn<unknown> = <TData>(\r\n row: Row<TData>,\r\n columnId: string,\r\n filterValue: DateFilterValue,\r\n): boolean => {\r\n const rawCell: unknown = row.getValue(columnId);\r\n\r\n // null/undefined cell → false (null-safe, T1/T2)\r\n if (rawCell == null) return false;\r\n\r\n // cell 변환: Date | ISO string | number(epoch ms) → Date instance (D12)\r\n const cellDate = new Date(rawCell as string | number | Date);\r\n\r\n // Invalid Date → false (NaN-safe, T3/E4)\r\n if (isNaN(cellDate.getTime())) return false;\r\n\r\n const { from, to } = filterValue;\r\n\r\n // 양쪽 모두 undefined → autoRemove (T4) — 방어적 check (autoRemove가 처리하지만 안전)\r\n if (from === undefined && to === undefined) return true;\r\n\r\n // from-only: cell ≥ startOfDay(from) (T5/T6, D7)\r\n if (from !== undefined && to === undefined) {\r\n return cellDate >= startOfDay(from);\r\n }\r\n\r\n // to-only: cell ≤ endOfDay(to) (T7/T8, D7)\r\n if (from === undefined && to !== undefined) {\r\n return cellDate <= endOfDay(to);\r\n }\r\n\r\n // 양쪽 bound: isWithinInterval (T9~T12, E1)\r\n try {\r\n return isWithinInterval(cellDate, {\r\n start: startOfDay(from as Date),\r\n end: endOfDay(to as Date),\r\n });\r\n } catch {\r\n // RangeError: from > to (E1) → false\r\n return false;\r\n }\r\n};\r\n\r\n/**\r\n * TanStack autoRemove 등록 (D7, Section 4.2).\r\n * from과 to 모두 undefined 일 때 TanStack이 해당 컬럼 필터를 자동 제거.\r\n * 단일 bound (from-only 또는 to-only)는 필터 유지.\r\n */\r\ndateRangeFilterFn.autoRemove = (val: DateFilterValue | undefined): boolean =>\r\n !val || (val.from === undefined && val.to === undefined);\r\n\r\n// ---------------------------------------------------------------------------\r\n// selectFilterFn — TanStack built-in arrIncludes re-export (MOD-GRID-09 G-004 D2)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * TanStack 커스텀 filterFn — 다중선택 배열 포함 필터.\r\n *\r\n * `filterFns.arrIncludes` re-export: cell value가 filterValue 배열의\r\n * 임의 요소와 일치하면 true.\r\n *\r\n * `autoRemove`: filterValue?.length === 0 시 TanStack이 자동 필터 해제.\r\n *\r\n * columnDef에 `filterFn: selectFilterFn` 으로 직접 참조 방식 등록.\r\n *\r\n * 주의: consumer useReactTable options에\r\n * `getFacetedRowModel: getFacetedRowModel()` 과\r\n * `getFacetedUniqueValues: getFacetedUniqueValues()` 등록 필수 (D3).\r\n *\r\n * @example\r\n * ```typescript\r\n * columnHelper.accessor('category', {\r\n * filterFn: selectFilterFn,\r\n * header: ({ column }) => <SelectFilter column={column} />,\r\n * });\r\n * ```\r\n */\r\nexport const selectFilterFn = filterFns.arrIncludes;\r\n","/**\r\n * @topgrid/grid-features — NumberFilter 메인 컴포넌트.\r\n *\r\n * MOD-GRID-09 G-002:\r\n * 컬럼 헤더 Popover + 7가지 숫자 연산자(=, !=, >, <, >=, <=, between) + 필터 인디케이터.\r\n *\r\n * AC-001: NumberFilterValue 타입 + column.setFilterValue / getFilterValue 사용\r\n * AC-002: numberFilterFn 등록 패턴 문서화 (Section 12 예시)\r\n * AC-003: 단항 연산자 → 단일 value input; between → min/max 두 input (조건부 렌더)\r\n * AC-004: G-001 FilterPopover 재사용 (DRY, D7)\r\n * AC-005: G-001 FilterIndicator 재사용 (column.getIsFiltered() 기반, D7)\r\n * AC-006: tsc 0 error (C-29 spread-skip 패턴 적용)\r\n *\r\n * @remarks\r\n * `verbatimModuleSyntax: true` — import type 분리.\r\n * `exactOptionalPropertyTypes: true` — popoverAlign → FilterPopover align 전달 시\r\n * C-29 spread-skip 패턴 (Section 4.7).\r\n * `noUnusedLocals/noUnusedParameters: true` — 모든 변수 사용.\r\n * Tailwind className 전용 (C-5). style={{}} 없음.\r\n */\r\n\r\nimport { useState, useEffect } from 'react';\r\nimport type { NumberFilterProps, NumberFilterOperator, NumberFilterValue } from './types';\r\nimport { FilterPopover } from './FilterPopover';\r\nimport { FilterIndicator } from './FilterIndicator';\r\n\r\n// ---------------------------------------------------------------------------\r\n// FunnelIcon — 인라인 SVG (신규 dep 없음 — D2, TextFilter 동일 패턴)\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction FunnelIcon(): JSX.Element {\r\n return (\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 20 20\"\r\n fill=\"currentColor\"\r\n aria-hidden=\"true\"\r\n className=\"w-4 h-4\"\r\n >\r\n <path\r\n fillRule=\"evenodd\"\r\n d=\"M2.628 1.601C5.028 1.206 7.49 1 10 1s4.973.206 7.372.601a.75.75 0 0 1 .628.74v2.288a2.25 2.25 0 0 1-.659 1.59l-4.682 4.683a2.25 2.25 0 0 0-.659 1.59v3.037c0 .684-.31 1.33-.844 1.757l-1.937 1.55A.75.75 0 0 1 9 18.25v-5.757a2.25 2.25 0 0 0-.659-1.591L3.659 6.22A2.25 2.25 0 0 1 3 4.629V2.34a.75.75 0 0 1 .628-.74z\"\r\n clipRule=\"evenodd\"\r\n />\r\n </svg>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// NumberFilter 컴포넌트\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 숫자 필터 UI — 7가지 연산자 select + 조건부 input + clear 버튼.\r\n *\r\n * `FilterPopover` + `FilterIndicator`를 조합한 메인 컴포넌트 (G-001 재사용).\r\n * `column.setFilterValue`로 TanStack columnFilters에 연결.\r\n * 디바운스 300ms (D10, Section 4.6).\r\n * between 연산자: min/max 두 input 조건부 렌더 (AC-003, D9, Section 5.3).\r\n *\r\n * @template TData - TanStack Row data 타입.\r\n *\r\n * @example\r\n * ```tsx\r\n * // columnDef header에 렌더:\r\n * header: ({ column }) => (\r\n * <div className=\"flex items-center gap-1\">\r\n * <span>가격</span>\r\n * <NumberFilter column={column} defaultOperator=\"=\" />\r\n * </div>\r\n * ),\r\n * filterFn: numberFilterFn,\r\n * ```\r\n */\r\nexport function NumberFilter<TData>({\r\n column,\r\n defaultOperator,\r\n popoverAlign,\r\n}: NumberFilterProps<TData>): JSX.Element {\r\n // 현재 TanStack 필터 값 읽기 (unknown → NumberFilterValue | undefined 캐스트, C-4)\r\n const currentValue = column.getFilterValue() as NumberFilterValue | undefined;\r\n\r\n // 연산자 로컬 상태\r\n const [operator, setOperator] = useState<NumberFilterOperator>(\r\n currentValue?.operator ?? defaultOperator ?? '=',\r\n );\r\n\r\n // 단항 연산자 입력값 로컬 상태 (디바운스 300ms, D10)\r\n const [inputValue, setInputValue] = useState<string>(\r\n currentValue?.value !== undefined ? String(currentValue.value) : '',\r\n );\r\n\r\n // between min/max 로컬 상태 (D10, Section 4.6)\r\n const [minValue, setMinValue] = useState<string>(\r\n currentValue?.min !== undefined ? String(currentValue.min) : '',\r\n );\r\n const [maxValue, setMaxValue] = useState<string>(\r\n currentValue?.max !== undefined ? String(currentValue.max) : '',\r\n );\r\n\r\n // 단항 연산자 디바운스 effect (Section 4.6)\r\n useEffect(() => {\r\n const timer = setTimeout(() => {\r\n const num = parseFloat(inputValue);\r\n if (inputValue === '' || isNaN(num)) {\r\n column.setFilterValue(undefined);\r\n } else {\r\n column.setFilterValue({ operator, value: num } satisfies NumberFilterValue);\r\n }\r\n }, 300);\r\n return () => clearTimeout(timer);\r\n }, [inputValue, operator, column]);\r\n\r\n // between 디바운스 effect (Section 4.6)\r\n useEffect(() => {\r\n if (operator !== 'between') return;\r\n const timer = setTimeout(() => {\r\n const min = parseFloat(minValue);\r\n const max = parseFloat(maxValue);\r\n const hasMin = minValue !== '' && !isNaN(min);\r\n const hasMax = maxValue !== '' && !isNaN(max);\r\n if (!hasMin && !hasMax) {\r\n column.setFilterValue(undefined);\r\n } else {\r\n column.setFilterValue({\r\n operator: 'between',\r\n ...(hasMin ? { min } : {}),\r\n ...(hasMax ? { max } : {}),\r\n } satisfies NumberFilterValue);\r\n }\r\n }, 300);\r\n return () => clearTimeout(timer);\r\n }, [minValue, maxValue, operator, column]);\r\n\r\n // clear 버튼 핸들러\r\n const handleClear = (): void => {\r\n setInputValue('');\r\n setMinValue('');\r\n setMaxValue('');\r\n setOperator(defaultOperator ?? '=');\r\n column.setFilterValue(undefined);\r\n };\r\n\r\n // 연산자 변경 핸들러 — EC-03: operator 변경 시 기존 value 상태 초기화\r\n const handleOperatorChange = (e: React.ChangeEvent<HTMLSelectElement>): void => {\r\n setOperator(e.target.value as NumberFilterOperator);\r\n // EC-03: 연산자 전환 시 clean state 보장\r\n setInputValue('');\r\n setMinValue('');\r\n setMaxValue('');\r\n column.setFilterValue(undefined);\r\n };\r\n\r\n // 단항 값 변경 핸들러\r\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>): void => {\r\n setInputValue(e.target.value);\r\n };\r\n\r\n // between min 변경 핸들러\r\n const handleMinChange = (e: React.ChangeEvent<HTMLInputElement>): void => {\r\n setMinValue(e.target.value);\r\n };\r\n\r\n // between max 변경 핸들러\r\n const handleMaxChange = (e: React.ChangeEvent<HTMLInputElement>): void => {\r\n setMaxValue(e.target.value);\r\n };\r\n\r\n // C-29 spread-skip: popoverAlign → FilterPopover align 전달\r\n const alignProp = popoverAlign !== undefined ? { align: popoverAlign } : {};\r\n\r\n const isFiltered = column.getIsFiltered();\r\n\r\n return (\r\n <span className=\"inline-flex items-center gap-0.5\">\r\n {/* FilterIndicator: 활성 필터 파란 dot (AC-005, G-001 재사용) */}\r\n <FilterIndicator isFiltered={isFiltered} />\r\n\r\n {/* FilterPopover: 아이콘 버튼 트리거 + 내용 (AC-004, G-001 재사용) */}\r\n <FilterPopover\r\n trigger={\r\n <button\r\n type=\"button\"\r\n aria-label=\"숫자 필터\"\r\n aria-pressed={isFiltered}\r\n className=\"inline-flex items-center justify-center w-5 h-5 rounded text-gray-400 hover:text-blue-500 hover:bg-gray-100 focus:outline-none focus:ring-1 focus:ring-blue-400 transition-colors\"\r\n >\r\n <FunnelIcon />\r\n </button>\r\n }\r\n {...alignProp}\r\n >\r\n {/* 팝오버 내용 */}\r\n <div className=\"p-3 space-y-2\">\r\n {/* 연산자 선택 (Section 10 — 7 options) */}\r\n <select\r\n aria-label=\"연산자\"\r\n value={operator}\r\n onChange={handleOperatorChange}\r\n className=\"w-full px-2 py-1 text-sm border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-400 bg-white\"\r\n >\r\n <option value=\"=\">같음 (=)</option>\r\n <option value=\"!=\">다름 (≠)</option>\r\n <option value=\">\">초과 (>)</option>\r\n <option value=\"<\">미만 (<)</option>\r\n <option value=\">=\">이상 (≥)</option>\r\n <option value=\"<=\">이하 (≤)</option>\r\n <option value=\"between\">사이 (between)</option>\r\n </select>\r\n\r\n {/* 조건부 input 렌더 (AC-003, D9, Section 5.3) */}\r\n {operator === 'between' ? (\r\n <>\r\n {/* between: min input */}\r\n <input\r\n type=\"number\"\r\n aria-label=\"최솟값\"\r\n value={minValue}\r\n onChange={handleMinChange}\r\n placeholder=\"최솟값...\"\r\n className=\"w-full px-2 py-1 text-sm border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-400\"\r\n />\r\n {/* between: 구분자 */}\r\n <span className=\"block text-center text-xs text-gray-400\">~</span>\r\n {/* between: max input */}\r\n <input\r\n type=\"number\"\r\n aria-label=\"최댓값\"\r\n value={maxValue}\r\n onChange={handleMaxChange}\r\n placeholder=\"최댓값...\"\r\n className=\"w-full px-2 py-1 text-sm border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-400\"\r\n />\r\n </>\r\n ) : (\r\n /* 단항 연산자: 단일 value input */\r\n <input\r\n type=\"number\"\r\n aria-label=\"필터 값\"\r\n value={inputValue}\r\n onChange={handleInputChange}\r\n placeholder=\"값 입력...\"\r\n className=\"w-full px-2 py-1 text-sm border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-400\"\r\n />\r\n )}\r\n\r\n {/* 초기화 버튼 */}\r\n <button\r\n type=\"button\"\r\n onClick={handleClear}\r\n className=\"w-full px-2 py-1 text-xs text-gray-600 border border-gray-200 rounded hover:bg-gray-50 hover:text-gray-900 transition-colors\"\r\n >\r\n 초기화\r\n </button>\r\n </div>\r\n </FilterPopover>\r\n </span>\r\n );\r\n}\r\n","/**\r\n * @topgrid/grid-features — DateFilter 컴포넌트.\r\n *\r\n * MOD-GRID-09 G-003:\r\n * react-datepicker 기반 날짜 범위(from/to) 선택 필터.\r\n * FilterPopover + FilterIndicator 재사용 (G-001).\r\n *\r\n * 핵심 동작:\r\n * - from/to DatePicker (한국어 locale, locale=\"ko\")\r\n * - maxDate/minDate 역전 방지 (E1)\r\n * - 초기화 버튼: column.setFilterValue(undefined)\r\n * - C-29: popoverAlign → FilterPopover align spread-skip 패턴\r\n * - D4: CSS import 없음 — 소비자가 react-datepicker/dist/react-datepicker.css import\r\n *\r\n * @remarks\r\n * `verbatimModuleSyntax: true` — import type 필수.\r\n * `exactOptionalPropertyTypes: true` — optional prop 전달 시 C-29 spread-skip 패턴.\r\n * Tailwind className 전용 (C-5). 내부 CSS import 없음 (D4).\r\n */\r\n\r\nimport DatePicker, { registerLocale } from 'react-datepicker';\r\nimport { ko } from 'date-fns/locale';\r\nimport type { DateFilterProps, DateFilterValue } from './types';\r\nimport { FilterPopover } from './FilterPopover';\r\nimport { FilterIndicator } from './FilterIndicator';\r\n\r\n// 한국어 로케일 1회 등록 (D10, Section 4.5)\r\nregisterLocale('ko', ko);\r\n\r\n/**\r\n * 날짜 범위 필터 컴포넌트.\r\n *\r\n * FilterPopover + FilterIndicator를 재사용하여 from/to DatePicker를 렌더.\r\n * `column.setFilterValue` 로 TanStack Table 필터링을 트리거.\r\n *\r\n * @template TData - TanStack Row data 타입.\r\n *\r\n * @example\r\n * ```tsx\r\n * columnHelper.accessor('orderDate', {\r\n * filterFn: dateRangeFilterFn,\r\n * header: ({ column }) => (\r\n * <div>\r\n * 주문일\r\n * <DateFilter column={column} />\r\n * </div>\r\n * ),\r\n * });\r\n * ```\r\n */\r\nexport function DateFilter<TData>({ column, popoverAlign }: DateFilterProps<TData>): JSX.Element {\r\n const filterValue = column.getFilterValue() as DateFilterValue | undefined;\r\n const fromDate = filterValue?.from ?? null;\r\n const toDate = filterValue?.to ?? null;\r\n\r\n const handleFromChange = (date: Date | null): void => {\r\n const from = date ?? undefined;\r\n const to = filterValue?.to;\r\n if (from === undefined && to === undefined) {\r\n column.setFilterValue(undefined);\r\n } else {\r\n // C-29: exactOptionalPropertyTypes — spread-skip 패턴으로 optional props 전달\r\n const val: DateFilterValue = {\r\n ...(from !== undefined ? { from } : {}),\r\n ...(to !== undefined ? { to } : {}),\r\n };\r\n column.setFilterValue(val);\r\n }\r\n };\r\n\r\n const handleToChange = (date: Date | null): void => {\r\n const from = filterValue?.from;\r\n const to = date ?? undefined;\r\n if (from === undefined && to === undefined) {\r\n column.setFilterValue(undefined);\r\n } else {\r\n // C-29: exactOptionalPropertyTypes — spread-skip 패턴으로 optional props 전달\r\n const val: DateFilterValue = {\r\n ...(from !== undefined ? { from } : {}),\r\n ...(to !== undefined ? { to } : {}),\r\n };\r\n column.setFilterValue(val);\r\n }\r\n };\r\n\r\n const handleClear = (): void => {\r\n column.setFilterValue(undefined);\r\n };\r\n\r\n // 날짜 범위 요약 텍스트\r\n const summaryText = (): string => {\r\n if (fromDate && toDate) {\r\n const fmt = (d: Date): string =>\r\n `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;\r\n return `${fmt(fromDate)} ~ ${fmt(toDate)}`;\r\n }\r\n if (fromDate) {\r\n const d = fromDate;\r\n return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')} ~`;\r\n }\r\n if (toDate) {\r\n const d = toDate;\r\n return `~ ${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;\r\n }\r\n return '날짜 필터';\r\n };\r\n\r\n const isFiltered = column.getIsFiltered();\r\n\r\n const trigger = (\r\n <button\r\n type=\"button\"\r\n aria-label=\"날짜 필터\"\r\n aria-pressed={isFiltered}\r\n className=\"inline-flex items-center gap-1 px-2 py-1 text-xs rounded border border-gray-300 bg-white hover:bg-gray-50 focus:outline-none focus:ring-1 focus:ring-blue-500\"\r\n >\r\n <FilterIndicator isFiltered={isFiltered} />\r\n <span className=\"truncate max-w-[120px]\">{summaryText()}</span>\r\n </button>\r\n );\r\n\r\n // C-29: popoverAlign undefined일 때 prop 전달 안 함 (spread-skip)\r\n return (\r\n <FilterPopover\r\n trigger={trigger}\r\n {...(popoverAlign !== undefined ? { align: popoverAlign } : {})}\r\n >\r\n <div className=\"p-3 space-y-2 min-w-[220px]\">\r\n {/* 시작일 */}\r\n <div className=\"space-y-1\">\r\n <label className=\"block text-xs font-medium text-gray-700\">시작일</label>\r\n <DatePicker\r\n selected={fromDate}\r\n onChange={handleFromChange}\r\n locale=\"ko\"\r\n placeholderText=\"시작일\"\r\n dateFormat=\"yyyy-MM-dd\"\r\n // C-29: maxDate? → spread-skip (toDate is Date | null; pass Date only when non-null)\r\n {...(toDate !== null ? { maxDate: toDate } : {})}\r\n className=\"w-full px-2 py-1 text-sm border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-500\"\r\n />\r\n </div>\r\n {/* 종료일 */}\r\n <div className=\"space-y-1\">\r\n <label className=\"block text-xs font-medium text-gray-700\">종료일</label>\r\n <DatePicker\r\n selected={toDate}\r\n onChange={handleToChange}\r\n locale=\"ko\"\r\n placeholderText=\"종료일\"\r\n dateFormat=\"yyyy-MM-dd\"\r\n // C-29: minDate? → spread-skip (fromDate is Date | null; pass Date only when non-null)\r\n {...(fromDate !== null ? { minDate: fromDate } : {})}\r\n className=\"w-full px-2 py-1 text-sm border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-500\"\r\n />\r\n </div>\r\n {/* 초기화 */}\r\n <button\r\n type=\"button\"\r\n onClick={handleClear}\r\n className=\"w-full px-2 py-1 text-xs text-gray-600 border border-gray-200 rounded hover:bg-gray-50 focus:outline-none focus:ring-1 focus:ring-blue-500\"\r\n >\r\n 초기화\r\n </button>\r\n </div>\r\n </FilterPopover>\r\n );\r\n}\r\n","/**\r\n * @topgrid/grid-features — SelectFilter 컴포넌트.\r\n *\r\n * MOD-GRID-09 G-004 AC-001:\r\n * column.getFacetedUniqueValues() Map 읽어 체크박스 옵션 렌더.\r\n * 선택 시 column.setFilterValue(string[] | undefined) 호출.\r\n *\r\n * AC-003: 옵션 수 >= searchThreshold(기본 50) 시 내부 검색 input 자동 노출.\r\n * String.includes 대소문자 무시 — 신규 dep 없음 (D4).\r\n *\r\n * C-29: popoverAlign optional prop → FilterPopover align에 spread-skip 전달.\r\n * C-4: no any — Column<TData, unknown>.\r\n * C-5: Tailwind className만.\r\n * NFR-5: 체크박스 <input type=\"checkbox\"> 네이티브, label 연결 필수.\r\n *\r\n * @remarks\r\n * consumer useReactTable options에 반드시 등록 필요 (D3):\r\n * getFacetedRowModel: getFacetedRowModel(),\r\n * getFacetedUniqueValues: getFacetedUniqueValues(),\r\n */\r\n\r\nimport { useState, useId } from 'react';\r\nimport { FilterPopover } from './FilterPopover';\r\nimport { FilterIndicator } from './FilterIndicator';\r\nimport type { SelectFilterProps } from './types';\r\n\r\n/**\r\n * Excel-style 다중선택 체크박스 필터 컴포넌트.\r\n *\r\n * @template TData - TanStack Row data 타입.\r\n * @param props.column - TanStack Column 인스턴스.\r\n * @param props.searchThreshold - 내부 검색 노출 임계값 (기본 50).\r\n * @param props.popoverAlign - 팝오버 정렬 방향 (기본 'left').\r\n */\r\nexport function SelectFilter<TData>({\r\n column,\r\n searchThreshold = 50,\r\n popoverAlign,\r\n}: SelectFilterProps<TData>): JSX.Element {\r\n const [searchText, setSearchText] = useState('');\r\n const searchInputId = useId();\r\n\r\n // getFacetedUniqueValues()는 consumer가 getFacetedRowModel 등록해야 동작 (D3)\r\n const uniqueValues = column.getFacetedUniqueValues();\r\n const currentFilter = column.getFilterValue() as string[] | undefined;\r\n const isFiltered = column.getIsFiltered();\r\n\r\n // Map entries: [value, count][]\r\n const allOptions = Array.from(uniqueValues.entries()) as [unknown, number][];\r\n\r\n // 내부 검색 필터링 (대소문자 무시, AC-003, D4)\r\n const showSearch = uniqueValues.size >= searchThreshold;\r\n const filteredOptions = showSearch\r\n ? allOptions.filter(([val]) =>\r\n String(val === '' ? '' : val)\r\n .toLowerCase()\r\n .includes(searchText.toLowerCase()),\r\n )\r\n : allOptions;\r\n\r\n // 선택된 값 Set (렌더 O(1) 조회용)\r\n const selectedSet = new Set(currentFilter ?? []);\r\n\r\n // 전체 선택 여부 — 옵션이 있고 모두 선택됐을 때\r\n const allSelected =\r\n allOptions.length > 0 &&\r\n allOptions.every(([val]) => selectedSet.has(String(val)));\r\n\r\n /**\r\n * 체크박스 토글 핸들러.\r\n * 선택 해제 후 빈 배열이면 undefined (autoRemove) 전달.\r\n */\r\n const handleToggle = (optionValue: string): void => {\r\n const next = new Set(selectedSet);\r\n if (next.has(optionValue)) {\r\n next.delete(optionValue);\r\n } else {\r\n next.add(optionValue);\r\n }\r\n const arr = Array.from(next);\r\n column.setFilterValue(arr.length > 0 ? arr : undefined);\r\n };\r\n\r\n /** 전체 선택 / 해제 토글 */\r\n const handleToggleAll = (): void => {\r\n if (allSelected) {\r\n // 전체 해제 → undefined (autoRemove, Truth Table 5.1 row 4)\r\n column.setFilterValue(undefined);\r\n } else {\r\n column.setFilterValue(allOptions.map(([val]) => String(val)));\r\n }\r\n };\r\n\r\n // 트리거: 칼럼명 + 활성 인디케이터\r\n const trigger = (\r\n <button\r\n type=\"button\"\r\n className=\"inline-flex items-center gap-1 text-sm font-medium text-gray-700 hover:text-gray-900\"\r\n aria-label={`${column.id} 필터`}\r\n >\r\n <span>{column.id}</span>\r\n <FilterIndicator isFiltered={isFiltered} />\r\n <svg\r\n className=\"w-3 h-3 text-gray-400\"\r\n fill=\"none\"\r\n viewBox=\"0 0 20 20\"\r\n aria-hidden=\"true\"\r\n >\r\n <path\r\n stroke=\"currentColor\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n strokeWidth={2}\r\n d=\"M6 8l4 4 4-4\"\r\n />\r\n </svg>\r\n </button>\r\n );\r\n\r\n return (\r\n <FilterPopover\r\n {...(popoverAlign !== undefined ? { align: popoverAlign } : {})}\r\n trigger={trigger}\r\n >\r\n <div className=\"p-2 w-52\">\r\n {/* 내부 검색 input — searchThreshold 충족 시만 노출 (AC-003, D4) */}\r\n {showSearch && (\r\n <div className=\"mb-2\">\r\n <label htmlFor={searchInputId} className=\"sr-only\">\r\n 옵션 검색\r\n </label>\r\n <input\r\n id={searchInputId}\r\n type=\"text\"\r\n value={searchText}\r\n onChange={(e) => setSearchText(e.target.value)}\r\n placeholder=\"Search options…\"\r\n className=\"w-full rounded border border-gray-300 px-2 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-blue-500\"\r\n />\r\n </div>\r\n )}\r\n\r\n {/* 전체 선택/해제 (Truth Table 5.1 row 4) */}\r\n {allOptions.length > 0 && (\r\n <div className=\"mb-1 border-b border-gray-100 pb-1\">\r\n <label className=\"flex items-center gap-2 cursor-pointer px-1 py-0.5 rounded hover:bg-gray-50 text-xs text-gray-600\">\r\n <input\r\n type=\"checkbox\"\r\n checked={allSelected}\r\n onChange={handleToggleAll}\r\n className=\"rounded border-gray-300 text-blue-600 focus:ring-blue-500\"\r\n />\r\n 전체 선택/해제\r\n </label>\r\n </div>\r\n )}\r\n\r\n {/* 옵션 목록 */}\r\n {filteredOptions.length === 0 ? (\r\n <p className=\"px-1 py-1 text-xs text-gray-400\">No options</p>\r\n ) : (\r\n <ul className=\"max-h-48 overflow-y-auto space-y-0.5\">\r\n {filteredOptions.map(([val, count]) => {\r\n // E-3: 빈 문자열 → \"(blank)\" 레이블\r\n const rawStr = String(val);\r\n const displayLabel = rawStr === '' ? '(blank)' : rawStr;\r\n const optId = `${searchInputId}-opt-${rawStr}`;\r\n const isChecked = selectedSet.has(rawStr);\r\n\r\n return (\r\n <li key={rawStr}>\r\n <label\r\n htmlFor={optId}\r\n className=\"flex items-center gap-2 cursor-pointer px-1 py-0.5 rounded hover:bg-gray-50\"\r\n >\r\n <input\r\n id={optId}\r\n type=\"checkbox\"\r\n checked={isChecked}\r\n onChange={() => handleToggle(rawStr)}\r\n className=\"rounded border-gray-300 text-blue-600 focus:ring-blue-500\"\r\n />\r\n <span className=\"flex-1 truncate text-xs text-gray-700\">\r\n {displayLabel}\r\n </span>\r\n <span className=\"text-xs text-gray-400\">({count})</span>\r\n </label>\r\n </li>\r\n );\r\n })}\r\n </ul>\r\n )}\r\n </div>\r\n </FilterPopover>\r\n );\r\n}\r\n","/**\r\n * @topgrid/grid-features — GlobalSearchInput 컴포넌트.\r\n *\r\n * MOD-GRID-09 G-004 AC-004:\r\n * 입력값 변경 후 300ms debounce → table.setGlobalFilter(value) 호출.\r\n *\r\n * Truth Table 5.3:\r\n * - \"\" (빈 문자열) → setGlobalFilter(undefined) (autoRemove)\r\n * - \" \" (공백만) → trim 후 빈 문자열 → setGlobalFilter(undefined)\r\n * - \"abc\" → setGlobalFilter(\"abc\")\r\n *\r\n * D5: useEffect + setTimeout 기반 debounce — 신규 dep 없음.\r\n * C-4: no any — Table<TData>.\r\n * C-5: Tailwind className만.\r\n *\r\n * @remarks\r\n * consumer useReactTable options에 globalFilter state 등록 필요:\r\n * state: { ..., globalFilter },\r\n * onGlobalFilterChange: setGlobalFilter,\r\n */\r\n\r\nimport { useState, useEffect } from 'react';\r\nimport type { GlobalSearchInputProps } from './types';\r\n\r\n/**\r\n * 전체 행 검색 입력 컴포넌트 (debounce 300ms).\r\n *\r\n * @template TData - TanStack Row data 타입.\r\n * @param props.table - TanStack Table 인스턴스.\r\n * @param props.debounceMs - 디바운스 ms (기본 300).\r\n * @param props.placeholder - 입력 placeholder (기본 'Search all columns…').\r\n */\r\nexport function GlobalSearchInput<TData>({\r\n table,\r\n debounceMs = 300,\r\n placeholder = 'Search all columns…',\r\n}: GlobalSearchInputProps<TData>): JSX.Element {\r\n const [inputValue, setInputValue] = useState('');\r\n\r\n // debounce: 300ms 후 table.setGlobalFilter 호출 (AC-004, D5)\r\n useEffect(() => {\r\n const trimmed = inputValue.trim();\r\n const timer = setTimeout(\r\n () => table.setGlobalFilter(trimmed === '' ? undefined : trimmed),\r\n debounceMs,\r\n );\r\n return () => clearTimeout(timer);\r\n }, [inputValue, debounceMs, table]);\r\n\r\n return (\r\n <div className=\"relative flex items-center\">\r\n <svg\r\n className=\"absolute left-2 w-4 h-4 text-gray-400 pointer-events-none\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n aria-hidden=\"true\"\r\n >\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n strokeWidth={2}\r\n d=\"M21 21l-4.35-4.35M16.65 16.65A7.5 7.5 0 1 0 4.5 4.5a7.5 7.5 0 0 0 12.15 12.15z\"\r\n />\r\n </svg>\r\n <input\r\n type=\"text\"\r\n value={inputValue}\r\n onChange={(e) => setInputValue(e.target.value)}\r\n placeholder={placeholder}\r\n className=\"pl-8 pr-3 py-1.5 rounded border border-gray-300 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 w-56\"\r\n aria-label=\"전체 검색\"\r\n />\r\n </div>\r\n );\r\n}\r\n","/**\r\n * @topgrid/grid-features — FilterResetButton 컴포넌트.\r\n *\r\n * MOD-GRID-09 G-004 AC-005:\r\n * table.resetColumnFilters() + table.setGlobalFilter(undefined) 호출.\r\n * columnFilters.length === 0 && !globalFilter 시 disabled.\r\n *\r\n * D6: disabled 조건 — Truth Table 5.2:\r\n * - columnFilters=[], globalFilter=undefined → disabled=true\r\n * - 그 외 → disabled=false\r\n *\r\n * C-4: no any — Table<TData>.\r\n * C-5: Tailwind className만.\r\n *\r\n * @remarks\r\n * children prop은 버튼 레이블 오버라이드 용도.\r\n * 기본 레이블: 'Reset Filters'.\r\n */\r\n\r\nimport type { FilterResetButtonProps } from './types';\r\n\r\n/**\r\n * 필터 전체 초기화 버튼 컴포넌트.\r\n *\r\n * @template TData - TanStack Row data 타입.\r\n * @param props.table - TanStack Table 인스턴스.\r\n * @param props.children - 버튼 레이블 (기본 'Reset Filters').\r\n */\r\nexport function FilterResetButton<TData>({\r\n table,\r\n children,\r\n}: FilterResetButtonProps<TData>): JSX.Element {\r\n const { columnFilters, globalFilter } = table.getState();\r\n\r\n // disabled 조건 (AC-005, D6, Truth Table 5.2)\r\n const isDisabled = columnFilters.length === 0 && !globalFilter;\r\n\r\n const handleClick = (): void => {\r\n table.resetColumnFilters();\r\n table.setGlobalFilter(undefined);\r\n };\r\n\r\n return (\r\n <button\r\n type=\"button\"\r\n onClick={handleClick}\r\n disabled={isDisabled}\r\n className={[\r\n 'inline-flex items-center gap-1 rounded border px-3 py-1.5 text-sm font-medium transition-colors',\r\n isDisabled\r\n ? 'border-gray-200 bg-gray-50 text-gray-400 opacity-50 cursor-not-allowed'\r\n : 'border-gray-300 bg-white text-gray-700 hover:bg-gray-50 hover:text-gray-900',\r\n ].join(' ')}\r\n aria-label=\"필터 초기화\"\r\n >\r\n {children ?? 'Reset Filters'}\r\n </button>\r\n );\r\n}\r\n"]}
|