kokotable 0.95.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 +320 -0
- package/contract/table-request.schema.json +424 -0
- package/dist/core/src/adapters/sources.d.ts +59 -0
- package/dist/core/src/adapters/sources.js +235 -0
- package/dist/core/src/client/aggregate.d.ts +94 -0
- package/dist/core/src/client/aggregate.js +222 -0
- package/dist/core/src/client/groupRows.d.ts +40 -0
- package/dist/core/src/client/groupRows.js +145 -0
- package/dist/core/src/client/runClientQuery.d.ts +67 -0
- package/dist/core/src/client/runClientQuery.js +157 -0
- package/dist/core/src/columns/ColumnBuilder.d.ts +121 -0
- package/dist/core/src/columns/ColumnBuilder.js +273 -0
- package/dist/core/src/columns/cascade.d.ts +24 -0
- package/dist/core/src/columns/cascade.js +93 -0
- package/dist/core/src/columns/deriveOperators.d.ts +38 -0
- package/dist/core/src/columns/deriveOperators.js +94 -0
- package/dist/core/src/columns/expandSorting.d.ts +19 -0
- package/dist/core/src/columns/expandSorting.js +52 -0
- package/dist/core/src/columns/helper.d.ts +196 -0
- package/dist/core/src/columns/helper.js +250 -0
- package/dist/core/src/columns/labels.d.ts +12 -0
- package/dist/core/src/columns/labels.js +21 -0
- package/dist/core/src/contract.d.ts +12 -0
- package/dist/core/src/contract.js +12 -0
- package/dist/core/src/contracts/aiColumnMeta.d.ts +102 -0
- package/dist/core/src/contracts/aiColumnMeta.js +197 -0
- package/dist/core/src/contracts/describeColumns.d.ts +48 -0
- package/dist/core/src/contracts/describeColumns.js +40 -0
- package/dist/core/src/contracts/exportData.d.ts +150 -0
- package/dist/core/src/contracts/exportData.js +228 -0
- package/dist/core/src/contracts/index.d.ts +6 -0
- package/dist/core/src/contracts/index.js +12 -0
- package/dist/core/src/contracts/legacyUrl.d.ts +47 -0
- package/dist/core/src/contracts/legacyUrl.js +156 -0
- package/dist/core/src/contracts/preset.d.ts +104 -0
- package/dist/core/src/contracts/preset.js +141 -0
- package/dist/core/src/contracts/presetStore.d.ts +81 -0
- package/dist/core/src/contracts/presetStore.js +146 -0
- package/dist/core/src/contracts/usePresets.d.ts +51 -0
- package/dist/core/src/contracts/usePresets.js +170 -0
- package/dist/core/src/defaults.d.ts +7 -0
- package/dist/core/src/defaults.js +9 -0
- package/dist/core/src/filters/codec.d.ts +20 -0
- package/dist/core/src/filters/codec.js +61 -0
- package/dist/core/src/filters/date.d.ts +30 -0
- package/dist/core/src/filters/date.js +179 -0
- package/dist/core/src/filters/describe.d.ts +45 -0
- package/dist/core/src/filters/describe.js +189 -0
- package/dist/core/src/filters/kind.d.ts +52 -0
- package/dist/core/src/filters/kind.js +110 -0
- package/dist/core/src/filters/kinds/_list.d.ts +13 -0
- package/dist/core/src/filters/kinds/_list.js +42 -0
- package/dist/core/src/filters/kinds/boolean.d.ts +3 -0
- package/dist/core/src/filters/kinds/boolean.js +21 -0
- package/dist/core/src/filters/kinds/cascade.d.ts +6 -0
- package/dist/core/src/filters/kinds/cascade.js +16 -0
- package/dist/core/src/filters/kinds/commonCode.d.ts +3 -0
- package/dist/core/src/filters/kinds/commonCode.js +13 -0
- package/dist/core/src/filters/kinds/date.d.ts +9 -0
- package/dist/core/src/filters/kinds/date.js +32 -0
- package/dist/core/src/filters/kinds/enum.d.ts +4 -0
- package/dist/core/src/filters/kinds/enum.js +13 -0
- package/dist/core/src/filters/kinds/index.d.ts +49 -0
- package/dist/core/src/filters/kinds/index.js +39 -0
- package/dist/core/src/filters/kinds/linear.d.ts +5 -0
- package/dist/core/src/filters/kinds/linear.js +16 -0
- package/dist/core/src/filters/kinds/number.d.ts +5 -0
- package/dist/core/src/filters/kinds/number.js +19 -0
- package/dist/core/src/filters/kinds/period.d.ts +4 -0
- package/dist/core/src/filters/kinds/period.js +17 -0
- package/dist/core/src/filters/kinds/relation.d.ts +5 -0
- package/dist/core/src/filters/kinds/relation.js +17 -0
- package/dist/core/src/filters/kinds/schedule.d.ts +5 -0
- package/dist/core/src/filters/kinds/schedule.js +16 -0
- package/dist/core/src/filters/kinds/text.d.ts +4 -0
- package/dist/core/src/filters/kinds/text.js +18 -0
- package/dist/core/src/filters/linear.d.ts +33 -0
- package/dist/core/src/filters/linear.js +65 -0
- package/dist/core/src/filters/list.d.ts +32 -0
- package/dist/core/src/filters/list.js +87 -0
- package/dist/core/src/filters/number.d.ts +17 -0
- package/dist/core/src/filters/number.js +82 -0
- package/dist/core/src/filters/registry.d.ts +23 -0
- package/dist/core/src/filters/registry.js +121 -0
- package/dist/core/src/filters/relation.d.ts +26 -0
- package/dist/core/src/filters/relation.js +75 -0
- package/dist/core/src/filters/schedule.d.ts +21 -0
- package/dist/core/src/filters/schedule.js +60 -0
- package/dist/core/src/filters/text.d.ts +44 -0
- package/dist/core/src/filters/text.js +127 -0
- package/dist/core/src/hooks/index.d.ts +6 -0
- package/dist/core/src/hooks/index.js +6 -0
- package/dist/core/src/hooks/useDataTable.d.ts +13 -0
- package/dist/core/src/hooks/useDataTable.js +109 -0
- package/dist/core/src/hooks/useGrouping.d.ts +33 -0
- package/dist/core/src/hooks/useGrouping.js +57 -0
- package/dist/core/src/hooks/useSearchHistory.d.ts +20 -0
- package/dist/core/src/hooks/useSearchHistory.js +52 -0
- package/dist/core/src/hooks/useTableData.d.ts +25 -0
- package/dist/core/src/hooks/useTableData.js +49 -0
- package/dist/core/src/hooks/useTableFromDefinition.d.ts +47 -0
- package/dist/core/src/hooks/useTableFromDefinition.js +174 -0
- package/dist/core/src/hooks/useTableSearch.d.ts +10 -0
- package/dist/core/src/hooks/useTableSearch.js +434 -0
- package/dist/core/src/hooks/useUrlSync.d.ts +64 -0
- package/dist/core/src/hooks/useUrlSync.js +145 -0
- package/dist/core/src/index.d.ts +38 -0
- package/dist/core/src/index.js +43 -0
- package/dist/core/src/pivot/runClientPivot.d.ts +101 -0
- package/dist/core/src/pivot/runClientPivot.js +230 -0
- package/dist/core/src/pivot/transforms.d.ts +19 -0
- package/dist/core/src/pivot/transforms.js +105 -0
- package/dist/core/src/registry.d.ts +46 -0
- package/dist/core/src/registry.js +67 -0
- package/dist/core/src/types/column.d.ts +213 -0
- package/dist/core/src/types/column.js +1 -0
- package/dist/core/src/types/data.d.ts +63 -0
- package/dist/core/src/types/data.js +1 -0
- package/dist/core/src/types/definition.d.ts +239 -0
- package/dist/core/src/types/definition.js +59 -0
- package/dist/core/src/types/filter.d.ts +94 -0
- package/dist/core/src/types/filter.js +1 -0
- package/dist/core/src/types/filterKind.d.ts +204 -0
- package/dist/core/src/types/filterKind.js +1 -0
- package/dist/core/src/types/grouping.d.ts +80 -0
- package/dist/core/src/types/grouping.js +4 -0
- package/dist/core/src/types/index.d.ts +8 -0
- package/dist/core/src/types/index.js +8 -0
- package/dist/core/src/types/operators.d.ts +64 -0
- package/dist/core/src/types/operators.js +108 -0
- package/dist/core/src/types/request.d.ts +114 -0
- package/dist/core/src/types/request.js +1 -0
- package/dist/core/src/types/search.d.ts +112 -0
- package/dist/core/src/types/search.js +1 -0
- package/dist/core/src/types/sources.d.ts +84 -0
- package/dist/core/src/types/sources.js +1 -0
- package/dist/core/src/types/table.d.ts +40 -0
- package/dist/core/src/types/table.js +1 -0
- package/dist/core/src/utils/compare.d.ts +13 -0
- package/dist/core/src/utils/compare.js +53 -0
- package/dist/core/src/utils/dateText.d.ts +12 -0
- package/dist/core/src/utils/dateText.js +30 -0
- package/dist/core/src/utils/getValue.d.ts +9 -0
- package/dist/core/src/utils/getValue.js +32 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/mui/src/TableLayout.d.ts +75 -0
- package/dist/mui/src/TableLayout.js +162 -0
- package/dist/mui/src/TableScreen.d.ts +70 -0
- package/dist/mui/src/TableScreen.js +174 -0
- package/dist/mui/src/addons/FilterModal.d.ts +45 -0
- package/dist/mui/src/addons/FilterModal.js +39 -0
- package/dist/mui/src/addons/FilterStatusModal.d.ts +40 -0
- package/dist/mui/src/addons/FilterStatusModal.js +68 -0
- package/dist/mui/src/addons/PresetBar.d.ts +56 -0
- package/dist/mui/src/addons/PresetBar.js +145 -0
- package/dist/mui/src/addons/ShareDropdown.d.ts +35 -0
- package/dist/mui/src/addons/ShareDropdown.js +41 -0
- package/dist/mui/src/addons/searchStore.d.ts +44 -0
- package/dist/mui/src/addons/searchStore.js +112 -0
- package/dist/mui/src/addons/searchSummary.d.ts +106 -0
- package/dist/mui/src/addons/searchSummary.js +109 -0
- package/dist/mui/src/columns/displayColumns.d.ts +100 -0
- package/dist/mui/src/columns/displayColumns.js +174 -0
- package/dist/mui/src/content/CardContent.d.ts +24 -0
- package/dist/mui/src/content/CardContent.js +25 -0
- package/dist/mui/src/content/ColumnMenu.d.ts +43 -0
- package/dist/mui/src/content/ColumnMenu.js +40 -0
- package/dist/mui/src/content/ColumnVisibilityMenu.d.ts +14 -0
- package/dist/mui/src/content/ColumnVisibilityMenu.js +37 -0
- package/dist/mui/src/content/EditCell.d.ts +19 -0
- package/dist/mui/src/content/EditCell.js +61 -0
- package/dist/mui/src/content/HeaderCell.d.ts +59 -0
- package/dist/mui/src/content/HeaderCell.js +88 -0
- package/dist/mui/src/content/SelectionActionBar.d.ts +17 -0
- package/dist/mui/src/content/SelectionActionBar.js +19 -0
- package/dist/mui/src/content/SortControl.d.ts +17 -0
- package/dist/mui/src/content/SortControl.js +45 -0
- package/dist/mui/src/content/TableContent.d.ts +191 -0
- package/dist/mui/src/content/TableContent.js +525 -0
- package/dist/mui/src/content/TableErrorFallback.d.ts +24 -0
- package/dist/mui/src/content/TableErrorFallback.js +29 -0
- package/dist/mui/src/content/cellError.d.ts +13 -0
- package/dist/mui/src/content/cellError.js +20 -0
- package/dist/mui/src/content/cellRenderers.d.ts +71 -0
- package/dist/mui/src/content/cellRenderers.js +127 -0
- package/dist/mui/src/content/cellValue.d.ts +27 -0
- package/dist/mui/src/content/cellValue.js +82 -0
- package/dist/mui/src/content/columnOrder.d.ts +7 -0
- package/dist/mui/src/content/columnOrder.js +48 -0
- package/dist/mui/src/content/density.d.ts +13 -0
- package/dist/mui/src/content/density.js +12 -0
- package/dist/mui/src/content/menuItems.d.ts +118 -0
- package/dist/mui/src/content/menuItems.js +128 -0
- package/dist/mui/src/content/pinning.d.ts +17 -0
- package/dist/mui/src/content/pinning.js +65 -0
- package/dist/mui/src/exporters/index.d.ts +1 -0
- package/dist/mui/src/exporters/index.js +1 -0
- package/dist/mui/src/exporters/useExportFormatters.d.ts +13 -0
- package/dist/mui/src/exporters/useExportFormatters.js +17 -0
- package/dist/mui/src/filter/FilterArea.d.ts +40 -0
- package/dist/mui/src/filter/FilterArea.js +37 -0
- package/dist/mui/src/filter/FilterInput.d.ts +14 -0
- package/dist/mui/src/filter/FilterInput.js +18 -0
- package/dist/mui/src/filter/inputs/DateFilterInput.d.ts +14 -0
- package/dist/mui/src/filter/inputs/DateFilterInput.js +112 -0
- package/dist/mui/src/filter/inputs/LinearFilterInput.d.ts +10 -0
- package/dist/mui/src/filter/inputs/LinearFilterInput.js +35 -0
- package/dist/mui/src/filter/inputs/NumberFilterInput.d.ts +12 -0
- package/dist/mui/src/filter/inputs/NumberFilterInput.js +111 -0
- package/dist/mui/src/filter/inputs/OperatorMenu.d.ts +14 -0
- package/dist/mui/src/filter/inputs/OperatorMenu.js +25 -0
- package/dist/mui/src/filter/inputs/RelationFilterInput.d.ts +11 -0
- package/dist/mui/src/filter/inputs/RelationFilterInput.js +67 -0
- package/dist/mui/src/filter/inputs/ScheduleFilterInput.d.ts +8 -0
- package/dist/mui/src/filter/inputs/ScheduleFilterInput.js +17 -0
- package/dist/mui/src/filter/inputs/SelectFilterInput.d.ts +16 -0
- package/dist/mui/src/filter/inputs/SelectFilterInput.js +57 -0
- package/dist/mui/src/filter/inputs/TextFilterInput.d.ts +13 -0
- package/dist/mui/src/filter/inputs/TextFilterInput.js +55 -0
- package/dist/mui/src/filter/kinds.d.ts +58 -0
- package/dist/mui/src/filter/kinds.js +103 -0
- package/dist/mui/src/grouping/AggregateChip.d.ts +55 -0
- package/dist/mui/src/grouping/AggregateChip.js +128 -0
- package/dist/mui/src/grouping/AggregationChips.d.ts +49 -0
- package/dist/mui/src/grouping/AggregationChips.js +80 -0
- package/dist/mui/src/grouping/AggregationSettingsButton.d.ts +14 -0
- package/dist/mui/src/grouping/AggregationSettingsButton.js +19 -0
- package/dist/mui/src/grouping/GroupingBar.d.ts +59 -0
- package/dist/mui/src/grouping/GroupingBar.js +267 -0
- package/dist/mui/src/grouping/StepExpandButton.d.ts +12 -0
- package/dist/mui/src/grouping/StepExpandButton.js +18 -0
- package/dist/mui/src/grouping/groupRow.d.ts +68 -0
- package/dist/mui/src/grouping/groupRow.js +129 -0
- package/dist/mui/src/headers/defaultHeader.d.ts +28 -0
- package/dist/mui/src/headers/defaultHeader.js +90 -0
- package/dist/mui/src/headers/defineHeader.d.ts +21 -0
- package/dist/mui/src/headers/defineHeader.js +68 -0
- package/dist/mui/src/headers/index.d.ts +5 -0
- package/dist/mui/src/headers/index.js +3 -0
- package/dist/mui/src/headers/meta.d.ts +18 -0
- package/dist/mui/src/headers/meta.js +11 -0
- package/dist/mui/src/headers/types.d.ts +84 -0
- package/dist/mui/src/headers/types.js +1 -0
- package/dist/mui/src/index.d.ts +110 -0
- package/dist/mui/src/index.js +79 -0
- package/dist/mui/src/layout/ResizeHandle.d.ts +30 -0
- package/dist/mui/src/layout/ResizeHandle.js +66 -0
- package/dist/mui/src/menus/columnMenu.d.ts +45 -0
- package/dist/mui/src/menus/columnMenu.js +144 -0
- package/dist/mui/src/menus/copy.d.ts +5 -0
- package/dist/mui/src/menus/copy.js +25 -0
- package/dist/mui/src/menus/index.d.ts +6 -0
- package/dist/mui/src/menus/index.js +4 -0
- package/dist/mui/src/menus/resolve.d.ts +15 -0
- package/dist/mui/src/menus/resolve.js +60 -0
- package/dist/mui/src/menus/rowMenu.d.ts +46 -0
- package/dist/mui/src/menus/rowMenu.js +76 -0
- package/dist/mui/src/pagination/PaginationArea.d.ts +16 -0
- package/dist/mui/src/pagination/PaginationArea.js +23 -0
- package/dist/mui/src/pivot/PivotTable.d.ts +18 -0
- package/dist/mui/src/pivot/PivotTable.js +100 -0
- package/dist/mui/src/provider/KokoTableProvider.d.ts +184 -0
- package/dist/mui/src/provider/KokoTableProvider.js +118 -0
- package/dist/mui/src/provider/labels.d.ts +85 -0
- package/dist/mui/src/provider/labels.js +104 -0
- package/dist/mui/src/registry.d.ts +30 -0
- package/dist/mui/src/registry.js +5 -0
- package/dist/mui/src/toolbar/GlobalFilterField.d.ts +36 -0
- package/dist/mui/src/toolbar/GlobalFilterField.js +78 -0
- package/dist/mui/src/virtual/VirtualTableContent.d.ts +41 -0
- package/dist/mui/src/virtual/VirtualTableContent.js +58 -0
- package/dist/mui/src/virtual/VirtualTableScreen.d.ts +13 -0
- package/dist/mui/src/virtual/VirtualTableScreen.js +23 -0
- package/dist/mui/src/virtual.d.ts +4 -0
- package/dist/mui/src/virtual.js +4 -0
- package/dist/mui/src/virtualColumns/VirtualColumnCard.d.ts +26 -0
- package/dist/mui/src/virtualColumns/VirtualColumnCard.js +29 -0
- package/dist/mui/src/virtualColumns/VirtualColumnDialog.d.ts +27 -0
- package/dist/mui/src/virtualColumns/VirtualColumnDialog.js +154 -0
- package/dist/mui/src/virtualColumns/VirtualColumnSection.d.ts +31 -0
- package/dist/mui/src/virtualColumns/VirtualColumnSection.js +55 -0
- package/dist/mui/src/virtualColumns/expression.d.ts +68 -0
- package/dist/mui/src/virtualColumns/expression.js +489 -0
- package/dist/mui/src/virtualColumns/useVirtualColumns.d.ts +67 -0
- package/dist/mui/src/virtualColumns/useVirtualColumns.js +111 -0
- package/dist/mui/src/virtualColumns/virtualColumnDefs.d.ts +29 -0
- package/dist/mui/src/virtualColumns/virtualColumnDefs.js +51 -0
- package/dist/mui-virtual.d.ts +1 -0
- package/dist/mui-virtual.js +2 -0
- package/dist/mui.d.ts +1 -0
- package/dist/mui.js +2 -0
- package/dist/query/src/index.d.ts +6 -0
- package/dist/query/src/index.js +7 -0
- package/dist/query/src/serverPivot.d.ts +42 -0
- package/dist/query/src/serverPivot.js +153 -0
- package/dist/query/src/useServerGrouping.d.ts +52 -0
- package/dist/query/src/useServerGrouping.js +186 -0
- package/dist/query/src/useServerTable.d.ts +20 -0
- package/dist/query/src/useServerTable.js +106 -0
- package/dist/query/src/useServerTableData.d.ts +48 -0
- package/dist/query/src/useServerTableData.js +64 -0
- package/dist/query/src/wire.d.ts +16 -0
- package/dist/query/src/wire.js +32 -0
- package/dist/query.d.ts +1 -0
- package/dist/query.js +2 -0
- package/package.json +125 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { extractColumnMeta, toAiColumnType, toLegacyColumnDefs, toLegacyColumnType, toLegacyFilterVariant, type AiColumnMeta, type AiInnerColumnMeta, type AiColumnType, type ExtractColumnMetaOptions, type LegacyColumnType, type LegacyFilterVariant, type LegacyCascadeFilterLevel, type LegacyCompatColumnDef, type LegacyCompatColumnMeta, } from "./aiColumnMeta";
|
|
2
|
+
export { extractPresetState, applyPresetState, reconcileColumnOrder, presetToSearchState, presetToSearchInitial, type PresetState, type PresetDensity, type PresetSearchPart, type ExtractPresetExtras, type ApplyPresetHandlers, } from "./preset";
|
|
3
|
+
export { extractTableData, extractExportData, extractRowValues, formatValueForExport, isDisplayColumn, toCsv, useTableExport, DISPLAY_COLUMN_PREFIXES, exportFormatters, defineExportFormatter, resolveExportFormatter, formatExportValue, formatRowForExport, exportColumnsOf, BUILTIN_EXPORT_FORMATTERS, type ExtractedTableData, type ExtractTableDataOptions, type ExtractExportDataSource, type CsvOptions, type ExportAdapter, type TableExport, type UseTableExportOptions, type ExportColumn, type ExportFormatterDef, type ExportFormatters, } from "./exportData";
|
|
4
|
+
export { decodeLegacyTableParams, normalizeLegacySearchState, isoToLocalDate, DEFAULT_LEGACY_PARAM_KEYS, type LegacyTableParams, } from "./legacyUrl";
|
|
5
|
+
export { type ReadUrlSearchStateOptions } from "../hooks/useUrlSync";
|
|
6
|
+
export { describeColumns, type ColumnDescription } from "./describeColumns";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// 소비 앱(ERP) 연동 계약 — AI 검색 컬럼 메타, 프리셋, 내보내기, 구 URL 포맷
|
|
2
|
+
//
|
|
3
|
+
// 각 파일 상단 주석에 "어떤 ERP 파일이 왜 소비하는가" 를 적어 두었다.
|
|
4
|
+
// `readUrlSearchState` / `useUrlSync` 자체는 `hooks/index.ts` 가 노출한다 —
|
|
5
|
+
// 여기서는 이번에 추가된 심볼만 덧붙인다.
|
|
6
|
+
export { extractColumnMeta, toAiColumnType, toLegacyColumnDefs, toLegacyColumnType, toLegacyFilterVariant, } from "./aiColumnMeta";
|
|
7
|
+
export { extractPresetState, applyPresetState, reconcileColumnOrder, presetToSearchState, presetToSearchInitial, } from "./preset";
|
|
8
|
+
export { extractTableData, extractExportData, extractRowValues, formatValueForExport, isDisplayColumn, toCsv, useTableExport, DISPLAY_COLUMN_PREFIXES,
|
|
9
|
+
// 포맷터 레지스트리
|
|
10
|
+
exportFormatters, defineExportFormatter, resolveExportFormatter, formatExportValue, formatRowForExport, exportColumnsOf, BUILTIN_EXPORT_FORMATTERS, } from "./exportData";
|
|
11
|
+
export { decodeLegacyTableParams, normalizeLegacySearchState, isoToLocalDate, DEFAULT_LEGACY_PARAM_KEYS, } from "./legacyUrl";
|
|
12
|
+
export { describeColumns } from "./describeColumns";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 구(舊) URL 포맷 디코더.
|
|
3
|
+
*
|
|
4
|
+
* 왜 필요한가: ERP 는 두 가지 URL 인코딩을 쓰고 있었다 (server.md D-2, gap.md 1절 42).
|
|
5
|
+
* - `core/useUrlTableParams.ts` — `tp=` + `btoa(JSON)` URL-safe (ASCII 한정, 한글이면 실패)
|
|
6
|
+
* - `utils/tableUrlParams.ts` — `filter=` + `btoa(encodeURIComponent(JSON))`
|
|
7
|
+
* (`buildTransactionFilterUrl` 등 22 호출부가 공유 링크로 뿌려 놓았다)
|
|
8
|
+
* koko `readUrlSearchState` 는 UTF-8 base64 라 ASCII `tp` 는 이미 읽히지만
|
|
9
|
+
* `filter=` 스킴은 읽지 못하고, 값 모양(SearchConfig — 날짜가 ISO 시각 문자열)도
|
|
10
|
+
* koko `SearchState`(날짜는 `YYYY-MM-DD`) 와 다르다.
|
|
11
|
+
*
|
|
12
|
+
* - `decodeLegacyTableParams(text)` — UTF-8 base64 → 순수 atob JSON → atob+decodeURIComponent
|
|
13
|
+
* JSON 순으로 시도한다.
|
|
14
|
+
* - `normalizeLegacySearchState(state, columns?)` — 기존 `SearchConfig` 모양을 koko
|
|
15
|
+
* `SearchState` 로 정규화한다. 날짜 필터의 ISO 시각 문자열을 로컬 `YYYY-MM-DD` 로 바꾼다.
|
|
16
|
+
* `readUrlSearchState`(hooks/useUrlSync.ts) 가 실패 시 이 디코더로 폴백한다.
|
|
17
|
+
*/
|
|
18
|
+
import type { RowData } from "@tanstack/react-table";
|
|
19
|
+
import type { TableColumnDefs } from "../types/column";
|
|
20
|
+
import type { SearchState } from "../types/search";
|
|
21
|
+
import { isoToLocalDate } from "../utils/dateText";
|
|
22
|
+
export { isoToLocalDate };
|
|
23
|
+
/** 구 스킴이 쓰던 파라미터 키 — `readUrlSearchState` 가 `paramKey` 다음으로 훑는다 */
|
|
24
|
+
export declare const DEFAULT_LEGACY_PARAM_KEYS: readonly string[];
|
|
25
|
+
/** 기존 `GenericFilterData`/`SearchConfig` 를 느슨하게 받는 모양 */
|
|
26
|
+
export type LegacyTableParams = Partial<Omit<SearchState, "columnFilterFns">> & {
|
|
27
|
+
columnFilterFns?: Record<string, string>;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* URL 파라미터 값 하나를 세 인코딩으로 순서대로 디코드한다.
|
|
32
|
+
* 어느 것도 JSON 객체를 내지 못하면 undefined.
|
|
33
|
+
*/
|
|
34
|
+
export declare function decodeLegacyTableParams(text: string): LegacyTableParams | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* 기존 `SearchConfig` 모양의 객체 → koko `Partial<SearchState>`.
|
|
37
|
+
*
|
|
38
|
+
* - 컬럼에 필터 kind 가 있고 `codec` 을 선언했으면 그 `decode` 를 거친다 (내장 date kind 가
|
|
39
|
+
* ISO 시각 → `YYYY-MM-DD` 를 맡는다). decode 가 undefined 면 항목을 뺀다.
|
|
40
|
+
* - 그 외 날짜 필터 값이 ISO 시각 문자열이면 로컬 `YYYY-MM-DD` 로 바꾼다.
|
|
41
|
+
* `columns` 가 주어지면 date/datetime 컬럼에만, 없으면 ISO 패턴이면서 연산자가
|
|
42
|
+
* `dateBetween`/`betweenInclusive` 인 항목에만 적용한다.
|
|
43
|
+
* - `columnFilterFns` 값이 문자열이면 그대로 둔다.
|
|
44
|
+
* - 알려진 필드(columnFilters/columnFilterFns/globalFilter/pagination/sorting/grouping/
|
|
45
|
+
* virtualColumns) 만 남긴다.
|
|
46
|
+
*/
|
|
47
|
+
export declare function normalizeLegacySearchState<T extends RowData = RowData>(state: LegacyTableParams | Record<string, unknown>, columns?: TableColumnDefs<T>): Partial<SearchState>;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { columnKey, resolveVariant } from "../columns/deriveOperators";
|
|
2
|
+
import { isoToLocalDate } from "../utils/dateText";
|
|
3
|
+
import { decodeFilterValues, filterKindMap } from "../filters/codec";
|
|
4
|
+
export { isoToLocalDate };
|
|
5
|
+
/** 구 스킴이 쓰던 파라미터 키 — `readUrlSearchState` 가 `paramKey` 다음으로 훑는다 */
|
|
6
|
+
export const DEFAULT_LEGACY_PARAM_KEYS = ["filter"];
|
|
7
|
+
const toStandardBase64 = (text) => text.replace(/-/g, "+").replace(/_/g, "/");
|
|
8
|
+
function parseJsonObject(json) {
|
|
9
|
+
const parsed = JSON.parse(json);
|
|
10
|
+
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)
|
|
11
|
+
? parsed
|
|
12
|
+
: undefined;
|
|
13
|
+
}
|
|
14
|
+
/** koko 방식 — base64 바이트를 UTF-8 로 해석. 잘못된 바이트열이면 throw (fatal) */
|
|
15
|
+
function decodeUtf8Base64(text) {
|
|
16
|
+
const binary = atob(toStandardBase64(text));
|
|
17
|
+
const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
18
|
+
return parseJsonObject(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
19
|
+
}
|
|
20
|
+
/** 기존 `useUrlTableParams` — `btoa(JSON)` (Latin1 문자열 그대로) */
|
|
21
|
+
function decodePlainBase64(text) {
|
|
22
|
+
return parseJsonObject(atob(toStandardBase64(text)));
|
|
23
|
+
}
|
|
24
|
+
/** 기존 `tableUrlParams` — `btoa(encodeURIComponent(JSON))` */
|
|
25
|
+
function decodeUriBase64(text) {
|
|
26
|
+
return parseJsonObject(decodeURIComponent(atob(toStandardBase64(text))));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* URL 파라미터 값 하나를 세 인코딩으로 순서대로 디코드한다.
|
|
30
|
+
* 어느 것도 JSON 객체를 내지 못하면 undefined.
|
|
31
|
+
*/
|
|
32
|
+
export function decodeLegacyTableParams(text) {
|
|
33
|
+
if (!text)
|
|
34
|
+
return undefined;
|
|
35
|
+
const decoders = [decodeUtf8Base64, decodePlainBase64, decodeUriBase64];
|
|
36
|
+
for (const decode of decoders) {
|
|
37
|
+
try {
|
|
38
|
+
const result = decode(text);
|
|
39
|
+
if (result)
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// 다음 인코딩 시도
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
// ─────────────────────────────────────────────────────────────
|
|
49
|
+
// SearchConfig → SearchState 정규화
|
|
50
|
+
// ─────────────────────────────────────────────────────────────
|
|
51
|
+
/** 컬럼 정보가 없을 때 "날짜 항목" 으로 보는 연산자 */
|
|
52
|
+
const LEGACY_DATE_OPERATORS = new Set(["dateBetween", "betweenInclusive"]);
|
|
53
|
+
function convertDateValue(value) {
|
|
54
|
+
if (Array.isArray(value))
|
|
55
|
+
return value.map((v) => isoToLocalDate(v));
|
|
56
|
+
return isoToLocalDate(value);
|
|
57
|
+
}
|
|
58
|
+
function isDateColumnSet(columns) {
|
|
59
|
+
const ids = new Set();
|
|
60
|
+
for (const column of columns) {
|
|
61
|
+
const id = columnKey(column);
|
|
62
|
+
if (!id)
|
|
63
|
+
continue;
|
|
64
|
+
const variant = resolveVariant(column);
|
|
65
|
+
const dataType = column.meta?.dataType;
|
|
66
|
+
if (variant === "date" || variant === "datetime" || dataType === "date" || dataType === "datetime") {
|
|
67
|
+
ids.add(id);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return ids;
|
|
71
|
+
}
|
|
72
|
+
function normalizeFilterFns(fns) {
|
|
73
|
+
if (!fns || typeof fns !== "object" || Array.isArray(fns))
|
|
74
|
+
return undefined;
|
|
75
|
+
const out = {};
|
|
76
|
+
for (const [id, op] of Object.entries(fns)) {
|
|
77
|
+
// 문자열이면 그대로 — 검증은 서버가 한다 (모르는 연산자는 기본값으로 강등)
|
|
78
|
+
if (typeof op === "string" && op.length > 0)
|
|
79
|
+
out[id] = op;
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
function normalizeFilters(filters) {
|
|
84
|
+
if (!Array.isArray(filters))
|
|
85
|
+
return undefined;
|
|
86
|
+
const out = [];
|
|
87
|
+
for (const entry of filters) {
|
|
88
|
+
if (!entry || typeof entry !== "object")
|
|
89
|
+
continue;
|
|
90
|
+
const { id, value } = entry;
|
|
91
|
+
if (typeof id !== "string" || !id)
|
|
92
|
+
continue;
|
|
93
|
+
out.push({ id, value });
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 기존 `SearchConfig` 모양의 객체 → koko `Partial<SearchState>`.
|
|
99
|
+
*
|
|
100
|
+
* - 컬럼에 필터 kind 가 있고 `codec` 을 선언했으면 그 `decode` 를 거친다 (내장 date kind 가
|
|
101
|
+
* ISO 시각 → `YYYY-MM-DD` 를 맡는다). decode 가 undefined 면 항목을 뺀다.
|
|
102
|
+
* - 그 외 날짜 필터 값이 ISO 시각 문자열이면 로컬 `YYYY-MM-DD` 로 바꾼다.
|
|
103
|
+
* `columns` 가 주어지면 date/datetime 컬럼에만, 없으면 ISO 패턴이면서 연산자가
|
|
104
|
+
* `dateBetween`/`betweenInclusive` 인 항목에만 적용한다.
|
|
105
|
+
* - `columnFilterFns` 값이 문자열이면 그대로 둔다.
|
|
106
|
+
* - 알려진 필드(columnFilters/columnFilterFns/globalFilter/pagination/sorting/grouping/
|
|
107
|
+
* virtualColumns) 만 남긴다.
|
|
108
|
+
*/
|
|
109
|
+
export function normalizeLegacySearchState(state, columns) {
|
|
110
|
+
const src = state;
|
|
111
|
+
const result = {};
|
|
112
|
+
const fns = normalizeFilterFns(src.columnFilterFns);
|
|
113
|
+
if (fns)
|
|
114
|
+
result.columnFilterFns = fns;
|
|
115
|
+
const filters = normalizeFilters(src.columnFilters);
|
|
116
|
+
if (filters) {
|
|
117
|
+
const dateColumns = columns ? isDateColumnSet(columns) : undefined;
|
|
118
|
+
const withCodec = columns ? filterKindMap(columns) : undefined;
|
|
119
|
+
const legacyConverted = filters.map((entry) => {
|
|
120
|
+
// kind codec 이 있는 컬럼은 아래 decodeFilterValues 가 맡는다
|
|
121
|
+
if (withCodec?.get(entry.id)?.codec)
|
|
122
|
+
return entry;
|
|
123
|
+
const isDate = dateColumns
|
|
124
|
+
? dateColumns.has(entry.id)
|
|
125
|
+
: LEGACY_DATE_OPERATORS.has(fns?.[entry.id] ?? "");
|
|
126
|
+
return isDate ? { id: entry.id, value: convertDateValue(entry.value) } : entry;
|
|
127
|
+
});
|
|
128
|
+
result.columnFilters = columns ? decodeFilterValues(columns, legacyConverted) : legacyConverted;
|
|
129
|
+
}
|
|
130
|
+
if (typeof src.globalFilter === "string")
|
|
131
|
+
result.globalFilter = src.globalFilter;
|
|
132
|
+
const pagination = src.pagination;
|
|
133
|
+
if (pagination && typeof pagination === "object") {
|
|
134
|
+
const pageIndex = Number(pagination.pageIndex);
|
|
135
|
+
const pageSize = Number(pagination.pageSize);
|
|
136
|
+
if (Number.isFinite(pageIndex) && Number.isFinite(pageSize)) {
|
|
137
|
+
result.pagination = { pageIndex, pageSize };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (Array.isArray(src.sorting)) {
|
|
141
|
+
result.sorting = src.sorting
|
|
142
|
+
.filter((s) => s && typeof s.id === "string")
|
|
143
|
+
.map((s) => ({
|
|
144
|
+
id: s.id,
|
|
145
|
+
desc: Boolean(s.desc),
|
|
146
|
+
...(typeof s.key === "string" ? { key: s.key } : {}),
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
if (Array.isArray(src.grouping)) {
|
|
150
|
+
result.grouping = src.grouping.filter((g) => typeof g === "string");
|
|
151
|
+
}
|
|
152
|
+
if (src.virtualColumns && typeof src.virtualColumns === "object" && !Array.isArray(src.virtualColumns)) {
|
|
153
|
+
result.virtualColumns = src.virtualColumns;
|
|
154
|
+
}
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 프리셋(TablePreset `state_json`) 계약.
|
|
3
|
+
*
|
|
4
|
+
* 왜 필요한가: ERP `packages/table/preset/usePreset.ts`(서버 API 훅) 와
|
|
5
|
+
* `Save/Edit/ManagePresetsDialog` 는 MRT 무의존이라 그대로 재사용하지만,
|
|
6
|
+
* `preset/presetStateUtils.ts` 의 `extractPresetState` / `applyPresetToTable` 은
|
|
7
|
+
* `table.params` / `table.setParams` / `table.setDensity` 라는 MRT 확장 인스턴스에
|
|
8
|
+
* 붙어 있어 koko `DataTable` 에서는 동작하지 않는다 (server.md D-1, gap.md 1절 13).
|
|
9
|
+
*
|
|
10
|
+
* 여기서는 같은 `PresetState` 모양을 core 에 정의하고
|
|
11
|
+
* - 검색 부분은 `table.search.params` / `table.search.setParams`(완전한 SearchState —
|
|
12
|
+
* 현재 params 와 병합해서 넘긴다),
|
|
13
|
+
* - 컬럼 UI 부분은 TanStack `table.getState()` / `setColumnVisibility` 등,
|
|
14
|
+
* - `density` 는 TanStack 상태에 없으므로 `extras` / `handlers` 로 무손실 왕복
|
|
15
|
+
* 하도록 옮겼다. `usePreset` 자체는 만들지 않는다 — ERP 가 기존 것을 쓴다.
|
|
16
|
+
*
|
|
17
|
+
* `usePreset` 이 요구하는 것은 `PresetState` 직렬화 가능 객체 하나뿐이다:
|
|
18
|
+
* savePreset(name, extractPresetState(table, { density }), scope)
|
|
19
|
+
* applyPresetState(table, preset.state_json, { setDensity })
|
|
20
|
+
*/
|
|
21
|
+
import type { RowData } from "@tanstack/react-table";
|
|
22
|
+
import type { DataTable } from "../types/table";
|
|
23
|
+
import type { SearchState } from "../types/search";
|
|
24
|
+
import type { FilterOperator } from "../types/operators";
|
|
25
|
+
import type { TableColumnDefs } from "../types/column";
|
|
26
|
+
export type PresetDensity = "comfortable" | "compact" | "spacious";
|
|
27
|
+
/** 기존 `preset/types.ts PresetState` 와 필드 동일 (sorting[].key 만 optional 로 추가) */
|
|
28
|
+
export interface PresetState {
|
|
29
|
+
columnVisibility?: Record<string, boolean>;
|
|
30
|
+
columnOrder?: string[];
|
|
31
|
+
columnSizing?: Record<string, number>;
|
|
32
|
+
columnPinning?: {
|
|
33
|
+
left?: string[];
|
|
34
|
+
right?: string[];
|
|
35
|
+
};
|
|
36
|
+
columnFilters?: Array<{
|
|
37
|
+
id: string;
|
|
38
|
+
value: unknown;
|
|
39
|
+
}>;
|
|
40
|
+
columnFilterFns?: Record<string, string>;
|
|
41
|
+
globalFilter?: string;
|
|
42
|
+
sorting?: Array<{
|
|
43
|
+
id: string;
|
|
44
|
+
desc: boolean;
|
|
45
|
+
key?: string;
|
|
46
|
+
}>;
|
|
47
|
+
grouping?: string[];
|
|
48
|
+
density?: PresetDensity;
|
|
49
|
+
pagination?: {
|
|
50
|
+
pageIndex: number;
|
|
51
|
+
pageSize: number;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export interface ExtractPresetExtras {
|
|
55
|
+
/** koko 에는 density 상태가 없다 — 렌더러/호출부가 들고 있는 값을 넘긴다 */
|
|
56
|
+
density?: PresetDensity;
|
|
57
|
+
}
|
|
58
|
+
export interface ApplyPresetHandlers {
|
|
59
|
+
/** 저장된 density 를 받을 곳. 미지정이면 density 는 무시된다 */
|
|
60
|
+
setDensity?: (density: PresetDensity) => void;
|
|
61
|
+
}
|
|
62
|
+
/** 검색 부분만 — `usePreset` 없이 `useTableSearch({ initial })` 에 넘길 때 */
|
|
63
|
+
export type PresetSearchPart = Pick<PresetState, "columnFilters" | "columnFilterFns" | "globalFilter" | "sorting" | "grouping" | "pagination">;
|
|
64
|
+
/**
|
|
65
|
+
* `DataTable` 에서 저장할 상태를 추출한다.
|
|
66
|
+
*
|
|
67
|
+
* - 컬럼 UI(columnVisibility/Order/Sizing/Pinning)는 `table.getState()`
|
|
68
|
+
* - 검색(columnFilters/columnFilterFns/globalFilter/sorting/grouping/pagination)은
|
|
69
|
+
* `table.search.params`. 연산자는 `search.operators`(컬럼 기본값 + 사용자 변경분의
|
|
70
|
+
* 완전한 맵)를 싣는다 — 기존도 `params.columnFilterFns` 완전 맵을 저장했다.
|
|
71
|
+
* - density 는 `extras.density`
|
|
72
|
+
* - 필터 값은 컬럼 kind 의 `codec.encode` 를 거친다 (내장 date kind: Date/ISO → `YYYY-MM-DD`)
|
|
73
|
+
*/
|
|
74
|
+
export declare function extractPresetState<T extends RowData>(table: DataTable<T>, extras?: ExtractPresetExtras): PresetState;
|
|
75
|
+
/**
|
|
76
|
+
* 저장된 columnOrder 를 현재 컬럼 정의와 맞춘다 (기존 `reconcileColumnOrder` 그대로).
|
|
77
|
+
*
|
|
78
|
+
* TanStack 은 order 배열에 없는 컬럼을 맨 뒤에 append 하므로, 프리셋 저장 이후
|
|
79
|
+
* 추가된 컬럼이 표 오른쪽 끝으로 밀려난다. 정의 순서상 바로 앞 컬럼 뒤에 끼워넣어
|
|
80
|
+
* 원래 자리에 가깝게 복원한다. 사라진 컬럼 id 는 제거.
|
|
81
|
+
*/
|
|
82
|
+
export declare function reconcileColumnOrder(currentIds: string[], storedOrder: string[]): string[];
|
|
83
|
+
/**
|
|
84
|
+
* 프리셋의 검색 부분을 **완전한 SearchState** 로 만든다.
|
|
85
|
+
*
|
|
86
|
+
* `table.search.setParams` 는 부분 상태를 받지 않으므로 현재 params 위에 얹는다.
|
|
87
|
+
* 연산자는 기존 `applyPresetToTable` 과 같은 방향으로 병합한다 — 컬럼 기본값
|
|
88
|
+
* (`baseOperators`, 보통 `search.operators`)이 새 컬럼을 보완하고, 프리셋 값이 덮어쓴다.
|
|
89
|
+
* `columns` 를 주면 필터 값이 컬럼 kind 의 `codec.decode` 를 거친다.
|
|
90
|
+
*/
|
|
91
|
+
export declare function presetToSearchState(preset: PresetSearchPart, current: SearchState, baseOperators?: Record<string, FilterOperator>, columns?: TableColumnDefs<any>): SearchState;
|
|
92
|
+
/**
|
|
93
|
+
* `useTableSearch({ initial })` 에 넘길 검색 초기값 — 마운트 시점에 기본 프리셋을
|
|
94
|
+
* 적용할 때 쓴다. 연산자는 `initialState` 가 `deriveOperators(columns)` 위에 얹는다.
|
|
95
|
+
*/
|
|
96
|
+
export declare function presetToSearchInitial(preset: PresetSearchPart, columns?: TableColumnDefs<any>): Partial<SearchState>;
|
|
97
|
+
/**
|
|
98
|
+
* 프리셋 상태를 `DataTable` 에 적용한다.
|
|
99
|
+
*
|
|
100
|
+
* - 검색 부분은 `table.search.setParams(완전한 상태)` 로 즉시 적용 (조회 트리거)
|
|
101
|
+
* - 컬럼 UI 부분은 TanStack setter 로 즉시 적용 (시각 변경만)
|
|
102
|
+
* - density 는 `handlers.setDensity` 가 있을 때만
|
|
103
|
+
*/
|
|
104
|
+
export declare function applyPresetState<T extends RowData>(table: DataTable<T>, preset: PresetState, handlers?: ApplyPresetHandlers): void;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { DEFAULT_PAGE_SIZE } from "../defaults";
|
|
2
|
+
import { decodeFilterValues, encodeFilterValues } from "../filters/codec";
|
|
3
|
+
const toOperators = (fns) => {
|
|
4
|
+
const out = {};
|
|
5
|
+
for (const [id, op] of Object.entries(fns ?? {})) {
|
|
6
|
+
if (typeof op === "string" && op.length > 0)
|
|
7
|
+
out[id] = op;
|
|
8
|
+
}
|
|
9
|
+
return out;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* `DataTable` 에서 저장할 상태를 추출한다.
|
|
13
|
+
*
|
|
14
|
+
* - 컬럼 UI(columnVisibility/Order/Sizing/Pinning)는 `table.getState()`
|
|
15
|
+
* - 검색(columnFilters/columnFilterFns/globalFilter/sorting/grouping/pagination)은
|
|
16
|
+
* `table.search.params`. 연산자는 `search.operators`(컬럼 기본값 + 사용자 변경분의
|
|
17
|
+
* 완전한 맵)를 싣는다 — 기존도 `params.columnFilterFns` 완전 맵을 저장했다.
|
|
18
|
+
* - density 는 `extras.density`
|
|
19
|
+
* - 필터 값은 컬럼 kind 의 `codec.encode` 를 거친다 (내장 date kind: Date/ISO → `YYYY-MM-DD`)
|
|
20
|
+
*/
|
|
21
|
+
export function extractPresetState(table, extras = {}) {
|
|
22
|
+
const state = table.getState();
|
|
23
|
+
const params = table.search.params;
|
|
24
|
+
return {
|
|
25
|
+
columnVisibility: { ...state.columnVisibility },
|
|
26
|
+
columnOrder: [...(state.columnOrder ?? [])],
|
|
27
|
+
columnSizing: { ...state.columnSizing },
|
|
28
|
+
columnPinning: {
|
|
29
|
+
left: [...(state.columnPinning?.left ?? [])],
|
|
30
|
+
right: [...(state.columnPinning?.right ?? [])],
|
|
31
|
+
},
|
|
32
|
+
columnFilters: encodeFilterValues(table.search.columns, params.columnFilters)
|
|
33
|
+
.map((f) => ({ id: f.id, value: f.value })),
|
|
34
|
+
columnFilterFns: { ...table.search.operators },
|
|
35
|
+
globalFilter: params.globalFilter ?? "",
|
|
36
|
+
sorting: params.sorting.map((s) => ({ id: s.id, desc: s.desc, ...(s.key ? { key: s.key } : {}) })),
|
|
37
|
+
grouping: [...(params.grouping ?? [])],
|
|
38
|
+
...(extras.density !== undefined ? { density: extras.density } : {}),
|
|
39
|
+
pagination: {
|
|
40
|
+
pageIndex: params.pagination?.pageIndex ?? state.pagination?.pageIndex ?? 0,
|
|
41
|
+
pageSize: params.pagination?.pageSize ?? state.pagination?.pageSize ?? DEFAULT_PAGE_SIZE,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 저장된 columnOrder 를 현재 컬럼 정의와 맞춘다 (기존 `reconcileColumnOrder` 그대로).
|
|
47
|
+
*
|
|
48
|
+
* TanStack 은 order 배열에 없는 컬럼을 맨 뒤에 append 하므로, 프리셋 저장 이후
|
|
49
|
+
* 추가된 컬럼이 표 오른쪽 끝으로 밀려난다. 정의 순서상 바로 앞 컬럼 뒤에 끼워넣어
|
|
50
|
+
* 원래 자리에 가깝게 복원한다. 사라진 컬럼 id 는 제거.
|
|
51
|
+
*/
|
|
52
|
+
export function reconcileColumnOrder(currentIds, storedOrder) {
|
|
53
|
+
const currentSet = new Set(currentIds);
|
|
54
|
+
const result = storedOrder.filter((id) => currentSet.has(id));
|
|
55
|
+
const kept = new Set(result);
|
|
56
|
+
currentIds.forEach((id, i) => {
|
|
57
|
+
if (kept.has(id))
|
|
58
|
+
return;
|
|
59
|
+
let anchor = -1;
|
|
60
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
61
|
+
const pos = result.indexOf(currentIds[j]);
|
|
62
|
+
if (pos !== -1) {
|
|
63
|
+
anchor = pos;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
result.splice(anchor + 1, 0, id);
|
|
68
|
+
kept.add(id);
|
|
69
|
+
});
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 프리셋의 검색 부분을 **완전한 SearchState** 로 만든다.
|
|
74
|
+
*
|
|
75
|
+
* `table.search.setParams` 는 부분 상태를 받지 않으므로 현재 params 위에 얹는다.
|
|
76
|
+
* 연산자는 기존 `applyPresetToTable` 과 같은 방향으로 병합한다 — 컬럼 기본값
|
|
77
|
+
* (`baseOperators`, 보통 `search.operators`)이 새 컬럼을 보완하고, 프리셋 값이 덮어쓴다.
|
|
78
|
+
* `columns` 를 주면 필터 값이 컬럼 kind 의 `codec.decode` 를 거친다.
|
|
79
|
+
*/
|
|
80
|
+
export function presetToSearchState(preset, current, baseOperators = current.columnFilterFns, columns) {
|
|
81
|
+
return {
|
|
82
|
+
...current,
|
|
83
|
+
columnFilters: decodeFilterValues(columns, (preset.columnFilters ?? []).map((f) => ({
|
|
84
|
+
id: f.id,
|
|
85
|
+
value: Array.isArray(f.value) ? [...f.value] : f.value,
|
|
86
|
+
}))),
|
|
87
|
+
columnFilterFns: { ...baseOperators, ...toOperators(preset.columnFilterFns) },
|
|
88
|
+
sorting: (preset.sorting ?? []).map((s) => ({ ...s })),
|
|
89
|
+
grouping: [...(preset.grouping ?? [])],
|
|
90
|
+
globalFilter: preset.globalFilter ?? "",
|
|
91
|
+
pagination: {
|
|
92
|
+
pageIndex: preset.pagination?.pageIndex ?? 0,
|
|
93
|
+
pageSize: preset.pagination?.pageSize ?? DEFAULT_PAGE_SIZE,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* `useTableSearch({ initial })` 에 넘길 검색 초기값 — 마운트 시점에 기본 프리셋을
|
|
99
|
+
* 적용할 때 쓴다. 연산자는 `initialState` 가 `deriveOperators(columns)` 위에 얹는다.
|
|
100
|
+
*/
|
|
101
|
+
export function presetToSearchInitial(preset, columns) {
|
|
102
|
+
return {
|
|
103
|
+
columnFilters: decodeFilterValues(columns, (preset.columnFilters ?? []).map((f) => ({ id: f.id, value: f.value }))),
|
|
104
|
+
columnFilterFns: toOperators(preset.columnFilterFns),
|
|
105
|
+
globalFilter: preset.globalFilter ?? "",
|
|
106
|
+
sorting: (preset.sorting ?? []).map((s) => ({ ...s })),
|
|
107
|
+
...(preset.grouping !== undefined ? { grouping: [...preset.grouping] } : {}),
|
|
108
|
+
...(preset.pagination !== undefined ? { pagination: { ...preset.pagination } } : {}),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 프리셋 상태를 `DataTable` 에 적용한다.
|
|
113
|
+
*
|
|
114
|
+
* - 검색 부분은 `table.search.setParams(완전한 상태)` 로 즉시 적용 (조회 트리거)
|
|
115
|
+
* - 컬럼 UI 부분은 TanStack setter 로 즉시 적용 (시각 변경만)
|
|
116
|
+
* - density 는 `handlers.setDensity` 가 있을 때만
|
|
117
|
+
*/
|
|
118
|
+
export function applyPresetState(table, preset, handlers = {}) {
|
|
119
|
+
table.search.setParams(presetToSearchState(preset, table.search.params, { ...table.search.operators }, table.search.columns));
|
|
120
|
+
if (preset.columnVisibility !== undefined) {
|
|
121
|
+
table.setColumnVisibility({ ...preset.columnVisibility });
|
|
122
|
+
}
|
|
123
|
+
if (preset.columnOrder !== undefined) {
|
|
124
|
+
const currentIds = table.getAllLeafColumns().map((c) => c.id);
|
|
125
|
+
table.setColumnOrder(currentIds.length > 0
|
|
126
|
+
? reconcileColumnOrder(currentIds, preset.columnOrder)
|
|
127
|
+
: [...preset.columnOrder]);
|
|
128
|
+
}
|
|
129
|
+
if (preset.columnSizing !== undefined) {
|
|
130
|
+
table.setColumnSizing({ ...preset.columnSizing });
|
|
131
|
+
}
|
|
132
|
+
if (preset.columnPinning !== undefined) {
|
|
133
|
+
table.setColumnPinning({
|
|
134
|
+
left: [...(preset.columnPinning.left ?? [])],
|
|
135
|
+
right: [...(preset.columnPinning.right ?? [])],
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
if (preset.density !== undefined) {
|
|
139
|
+
handlers.setDensity?.(preset.density);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 프리셋 저장소 계약.
|
|
3
|
+
*
|
|
4
|
+
* `preset.ts` 는 "상태를 JSON 으로 뽑고 되돌리는" 순수 함수만 두고, 어디에 저장하는가는
|
|
5
|
+
* 소비 앱 몫으로 남겨 두었다. 그 결과 스토리마다 localStorage 조립 코드가 반복됐고,
|
|
6
|
+
* ERP 는 react-query `usePreset` 을 따로 들고 있어 mui `PresetBar` 같은 공용 UI 를 만들 수
|
|
7
|
+
* 없었다. 여기서는 저장소를 **비동기 인터페이스 하나**(`PresetStore`) 로 고정한다 —
|
|
8
|
+
* localStorage 든 서버 API 든 같은 네 메서드만 있으면 `usePresets` / `PresetBar` 가 붙는다.
|
|
9
|
+
*
|
|
10
|
+
* ERP 서버(`TablePresetApi`) 연결 예:
|
|
11
|
+
* const store: PresetStore = {
|
|
12
|
+
* list: (tableKey) => fetchPresets(tableKey).then((rows) => rows.map(fromErp)),
|
|
13
|
+
* save: (p) => (p.id ? patchPreset(Number(p.id), toErp(p)) : createPreset(toErp(p))).then(fromErp),
|
|
14
|
+
* remove: (id) => removePreset(Number(id)),
|
|
15
|
+
* setDefault: (tableKey, id) => (id ? setDefaultPreset(Number(id), tableKey).then(() => undefined) : Promise.resolve()),
|
|
16
|
+
* };
|
|
17
|
+
*/
|
|
18
|
+
import type { PresetState } from "./preset";
|
|
19
|
+
/** `"user"` = 나만 · `"shared"` = 같은 표를 쓰는 모두 (ERP `private`/`shared` 대응) */
|
|
20
|
+
export type PresetScope = "user" | "shared";
|
|
21
|
+
export interface TablePreset {
|
|
22
|
+
id: string;
|
|
23
|
+
/** 표 식별자 — 같은 컬럼 세트를 쓰는 화면끼리 공유한다 (`useTable` 의 tableKey 와 같은 값) */
|
|
24
|
+
tableKey: string;
|
|
25
|
+
name: string;
|
|
26
|
+
state: PresetState;
|
|
27
|
+
scope?: PresetScope;
|
|
28
|
+
/** 표를 열 때 자동 적용되는 프리셋. 표 하나에 하나만 */
|
|
29
|
+
isDefault?: boolean;
|
|
30
|
+
createdAt?: string;
|
|
31
|
+
updatedAt?: string;
|
|
32
|
+
/** 만든 사람 식별자 — 공유 프리셋의 수정 권한 판정은 소비 앱 몫 */
|
|
33
|
+
owner?: string;
|
|
34
|
+
}
|
|
35
|
+
/** `save` 입력 — `id` 가 있으면 갱신, 없으면 생성 */
|
|
36
|
+
export type PresetInput = Omit<TablePreset, "id"> & {
|
|
37
|
+
id?: string;
|
|
38
|
+
};
|
|
39
|
+
export interface PresetStore {
|
|
40
|
+
list(tableKey: string): Promise<TablePreset[]>;
|
|
41
|
+
/** `id` 없으면 생성, 있으면 덮어쓰기. 저장된 결과(서버가 채운 id·시각 포함)를 돌려준다 */
|
|
42
|
+
save(preset: PresetInput): Promise<TablePreset>;
|
|
43
|
+
remove(id: string): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* 기본 프리셋 지정(`null` 이면 해제). 미구현이면 `usePresets` 가 `save` 로 `isDefault` 를
|
|
46
|
+
* 바꿔 가며 흉내 낸다 — 서버가 "표당 하나" 를 보장하는 API 가 있으면 여기에 연결한다.
|
|
47
|
+
*/
|
|
48
|
+
setDefault?(tableKey: string, id: string | null): Promise<void>;
|
|
49
|
+
}
|
|
50
|
+
export declare function newPresetId(): string;
|
|
51
|
+
export interface MemoryPresetStoreOptions {
|
|
52
|
+
/** 초기 데이터 */
|
|
53
|
+
seed?: TablePreset[];
|
|
54
|
+
/** 모든 호출을 이만큼 늦춘다 — 서버 왕복을 흉내 낼 때 (스토리·테스트) */
|
|
55
|
+
delayMs?: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 메모리 저장소 — 테스트·스토리·"서버가 아직 없는" 화면용.
|
|
59
|
+
* 돌려주는 객체는 매번 복사본이라 호출부가 바꿔도 내부 상태가 오염되지 않는다.
|
|
60
|
+
*/
|
|
61
|
+
export declare function createMemoryPresetStore(options?: MemoryPresetStoreOptions): PresetStore & {
|
|
62
|
+
/** 현재 내용 (테스트용) */
|
|
63
|
+
dump(): TablePreset[];
|
|
64
|
+
};
|
|
65
|
+
export interface LocalStoragePresetStoreOptions {
|
|
66
|
+
/** 저장 키 접두어. 기본 `"koko-table:presets"` — 표마다 `${namespace}:${tableKey}` 에 저장 */
|
|
67
|
+
namespace?: string;
|
|
68
|
+
/** 기본 `globalThis.localStorage`. SSR 등에서 없으면 메모리로 동작한다 */
|
|
69
|
+
storage?: Pick<Storage, "getItem" | "setItem" | "removeItem">;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* localStorage 저장소 — 서버 없이 브라우저에만 남긴다.
|
|
73
|
+
* 접근 실패(사파리 프라이빗 모드·권한)는 삼키고 빈 목록으로 동작한다.
|
|
74
|
+
*/
|
|
75
|
+
export declare function createLocalStoragePresetStore(namespaceOrOptions?: string | LocalStoragePresetStoreOptions): PresetStore;
|
|
76
|
+
/**
|
|
77
|
+
* 기본 프리셋 하나를 미리 읽는다 — 마운트 시점에 `useTableSearch({ initial })` 의 초기값으로
|
|
78
|
+
* 쓰려면 훅보다 먼저(라우트 loader·상위 컴포넌트) 불러 `presetToSearchInitial(preset.state)` 를
|
|
79
|
+
* 넘긴다. 이미 마운트된 표에는 `usePresets({ autoApplyDefault: true })` 가 같은 일을 한다.
|
|
80
|
+
*/
|
|
81
|
+
export declare function loadDefaultPreset(store: PresetStore, tableKey: string): Promise<TablePreset | null>;
|