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,49 @@
|
|
|
1
|
+
import type { RowData } from "@tanstack/react-table";
|
|
2
|
+
import type { AggregationDef, AggregationId, AggregationSpec, Registry, TableColumnDefs } from "../../../core/src/index";
|
|
3
|
+
export interface AggregationChipsProps<T extends RowData> {
|
|
4
|
+
/** 현재 집계 명세 `{ 컬럼: [함수, …] }` */
|
|
5
|
+
value: AggregationSpec;
|
|
6
|
+
onChange: (value: AggregationSpec) => void;
|
|
7
|
+
/** 집계 후보를 고를 컬럼 정의 — `meta.enableAgg` 이거나 숫자/enum/boolean 컬럼 */
|
|
8
|
+
columns: TableColumnDefs<T>;
|
|
9
|
+
/**
|
|
10
|
+
* 서버가 계산할 수 있는 함수만 보인다 (정의의 `server: true`).
|
|
11
|
+
* 서버 그룹핑 화면에서 켠다 — median/stddev 를 골라 보내면 서버가 400 을 준다.
|
|
12
|
+
*/
|
|
13
|
+
serverOnly?: boolean;
|
|
14
|
+
/** 특정 컬럼만 보이게 좁힌다 */
|
|
15
|
+
columnIds?: string[];
|
|
16
|
+
size?: "small" | "medium";
|
|
17
|
+
/** 집계 레지스트리 — 미지정 시 Provider(`useAggregations()`) */
|
|
18
|
+
registry?: Registry<AggregationDef>;
|
|
19
|
+
}
|
|
20
|
+
interface AggregatableColumn {
|
|
21
|
+
id: string;
|
|
22
|
+
label: string;
|
|
23
|
+
keys: AggregationId[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 집계를 걸 수 있는 컬럼과 그 컬럼에 허용되는 함수 목록.
|
|
27
|
+
*
|
|
28
|
+
* 함수 후보는 레지스트리 정의의 `applies(dataType)` 로 정한다 (내장: 숫자 → sum·avg·median·min·max·stddev·고유·개수·점유율,
|
|
29
|
+
* boolean → 고유·개수·점유율, 나머지 → 고유·개수·점유율·최빈값). 사용자 집계(`p90` 등)도 `applies` 가 참이면 뜬다.
|
|
30
|
+
* `enableAgg` 가 꺼져 있어도 숫자/enum/boolean 은 후보에 올린다 — 칩은 "켤 수 있는 것"
|
|
31
|
+
* 을 보여 주는 UI 라서, 선언에 없는 컬럼을 사용자가 즉석에서 집계할 수 있어야 한다.
|
|
32
|
+
*/
|
|
33
|
+
export declare function aggregatableColumns<T extends RowData>(columns: TableColumnDefs<T>, options?: {
|
|
34
|
+
serverOnly?: boolean;
|
|
35
|
+
columnIds?: string[];
|
|
36
|
+
registry?: Registry<AggregationDef>;
|
|
37
|
+
}): AggregatableColumn[];
|
|
38
|
+
/** 명세에서 함수 하나를 켜거나 끈다. 컬럼의 함수가 모두 꺼지면 키 자체를 지운다 */
|
|
39
|
+
export declare function toggleAggregation(spec: AggregationSpec, columnId: string, fn: AggregationId): AggregationSpec;
|
|
40
|
+
/**
|
|
41
|
+
* 집계 함수 선택 칩 — 컬럼별로 어떤 집계를 그룹 헤더에 띄울지 토글한다.
|
|
42
|
+
*
|
|
43
|
+
* 결과는 `AggregationSpec` 그대로라 `useGrouping({ aggregation })` 에 넣으면
|
|
44
|
+
* 클라이언트 그룹 헤더가 바뀌고, `search.patchParams({ aggregation })` 에 넣으면
|
|
45
|
+
* 서버 요청의 `aggregation` 이 바뀐다. 두 경로가 같은 값을 쓰므로 화면이 모드를
|
|
46
|
+
* 바꿔도 이 컴포넌트는 그대로다.
|
|
47
|
+
*/
|
|
48
|
+
export declare function AggregationChips<T extends RowData>({ value, onChange, columns, serverOnly, columnIds, size, registry: givenRegistry, }: AggregationChipsProps<T>): import("react").JSX.Element;
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Chip, Stack, Tooltip, Typography } from "@mui/material";
|
|
3
|
+
import { aggregations as defaultAggregations, aggregationKeysFor, aggregationLabel, columnKey, isServerAggregation, } from "../../../core/src/index";
|
|
4
|
+
import { useKokoTableConfig } from "../provider/KokoTableProvider";
|
|
5
|
+
import { useAggregations } from "./AggregateChip";
|
|
6
|
+
/**
|
|
7
|
+
* 집계를 걸 수 있는 컬럼과 그 컬럼에 허용되는 함수 목록.
|
|
8
|
+
*
|
|
9
|
+
* 함수 후보는 레지스트리 정의의 `applies(dataType)` 로 정한다 (내장: 숫자 → sum·avg·median·min·max·stddev·고유·개수·점유율,
|
|
10
|
+
* boolean → 고유·개수·점유율, 나머지 → 고유·개수·점유율·최빈값). 사용자 집계(`p90` 등)도 `applies` 가 참이면 뜬다.
|
|
11
|
+
* `enableAgg` 가 꺼져 있어도 숫자/enum/boolean 은 후보에 올린다 — 칩은 "켤 수 있는 것"
|
|
12
|
+
* 을 보여 주는 UI 라서, 선언에 없는 컬럼을 사용자가 즉석에서 집계할 수 있어야 한다.
|
|
13
|
+
*/
|
|
14
|
+
export function aggregatableColumns(columns, options = {}) {
|
|
15
|
+
const registry = options.registry ?? defaultAggregations;
|
|
16
|
+
const list = [];
|
|
17
|
+
for (const column of columns) {
|
|
18
|
+
const id = columnKey(column);
|
|
19
|
+
if (!id)
|
|
20
|
+
continue;
|
|
21
|
+
if (options.columnIds && !options.columnIds.includes(id))
|
|
22
|
+
continue;
|
|
23
|
+
const meta = column.meta;
|
|
24
|
+
if (meta?.forceHidden)
|
|
25
|
+
continue;
|
|
26
|
+
// enableAgg: false 는 "집계 대상 아님" 명시 — 참조번호(ColumnIsIDX/SpecificNumber)처럼
|
|
27
|
+
// 숫자지만 합계·평균이 뜻이 없는 컬럼이 여기 든다
|
|
28
|
+
if (meta?.enableAgg === false)
|
|
29
|
+
continue;
|
|
30
|
+
const dataType = meta?.dataType;
|
|
31
|
+
const typed = dataType === "number" || dataType === "enum" || dataType === "select" || dataType === "boolean";
|
|
32
|
+
if (!meta?.enableAgg && !typed)
|
|
33
|
+
continue;
|
|
34
|
+
const keys = aggregationKeysFor(dataType, registry, { serverOnly: options.serverOnly });
|
|
35
|
+
if (keys.length === 0)
|
|
36
|
+
continue;
|
|
37
|
+
const header = column.filterLabel ?? column.header;
|
|
38
|
+
list.push({ id, label: typeof header === "string" ? header : id, keys });
|
|
39
|
+
}
|
|
40
|
+
return list;
|
|
41
|
+
}
|
|
42
|
+
/** 명세에서 함수 하나를 켜거나 끈다. 컬럼의 함수가 모두 꺼지면 키 자체를 지운다 */
|
|
43
|
+
export function toggleAggregation(spec, columnId, fn) {
|
|
44
|
+
const current = spec[columnId] ?? [];
|
|
45
|
+
const next = current.includes(fn) ? current.filter((key) => key !== fn) : [...current, fn];
|
|
46
|
+
const result = { ...spec };
|
|
47
|
+
if (next.length === 0)
|
|
48
|
+
delete result[columnId];
|
|
49
|
+
else
|
|
50
|
+
result[columnId] = next;
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 집계 함수 선택 칩 — 컬럼별로 어떤 집계를 그룹 헤더에 띄울지 토글한다.
|
|
55
|
+
*
|
|
56
|
+
* 결과는 `AggregationSpec` 그대로라 `useGrouping({ aggregation })` 에 넣으면
|
|
57
|
+
* 클라이언트 그룹 헤더가 바뀌고, `search.patchParams({ aggregation })` 에 넣으면
|
|
58
|
+
* 서버 요청의 `aggregation` 이 바뀐다. 두 경로가 같은 값을 쓰므로 화면이 모드를
|
|
59
|
+
* 바꿔도 이 컴포넌트는 그대로다.
|
|
60
|
+
*/
|
|
61
|
+
export function AggregationChips({ value, onChange, columns, serverOnly = false, columnIds, size = "small", registry: givenRegistry, }) {
|
|
62
|
+
const { labels } = useKokoTableConfig();
|
|
63
|
+
const provided = useAggregations();
|
|
64
|
+
const registry = givenRegistry ?? provided;
|
|
65
|
+
const aggLabels = labels.aggregations;
|
|
66
|
+
const labelOf = (fn) => aggLabels[fn] ?? aggregationLabel(fn, registry);
|
|
67
|
+
const targets = aggregatableColumns(columns, { serverOnly, columnIds, registry });
|
|
68
|
+
if (targets.length === 0) {
|
|
69
|
+
return (_jsx(Typography, { variant: "body2", color: "text.disabled", children: "\uC9D1\uACC4\uD560 \uC218 \uC788\uB294 \uCEEC\uB7FC\uC774 \uC5C6\uC2B5\uB2C8\uB2E4" }));
|
|
70
|
+
}
|
|
71
|
+
return (_jsx(Stack, { spacing: 0.5, role: "group", "aria-label": "\uC9D1\uACC4 \uD568\uC218", children: targets.map((target) => {
|
|
72
|
+
const selected = value[target.id] ?? [];
|
|
73
|
+
return (_jsxs(Stack, { direction: "row", alignItems: "center", sx: { flexWrap: "wrap", gap: 0.5 }, children: [_jsx(Typography, { variant: "body2", sx: { minWidth: 64, fontWeight: 600 }, children: target.label }), target.keys.map((fn) => {
|
|
74
|
+
const on = selected.includes(fn);
|
|
75
|
+
const serverCannot = !isServerAggregation(fn, registry);
|
|
76
|
+
const fnLabel = labelOf(fn);
|
|
77
|
+
return (_jsx(Tooltip, { title: serverCannot ? `${fnLabel} — 클라이언트 전용 (서버 집계 없음)` : fnLabel, enterDelay: 500, children: _jsx(Chip, { size: size, label: fnLabel, color: on ? "primary" : "default", variant: on ? "filled" : "outlined", "aria-pressed": on, "aria-label": `${target.label} ${fnLabel}`, onClick: () => onChange(toggleAggregation(value, target.id, fn)), sx: serverCannot ? { borderStyle: "dashed" } : undefined }) }, fn));
|
|
78
|
+
})] }, target.id));
|
|
79
|
+
}) }));
|
|
80
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RowData } from "@tanstack/react-table";
|
|
2
|
+
import { type AggregationChipsProps } from "./AggregationChips";
|
|
3
|
+
export interface AggregationSettingsButtonProps<T extends RowData> extends AggregationChipsProps<T> {
|
|
4
|
+
tooltip?: string;
|
|
5
|
+
size?: "small" | "medium";
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* 집계 함수 선택을 **상세 설정** 뒤로 숨긴다.
|
|
9
|
+
*
|
|
10
|
+
* 초기 집계는 컬럼 정의(`setAgg`)에서 파생되므로 보통 사용자가 만질 일이 없다. 그래서
|
|
11
|
+
* 칩 묶음을 화면에 늘어놓지 않고 톱니 버튼 하나만 두고, 누르면 팝오버에 `AggregationChips` 를 연다.
|
|
12
|
+
* 그룹 행의 집계 칩 클릭(함수 전환)·헤더 메뉴 "집계" 항목과 같은 상태를 바꾼다.
|
|
13
|
+
*/
|
|
14
|
+
export declare function AggregationSettingsButton<T extends RowData>({ tooltip, size, ...chips }: AggregationSettingsButtonProps<T>): import("react").JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Box, IconButton, Popover, Tooltip, Typography } from "@mui/material";
|
|
4
|
+
import TuneIcon from "@mui/icons-material/Tune";
|
|
5
|
+
import { AggregationChips } from "./AggregationChips";
|
|
6
|
+
import { useKokoTableConfig } from "../provider/KokoTableProvider";
|
|
7
|
+
/**
|
|
8
|
+
* 집계 함수 선택을 **상세 설정** 뒤로 숨긴다.
|
|
9
|
+
*
|
|
10
|
+
* 초기 집계는 컬럼 정의(`setAgg`)에서 파생되므로 보통 사용자가 만질 일이 없다. 그래서
|
|
11
|
+
* 칩 묶음을 화면에 늘어놓지 않고 톱니 버튼 하나만 두고, 누르면 팝오버에 `AggregationChips` 를 연다.
|
|
12
|
+
* 그룹 행의 집계 칩 클릭(함수 전환)·헤더 메뉴 "집계" 항목과 같은 상태를 바꾼다.
|
|
13
|
+
*/
|
|
14
|
+
export function AggregationSettingsButton({ tooltip, size = "small", ...chips }) {
|
|
15
|
+
const { labels } = useKokoTableConfig();
|
|
16
|
+
const [anchor, setAnchor] = useState(null);
|
|
17
|
+
const title = tooltip ?? labels.ui.aggregationSettings;
|
|
18
|
+
return (_jsxs(_Fragment, { children: [_jsx(Tooltip, { title: title, children: _jsx(IconButton, { size: size, "aria-label": title, onClick: (e) => setAnchor(e.currentTarget), sx: { color: "text.secondary" }, children: _jsx(TuneIcon, { fontSize: "small" }) }) }), _jsx(Popover, { open: !!anchor, anchorEl: anchor, onClose: () => setAnchor(null), anchorOrigin: { vertical: "bottom", horizontal: "right" }, transformOrigin: { vertical: "top", horizontal: "right" }, children: _jsxs(Box, { sx: { p: 1.5, maxWidth: 520 }, children: [_jsx(Typography, { variant: "caption", color: "text.secondary", sx: { display: "block", mb: 1 }, children: title }), _jsx(AggregationChips, { ...chips })] }) })] }));
|
|
19
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import type { RowData } from "@tanstack/react-table";
|
|
3
|
+
import type { TableColumnDefs } from "../../../core/src/index";
|
|
4
|
+
import type { GroupedColumnMode } from "../content/TableContent";
|
|
5
|
+
/**
|
|
6
|
+
* 헤더 셀·그룹 칩이 드래그할 때 `dataTransfer` 에 싣는 MIME 키.
|
|
7
|
+
*
|
|
8
|
+
* `HeaderCell` 의 `draggableGroup` 이 같은 키로 컬럼 id 를 싣는다 — 바는 이 키만
|
|
9
|
+
* 보고 "그룹으로 추가할 컬럼" 인지 판단하므로, 다른 드래그(텍스트·파일)는 무시된다.
|
|
10
|
+
*/
|
|
11
|
+
export declare const GROUP_DRAG_COLUMN_TYPE = "text/koko-column";
|
|
12
|
+
/** 바 안의 칩을 끌 때 함께 싣는 출발 인덱스 — 있으면 "순서 변경" 이다 */
|
|
13
|
+
export declare const GROUP_DRAG_INDEX_TYPE = "text/koko-group-index";
|
|
14
|
+
/** 배치 크기 선택지 — 서버 그룹핑 `useServerGrouping({ batchSize })` */
|
|
15
|
+
export declare const GROUP_BATCH_SIZE_OPTIONS: readonly [20, 50, 100, 150, 200, 250, 500];
|
|
16
|
+
/** 그룹 불가 컬럼을 놓았을 때 문구 */
|
|
17
|
+
export declare const GROUP_REJECTED_MESSAGE = "\uC774 \uC5F4\uC740 \uADF8\uB8F9\uD551\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4";
|
|
18
|
+
export interface GroupingBarProps<T extends RowData> {
|
|
19
|
+
/** 상위 → 하위 순서의 그룹 컬럼 */
|
|
20
|
+
grouping: string[];
|
|
21
|
+
onChange: (grouping: string[]) => void;
|
|
22
|
+
/** 그룹 후보 컬럼 */
|
|
23
|
+
columns: TableColumnDefs<T>;
|
|
24
|
+
/** 그룹핑이 꺼져 있을 때 힌트 문구 */
|
|
25
|
+
placeholder?: string;
|
|
26
|
+
/**
|
|
27
|
+
* 드래그 조작. 기본 true.
|
|
28
|
+
* - 칩을 끌어 순서 변경, 바 밖에 놓으면 해제
|
|
29
|
+
* - `HeaderCell draggableGroup` 헤더를 끌어와 놓으면 추가
|
|
30
|
+
* 키보드(좌우 버튼·x)는 이 값과 무관하게 항상 동작한다.
|
|
31
|
+
*/
|
|
32
|
+
enableDrag?: boolean;
|
|
33
|
+
/** 그룹 컬럼 배치 — `onGroupedColumnModeChange` 와 함께 주면 Remove/Reorder 토글이 생긴다 */
|
|
34
|
+
groupedColumnMode?: GroupedColumnMode;
|
|
35
|
+
onGroupedColumnModeChange?: (mode: GroupedColumnMode) => void;
|
|
36
|
+
/** 서버 그룹핑 배치 크기 — `onBatchSizeChange` 와 함께 주면 선택 상자가 생긴다 */
|
|
37
|
+
batchSize?: number;
|
|
38
|
+
onBatchSizeChange?: (size: number) => void;
|
|
39
|
+
/** 옵션 슬롯 맨 오른쪽에 놓을 것 */
|
|
40
|
+
extra?: ReactNode;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 그룹 기준 바 — 그룹 컬럼을 추가·제거하고 **순서를 바꾼다.**
|
|
44
|
+
*
|
|
45
|
+
* 순서가 곧 계층이다. `["channel", "region"]` 은 채널 아래 지역이고, 뒤집으면
|
|
46
|
+
* 완전히 다른 트리가 된다. 그래서 제거보다 **순서 이동이 더 중요한 조작**이라
|
|
47
|
+
* 칩마다 좌우 이동 버튼을 붙였고, 마우스로는 칩을 끌어 옮길 수 있다.
|
|
48
|
+
*
|
|
49
|
+
* 드래그는 HTML5 DnD 만 쓴다(라이브러리 없음). 세 가지 조작이 붙는다:
|
|
50
|
+
* 1. 칩 → 바 안 다른 틈: 순서 변경
|
|
51
|
+
* 2. 헤더(`HeaderCell draggableGroup`) → 바: 그룹 추가 (`text/koko-column`)
|
|
52
|
+
* 3. 칩 → 바 밖: 해제 (`dragend` 의 `dropEffect === "none"` 판정)
|
|
53
|
+
* 키보드·터치는 좌우 버튼과 x, 추가 메뉴로 같은 일을 할 수 있다.
|
|
54
|
+
*
|
|
55
|
+
* 바 배경이 상태를 말한다 — 드래그 중 놓을 수 있으면 primary.100 + 굵은 하단선, 놓을 수 없으면
|
|
56
|
+
* error.light + 안내 문구, 그룹이 있으면 primary.50, 없으면 grey.50. 오른쪽 옵션 슬롯에는
|
|
57
|
+
* `groupedColumnMode` 토글·`batchSize` 선택·`extra` 가 놓인다.
|
|
58
|
+
*/
|
|
59
|
+
export declare function GroupingBar<T extends RowData>({ grouping, onChange, columns, placeholder, enableDrag, groupedColumnMode, onGroupedColumnModeChange, batchSize, onBatchSizeChange, extra, }: GroupingBarProps<T>): import("react").JSX.Element;
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, useRef, useState } from "react";
|
|
3
|
+
import { Autocomplete, Box, Chip, IconButton, ListItemText, Menu, MenuItem, Stack, TextField, ToggleButton, ToggleButtonGroup, Tooltip, Typography, } from "@mui/material";
|
|
4
|
+
import AddIcon from "@mui/icons-material/Add";
|
|
5
|
+
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
|
6
|
+
import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
|
|
7
|
+
import CloseIcon from "@mui/icons-material/Close";
|
|
8
|
+
import DragIndicatorIcon from "@mui/icons-material/DragIndicator";
|
|
9
|
+
import LayersIcon from "@mui/icons-material/Layers";
|
|
10
|
+
import { columnKey } from "../../../core/src/index";
|
|
11
|
+
/**
|
|
12
|
+
* 헤더 셀·그룹 칩이 드래그할 때 `dataTransfer` 에 싣는 MIME 키.
|
|
13
|
+
*
|
|
14
|
+
* `HeaderCell` 의 `draggableGroup` 이 같은 키로 컬럼 id 를 싣는다 — 바는 이 키만
|
|
15
|
+
* 보고 "그룹으로 추가할 컬럼" 인지 판단하므로, 다른 드래그(텍스트·파일)는 무시된다.
|
|
16
|
+
*/
|
|
17
|
+
export const GROUP_DRAG_COLUMN_TYPE = "text/koko-column";
|
|
18
|
+
/** 바 안의 칩을 끌 때 함께 싣는 출발 인덱스 — 있으면 "순서 변경" 이다 */
|
|
19
|
+
export const GROUP_DRAG_INDEX_TYPE = "text/koko-group-index";
|
|
20
|
+
/** 배치 크기 선택지 — 서버 그룹핑 `useServerGrouping({ batchSize })` */
|
|
21
|
+
export const GROUP_BATCH_SIZE_OPTIONS = [20, 50, 100, 150, 200, 250, 500];
|
|
22
|
+
/** 그룹 불가 컬럼을 놓았을 때 문구 */
|
|
23
|
+
export const GROUP_REJECTED_MESSAGE = "이 열은 그룹핑할 수 없습니다";
|
|
24
|
+
function candidates(columns) {
|
|
25
|
+
const list = [];
|
|
26
|
+
for (const column of columns) {
|
|
27
|
+
const id = columnKey(column);
|
|
28
|
+
if (!id)
|
|
29
|
+
continue;
|
|
30
|
+
if (column.enableGrouping === false)
|
|
31
|
+
continue;
|
|
32
|
+
if (column.meta?.forceHidden)
|
|
33
|
+
continue;
|
|
34
|
+
// 숫자·날짜는 값이 거의 다 달라 그룹이 행 수만큼 생긴다 — 후보에서 뺀다.
|
|
35
|
+
// 굳이 묶고 싶으면 변환을 건 파생 컬럼을 만들어 쓰는 쪽이 맞다.
|
|
36
|
+
const dataType = column.meta?.dataType;
|
|
37
|
+
if (dataType === "number" || dataType === "date" || dataType === "datetime")
|
|
38
|
+
continue;
|
|
39
|
+
const header = column.filterLabel ?? column.header;
|
|
40
|
+
list.push({ id, label: typeof header === "string" ? header : id });
|
|
41
|
+
}
|
|
42
|
+
return list;
|
|
43
|
+
}
|
|
44
|
+
const IDLE = { fromIndex: null, columnId: null, dropIndex: null, rejected: false, over: false };
|
|
45
|
+
/**
|
|
46
|
+
* 드롭 위치 표시 세로선.
|
|
47
|
+
* - `active` — 여기에 놓인다 (primary)
|
|
48
|
+
* - `beside` — 끌고 있는 칩의 바로 옆 틈 (놓아도 이동이 아님) — 점선
|
|
49
|
+
* - 그 외 드래그 중이면 옅은 선, 아니면 투명
|
|
50
|
+
*/
|
|
51
|
+
function DropLine({ active, beside, dragging }) {
|
|
52
|
+
return (_jsx(Box, { "aria-hidden": true, "data-testid": "group-drop-line", "data-active": active || undefined, sx: {
|
|
53
|
+
width: 3, height: 28, borderRadius: 1, flexShrink: 0, mx: 0.25, boxSizing: "border-box",
|
|
54
|
+
bgcolor: active ? "primary.main" : beside || !dragging ? "transparent" : "divider",
|
|
55
|
+
borderLeft: beside ? "2px dashed" : undefined,
|
|
56
|
+
borderColor: "divider",
|
|
57
|
+
transition: "background-color 0.15s, border-color 0.15s",
|
|
58
|
+
} }));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 그룹 기준 바 — 그룹 컬럼을 추가·제거하고 **순서를 바꾼다.**
|
|
62
|
+
*
|
|
63
|
+
* 순서가 곧 계층이다. `["channel", "region"]` 은 채널 아래 지역이고, 뒤집으면
|
|
64
|
+
* 완전히 다른 트리가 된다. 그래서 제거보다 **순서 이동이 더 중요한 조작**이라
|
|
65
|
+
* 칩마다 좌우 이동 버튼을 붙였고, 마우스로는 칩을 끌어 옮길 수 있다.
|
|
66
|
+
*
|
|
67
|
+
* 드래그는 HTML5 DnD 만 쓴다(라이브러리 없음). 세 가지 조작이 붙는다:
|
|
68
|
+
* 1. 칩 → 바 안 다른 틈: 순서 변경
|
|
69
|
+
* 2. 헤더(`HeaderCell draggableGroup`) → 바: 그룹 추가 (`text/koko-column`)
|
|
70
|
+
* 3. 칩 → 바 밖: 해제 (`dragend` 의 `dropEffect === "none"` 판정)
|
|
71
|
+
* 키보드·터치는 좌우 버튼과 x, 추가 메뉴로 같은 일을 할 수 있다.
|
|
72
|
+
*
|
|
73
|
+
* 바 배경이 상태를 말한다 — 드래그 중 놓을 수 있으면 primary.100 + 굵은 하단선, 놓을 수 없으면
|
|
74
|
+
* error.light + 안내 문구, 그룹이 있으면 primary.50, 없으면 grey.50. 오른쪽 옵션 슬롯에는
|
|
75
|
+
* `groupedColumnMode` 토글·`batchSize` 선택·`extra` 가 놓인다.
|
|
76
|
+
*/
|
|
77
|
+
export function GroupingBar({ grouping, onChange, columns, placeholder = "열을 드래그하여 그룹핑", enableDrag = true, groupedColumnMode, onGroupedColumnModeChange, batchSize, onBatchSizeChange, extra, }) {
|
|
78
|
+
const [anchorEl, setAnchorEl] = useState(null);
|
|
79
|
+
const [drag, setDrag] = useState(IDLE);
|
|
80
|
+
// 바 안에서 drop 이 실제로 처리됐는지 — dragend 에서 "밖에 놓았다" 를 판정할 때 본다
|
|
81
|
+
const droppedInsideRef = useRef(false);
|
|
82
|
+
const chipRefs = useRef(new Map());
|
|
83
|
+
const barRef = useRef(null);
|
|
84
|
+
const all = candidates(columns);
|
|
85
|
+
const labelOf = (id) => all.find((c) => c.id === id)?.label ?? id;
|
|
86
|
+
const remaining = all.filter((c) => !grouping.includes(c.id));
|
|
87
|
+
const isCandidate = (id) => all.some((c) => c.id === id);
|
|
88
|
+
const isGrouped = grouping.length > 0;
|
|
89
|
+
const move = (index, delta) => {
|
|
90
|
+
const target = index + delta;
|
|
91
|
+
if (target < 0 || target >= grouping.length)
|
|
92
|
+
return;
|
|
93
|
+
onChange(reorder(grouping, index, target));
|
|
94
|
+
};
|
|
95
|
+
// ── 드래그 ──
|
|
96
|
+
/** 커서 x → 칩 중앙 기준으로 놓일 틈 인덱스 */
|
|
97
|
+
const dropIndexFromX = (clientX) => {
|
|
98
|
+
for (let i = 0; i < grouping.length; i += 1) {
|
|
99
|
+
const el = chipRefs.current.get(i);
|
|
100
|
+
if (!el)
|
|
101
|
+
continue;
|
|
102
|
+
const rect = el.getBoundingClientRect();
|
|
103
|
+
if (clientX < rect.left + rect.width / 2)
|
|
104
|
+
return i;
|
|
105
|
+
}
|
|
106
|
+
return grouping.length;
|
|
107
|
+
};
|
|
108
|
+
const handleChipDragStart = (event, index) => {
|
|
109
|
+
const id = grouping[index];
|
|
110
|
+
event.dataTransfer.effectAllowed = "move";
|
|
111
|
+
event.dataTransfer.setData(GROUP_DRAG_INDEX_TYPE, String(index));
|
|
112
|
+
event.dataTransfer.setData(GROUP_DRAG_COLUMN_TYPE, id);
|
|
113
|
+
droppedInsideRef.current = false;
|
|
114
|
+
setDrag({ fromIndex: index, columnId: id, dropIndex: null, rejected: false, over: true });
|
|
115
|
+
};
|
|
116
|
+
const handleChipDragEnd = (event, index) => {
|
|
117
|
+
// 바 밖(유효한 드롭 대상이 없는 곳)에 놓으면 브라우저가 dropEffect 를 "none" 으로 준다.
|
|
118
|
+
// 그 경우 그룹에서 뺀다 — 칩을 "끌어내는" 동작이 곧 해제다.
|
|
119
|
+
// 단, Escape 취소도 dropEffect 가 "none" 이므로 좌표로 구분한다: 취소 시 브라우저는
|
|
120
|
+
// (0, 0) 을 주고, 바 rect 안에서 끝난 것도 "밖에 놓은" 게 아니다.
|
|
121
|
+
const cancelled = event.clientX === 0 && event.clientY === 0;
|
|
122
|
+
const barRect = barRef.current?.getBoundingClientRect();
|
|
123
|
+
const insideBar = !!barRect
|
|
124
|
+
&& event.clientX >= barRect.left && event.clientX <= barRect.right
|
|
125
|
+
&& event.clientY >= barRect.top && event.clientY <= barRect.bottom;
|
|
126
|
+
const outside = !droppedInsideRef.current
|
|
127
|
+
&& event.dataTransfer.dropEffect === "none"
|
|
128
|
+
&& !cancelled && !insideBar;
|
|
129
|
+
if (outside) {
|
|
130
|
+
const id = grouping[index];
|
|
131
|
+
if (id)
|
|
132
|
+
onChange(grouping.filter((key) => key !== id));
|
|
133
|
+
}
|
|
134
|
+
droppedInsideRef.current = false;
|
|
135
|
+
setDrag(IDLE);
|
|
136
|
+
};
|
|
137
|
+
const handleBarDragOver = (event) => {
|
|
138
|
+
if (!enableDrag)
|
|
139
|
+
return;
|
|
140
|
+
const types = Array.from(event.dataTransfer.types ?? []);
|
|
141
|
+
const isChip = types.includes(GROUP_DRAG_INDEX_TYPE);
|
|
142
|
+
const isColumn = types.includes(GROUP_DRAG_COLUMN_TYPE);
|
|
143
|
+
if (!isChip && !isColumn)
|
|
144
|
+
return; // 텍스트·파일 등 무관한 드래그는 기본 동작
|
|
145
|
+
event.preventDefault();
|
|
146
|
+
event.dataTransfer.dropEffect = isChip ? "move" : "copy";
|
|
147
|
+
const dropIndex = dropIndexFromX(event.clientX);
|
|
148
|
+
setDrag((prev) => {
|
|
149
|
+
const fromIndex = prev.fromIndex;
|
|
150
|
+
// 자기 자리(또는 바로 옆 틈)에 놓는 건 이동이 아니다 — 표시하지 않는다
|
|
151
|
+
const noop = fromIndex !== null && (dropIndex === fromIndex || dropIndex === fromIndex + 1);
|
|
152
|
+
const next = {
|
|
153
|
+
fromIndex, columnId: prev.columnId,
|
|
154
|
+
dropIndex: noop ? null : dropIndex,
|
|
155
|
+
rejected: false,
|
|
156
|
+
over: true,
|
|
157
|
+
};
|
|
158
|
+
return prev.dropIndex === next.dropIndex && prev.rejected === next.rejected
|
|
159
|
+
&& prev.fromIndex === next.fromIndex && prev.over
|
|
160
|
+
? prev
|
|
161
|
+
: next;
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
const handleBarDragLeave = (event) => {
|
|
165
|
+
const related = event.relatedTarget;
|
|
166
|
+
if (related && event.currentTarget.contains(related))
|
|
167
|
+
return;
|
|
168
|
+
setDrag((prev) => {
|
|
169
|
+
// 칩을 끌고 바 밖으로 나가는 중이면 fromIndex 는 남긴다 (dragend 가 정리)
|
|
170
|
+
if (prev.dropIndex === null && !prev.rejected && !prev.over)
|
|
171
|
+
return prev;
|
|
172
|
+
return { ...prev, dropIndex: null, rejected: false, over: prev.fromIndex !== null };
|
|
173
|
+
});
|
|
174
|
+
};
|
|
175
|
+
const handleBarDrop = (event) => {
|
|
176
|
+
if (!enableDrag)
|
|
177
|
+
return;
|
|
178
|
+
const rawIndex = event.dataTransfer.getData(GROUP_DRAG_INDEX_TYPE);
|
|
179
|
+
const columnId = event.dataTransfer.getData(GROUP_DRAG_COLUMN_TYPE);
|
|
180
|
+
if (!rawIndex && !columnId)
|
|
181
|
+
return;
|
|
182
|
+
event.preventDefault();
|
|
183
|
+
droppedInsideRef.current = true;
|
|
184
|
+
const dropIndex = dropIndexFromX(event.clientX);
|
|
185
|
+
if (rawIndex !== "") {
|
|
186
|
+
// 칩 순서 변경
|
|
187
|
+
const from = Number(rawIndex);
|
|
188
|
+
if (Number.isInteger(from) && from >= 0 && from < grouping.length) {
|
|
189
|
+
const insertAt = dropIndex > from ? dropIndex - 1 : dropIndex;
|
|
190
|
+
if (insertAt !== from)
|
|
191
|
+
onChange(reorder(grouping, from, insertAt));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
else if (columnId) {
|
|
195
|
+
// 헤더에서 끌어온 컬럼 — 후보여야 하고, 이미 있으면 그 자리로 옮긴다
|
|
196
|
+
if (!isCandidate(columnId)) {
|
|
197
|
+
setDrag({ ...IDLE, rejected: true });
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const existing = grouping.indexOf(columnId);
|
|
201
|
+
if (existing >= 0) {
|
|
202
|
+
const insertAt = dropIndex > existing ? dropIndex - 1 : dropIndex;
|
|
203
|
+
if (insertAt !== existing)
|
|
204
|
+
onChange(reorder(grouping, existing, insertAt));
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
const next = [...grouping];
|
|
208
|
+
next.splice(dropIndex, 0, columnId);
|
|
209
|
+
onChange(next);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
setDrag(IDLE);
|
|
213
|
+
};
|
|
214
|
+
const dragging = drag.fromIndex !== null || drag.over;
|
|
215
|
+
const rejected = drag.rejected;
|
|
216
|
+
const reordering = drag.fromIndex !== null;
|
|
217
|
+
const beside = (gap) => reordering && (drag.fromIndex === gap || drag.fromIndex === gap - 1);
|
|
218
|
+
const hint = rejected
|
|
219
|
+
? GROUP_REJECTED_MESSAGE
|
|
220
|
+
: dragging
|
|
221
|
+
? (reordering ? "놓아서 순서 변경 · 바 밖에 놓으면 해제" : "여기에 놓아 그룹 추가")
|
|
222
|
+
: !isGrouped ? placeholder : "";
|
|
223
|
+
const bgcolor = rejected ? "error.light" : dragging ? "primary.100" : isGrouped ? "primary.50" : "grey.50";
|
|
224
|
+
const borderColor = rejected ? "error.main" : dragging ? "primary.main" : "divider";
|
|
225
|
+
return (_jsxs(Stack, { direction: "row", alignItems: "center", role: "group", "aria-label": "\uADF8\uB8F9 \uAE30\uC900", "data-testid": "grouping-bar", "data-drag-state": rejected ? "rejected" : dragging ? "dragging" : isGrouped ? "grouped" : "empty", ref: barRef, onDragOver: handleBarDragOver, onDragLeave: handleBarDragLeave, onDrop: handleBarDrop, sx: {
|
|
226
|
+
flexWrap: "wrap", gap: 0.5, minHeight: "3rem", px: 1, borderRadius: 1,
|
|
227
|
+
bgcolor,
|
|
228
|
+
borderBottom: dragging || rejected ? 3 : 1,
|
|
229
|
+
borderColor,
|
|
230
|
+
cursor: rejected ? "not-allowed" : undefined,
|
|
231
|
+
transition: "background-color 0.2s, border-color 0.2s",
|
|
232
|
+
}, children: [_jsx(LayersIcon, { fontSize: "small", sx: { color: isGrouped ? "primary.main" : "text.disabled", flexShrink: 0 } }), grouping.map((id, index) => (_jsxs(Fragment, { children: [_jsx(DropLine, { active: drag.dropIndex === index, beside: beside(index), dragging: dragging }), _jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0, children: [_jsx(Chip, { ref: (el) => {
|
|
233
|
+
if (el)
|
|
234
|
+
chipRefs.current.set(index, el);
|
|
235
|
+
else
|
|
236
|
+
chipRefs.current.delete(index);
|
|
237
|
+
}, size: "small", color: "primary", variant: "outlined", icon: enableDrag ? _jsx(DragIndicatorIcon, { sx: { fontSize: 16, color: "grey.500", cursor: "grab" } }) : undefined, label: `${index + 1}. ${labelOf(id)}`, onDelete: () => onChange(grouping.filter((key) => key !== id)), deleteIcon: _jsx(CloseIcon, { sx: { fontSize: 16 } }), draggable: enableDrag, "data-group-index": index, onDragStart: enableDrag ? (event) => handleChipDragStart(event, index) : undefined, onDragEnd: enableDrag ? (event) => handleChipDragEnd(event, index) : undefined, sx: {
|
|
238
|
+
height: 32, bgcolor: "background.paper", flexShrink: 0,
|
|
239
|
+
opacity: drag.fromIndex === index ? 0.5 : 1,
|
|
240
|
+
"& .MuiChip-icon": { ml: 0.5, mr: -0.5 },
|
|
241
|
+
...(enableDrag ? { cursor: "grab", "&:active": { cursor: "grabbing" } } : {}),
|
|
242
|
+
} }), _jsx(Tooltip, { title: "\uC704\uB85C (\uC0C1\uC704 \uACC4\uCE35)", children: _jsx("span", { children: _jsx(IconButton, { size: "small", "aria-label": `${labelOf(id)} 상위로`, disabled: index === 0, onClick: () => move(index, -1), children: _jsx(ArrowBackIcon, { sx: { fontSize: 14 } }) }) }) }), _jsx(Tooltip, { title: "\uC544\uB798\uB85C (\uD558\uC704 \uACC4\uCE35)", children: _jsx("span", { children: _jsx(IconButton, { size: "small", "aria-label": `${labelOf(id)} 하위로`, disabled: index === grouping.length - 1, onClick: () => move(index, 1), children: _jsx(ArrowForwardIcon, { sx: { fontSize: 14 } }) }) }) })] })] }, id))), _jsx(DropLine, { active: drag.dropIndex === grouping.length, beside: beside(grouping.length), dragging: dragging }), _jsx(Tooltip, { title: remaining.length === 0 ? "추가할 컬럼이 없습니다" : "그룹 기준 추가", children: _jsx("span", { children: _jsx(IconButton, { size: "small", "aria-label": "\uADF8\uB8F9 \uAE30\uC900 \uCD94\uAC00", disabled: remaining.length === 0, onClick: (event) => setAnchorEl(event.currentTarget), children: _jsx(AddIcon, { fontSize: "small" }) }) }) }), isGrouped && (_jsx(Chip, { size: "small", variant: "outlined", label: "\uD574\uC81C", onClick: () => onChange([]), sx: { bgcolor: "background.paper" } })), _jsx(Box, { sx: { flex: 1, display: "flex", alignItems: "center", minHeight: 32, pl: 0.5, minWidth: 0 }, children: hint && (_jsx(Typography, { variant: "overline", color: rejected ? "error" : dragging ? "primary" : "text.secondary", sx: { userSelect: "none", lineHeight: 1.4, fontWeight: rejected ? 600 : undefined, whiteSpace: "nowrap" }, children: hint })) }), isGrouped && onGroupedColumnModeChange && (_jsxs(ToggleButtonGroup, { exclusive: true, size: "small", "aria-label": "\uADF8\uB8F9 \uCEEC\uB7FC \uBC30\uCE58", value: groupedColumnMode === false ? "false" : groupedColumnMode ?? "reorder", onChange: (_, next) => {
|
|
243
|
+
if (next === null)
|
|
244
|
+
return;
|
|
245
|
+
onGroupedColumnModeChange(next === "false" ? false : next);
|
|
246
|
+
}, sx: { flexShrink: 0, bgcolor: "background.paper", "& .MuiToggleButton-root": { py: 0.25, px: 1, fontSize: 11 } }, children: [_jsx(ToggleButton, { value: "remove", "aria-label": "\uADF8\uB8F9 \uCEEC\uB7FC \uC81C\uAC70", children: "Remove" }), _jsx(ToggleButton, { value: "reorder", "aria-label": "\uADF8\uB8F9 \uCEEC\uB7FC \uC55E\uC73C\uB85C", children: "Reorder" })] })), isGrouped && onBatchSizeChange && (_jsx(Autocomplete, { size: "small", freeSolo: true, disableClearable: true, options: GROUP_BATCH_SIZE_OPTIONS.map(String), value: batchSize !== undefined ? String(batchSize) : "", onChange: (_, next) => {
|
|
247
|
+
const num = parseInt(String(next), 10);
|
|
248
|
+
if (num > 0)
|
|
249
|
+
onBatchSizeChange(num);
|
|
250
|
+
}, onBlur: (event) => {
|
|
251
|
+
const num = parseInt(event.target.value, 10);
|
|
252
|
+
if (num > 0 && num !== batchSize)
|
|
253
|
+
onBatchSizeChange(num);
|
|
254
|
+
}, sx: { width: 96, flexShrink: 0 }, renderInput: (params) => (_jsx(TextField, { ...params, variant: "standard", label: "\uBC30\uCE58", slotProps: {
|
|
255
|
+
htmlInput: { ...params.inputProps, "aria-label": "배치 크기", style: { textAlign: "right", padding: "2px 0" } },
|
|
256
|
+
} })) })), extra, _jsx(Menu, { anchorEl: anchorEl, open: !!anchorEl, onClose: () => setAnchorEl(null), children: remaining.map((candidate) => (_jsx(MenuItem, { onClick: () => {
|
|
257
|
+
onChange([...grouping, candidate.id]);
|
|
258
|
+
setAnchorEl(null);
|
|
259
|
+
}, children: _jsx(ListItemText, { primary: candidate.label }) }, candidate.id))) })] }));
|
|
260
|
+
}
|
|
261
|
+
/** `from` 의 항목을 빼서 `to` 위치에 넣은 새 배열 */
|
|
262
|
+
function reorder(list, from, to) {
|
|
263
|
+
const next = [...list];
|
|
264
|
+
const [moved] = next.splice(from, 1);
|
|
265
|
+
next.splice(to, 0, moved);
|
|
266
|
+
return next;
|
|
267
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ExpandState } from "../../../core/src/index";
|
|
2
|
+
export interface StepExpandButtonProps {
|
|
3
|
+
expand: ExpandState;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* 단계 펼치기 — 한 번에 한 계층씩 여닫는다.
|
|
7
|
+
*
|
|
8
|
+
* 3단 그룹에서 "전부 펼치기"는 말단 행까지 쏟아져 오히려 안 보인다.
|
|
9
|
+
* 계층을 하나씩 여는 쪽이 실제로 쓰는 조작이라 기본 버튼으로 둔다
|
|
10
|
+
* (`expand.depth` 가 현재 열린 깊이, `maxDepth` 가 한계).
|
|
11
|
+
*/
|
|
12
|
+
export declare function StepExpandButton({ expand }: StepExpandButtonProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, ButtonGroup, Tooltip } from "@mui/material";
|
|
3
|
+
import UnfoldLessIcon from "@mui/icons-material/UnfoldLess";
|
|
4
|
+
import UnfoldMoreIcon from "@mui/icons-material/UnfoldMore";
|
|
5
|
+
/**
|
|
6
|
+
* 단계 펼치기 — 한 번에 한 계층씩 여닫는다.
|
|
7
|
+
*
|
|
8
|
+
* 3단 그룹에서 "전부 펼치기"는 말단 행까지 쏟아져 오히려 안 보인다.
|
|
9
|
+
* 계층을 하나씩 여는 쪽이 실제로 쓰는 조작이라 기본 버튼으로 둔다
|
|
10
|
+
* (`expand.depth` 가 현재 열린 깊이, `maxDepth` 가 한계).
|
|
11
|
+
*/
|
|
12
|
+
export function StepExpandButton({ expand }) {
|
|
13
|
+
if (!expand.canExpand)
|
|
14
|
+
return null;
|
|
15
|
+
const canMore = expand.depth < expand.maxDepth;
|
|
16
|
+
const canLess = expand.depth > 0;
|
|
17
|
+
return (_jsxs(ButtonGroup, { size: "small", variant: "outlined", children: [_jsx(Tooltip, { title: canLess ? "한 단계 접기" : "이미 다 접혀 있습니다", children: _jsx("span", { children: _jsx(Button, { "aria-label": "\uD55C \uB2E8\uACC4 \uC811\uAE30", disabled: !canLess, onClick: () => expand.expandToDepth(expand.depth - 1), startIcon: _jsx(UnfoldLessIcon, {}), children: "\uC811\uAE30" }) }) }), _jsx(Tooltip, { title: canMore ? "한 단계 펼치기" : "이미 다 펼쳐져 있습니다", children: _jsx("span", { children: _jsx(Button, { "aria-label": "\uD55C \uB2E8\uACC4 \uD3BC\uCE58\uAE30", disabled: !canMore, onClick: () => expand.expandToDepth(expand.depth + 1), startIcon: _jsx(UnfoldMoreIcon, {}), children: "\uD3BC\uCE58\uAE30" }) }) }), _jsxs(Button, { "aria-label": "\uB2E8\uACC4 \uD45C\uC2DC", disabled: true, sx: { minWidth: 52 }, children: [expand.depth, "/", expand.maxDepth] })] }));
|
|
18
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from "react";
|
|
2
|
+
import { type Column, type RowData } from "@tanstack/react-table";
|
|
3
|
+
import type { AggregateResult, AggregationDef, AggregationId, GroupTreeNode, Registry } from "../../../core/src/index";
|
|
4
|
+
import { type AggregateChipProps } from "./AggregateChip";
|
|
5
|
+
/** depth 1단계당 들여쓰기 (MUI spacing 단위). 그룹값 셀과 집계 셀에 같이 적용한다 */
|
|
6
|
+
export declare const GROUP_DEPTH_INDENT = 2;
|
|
7
|
+
/** depth 별 그룹 행 배경 — 깊어질수록 한 단계씩 어둡다 */
|
|
8
|
+
export declare const GROUP_ROW_BG_SHADES: readonly ["grey.50", "grey.100", "grey.200", "grey.300"];
|
|
9
|
+
export declare function groupRowBgColor(depth: number): string;
|
|
10
|
+
/** 그룹 행 hover 색 — 한 단계 더 어두운 색 */
|
|
11
|
+
export declare function groupRowHoverColor(depth: number): string;
|
|
12
|
+
/** `meta.aggRender` 가 받는 값 — ERP `AggValues` 와 같은 모양 */
|
|
13
|
+
export interface AggRenderProps {
|
|
14
|
+
/** 선택된(또는 첫) 함수의 값 */
|
|
15
|
+
value: unknown;
|
|
16
|
+
columnId: string;
|
|
17
|
+
/** 이 그룹 노드의 집계 전체 (`node.aggregates`) */
|
|
18
|
+
allAggregates: AggregateResult;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 합성 집계 렌더러. 코어의 `ColumnMeta.aggRender` 선언은 `(aggregates) => unknown` 으로 느슨하지만
|
|
22
|
+
* 렌더러는 ERP 와 같은 `(values, column)` 규약으로 부른다 — `allAggregates` 로 전체를 볼 수 있다.
|
|
23
|
+
*/
|
|
24
|
+
export type AggRender<T extends RowData> = (values: AggRenderProps, column: Column<T, unknown>) => ReactNode;
|
|
25
|
+
/** 카테고리형(enum/select/text) — 고유 개수 외의 집계는 뜻이 없다 */
|
|
26
|
+
export declare function isCategoricalColumn<T extends RowData>(column: Column<T, unknown>): boolean;
|
|
27
|
+
/** 그룹 컬럼의 표시 이름 — 헤더가 문자열이면 그것, 아니면 id */
|
|
28
|
+
export declare function columnLabelOf<T extends RowData>(column: Column<T, unknown>): string;
|
|
29
|
+
/**
|
|
30
|
+
* 그룹값 라벨 — `node.label`(코어가 `selectOptions` 로 해석) → 컬럼 `cell` 렌더러 → 문자열.
|
|
31
|
+
*
|
|
32
|
+
* `cell` 렌더러는 TanStack 셀 컨텍스트를 기대하므로 최소한의 모조 컨텍스트로 부른다 —
|
|
33
|
+
* `row.original` 에는 현재 값과 조상 그룹 값을 넣어 상위 필드를 참조하는 렌더러도 동작한다.
|
|
34
|
+
* 렌더러가 던지면 문자열로 물러선다.
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveGroupValue<T extends RowData>(node: GroupTreeNode<T>, column: Column<T, unknown> | undefined): ReactNode;
|
|
37
|
+
export interface AggregateCellContentOptions<T extends RowData> {
|
|
38
|
+
column: Column<T, unknown>;
|
|
39
|
+
node: GroupTreeNode<T>;
|
|
40
|
+
/** 컬럼별 선택된 함수 */
|
|
41
|
+
selection: Record<string, AggregationId>;
|
|
42
|
+
/** 없으면 칩은 읽기 전용 (메서드 시그니처 — `(id, fn: AggFnKey) => void` 처럼 좁은 콜백도 받는다) */
|
|
43
|
+
onSelect?(columnId: string, fn: AggregationId): void;
|
|
44
|
+
/** 칩 컴포넌트 교체 — `KokoTableProvider components.AggregateChip` */
|
|
45
|
+
chip?: ComponentType<AggregateChipProps>;
|
|
46
|
+
/**
|
|
47
|
+
* 집계 레지스트리 — boolean/카테고리형 컬럼에서 뜻 있는 함수를 고를 때 본다.
|
|
48
|
+
* 미지정 시 전역 `aggregations`, 칩(`AggregateChip`)은 Provider 것(`useAggregations()`)을 본다.
|
|
49
|
+
*/
|
|
50
|
+
registry?: Registry<AggregationDef>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 집계 셀 내용. 그릴 것이 없으면 `null`.
|
|
54
|
+
*
|
|
55
|
+
* 우선순위: `meta.aggRender` → 카테고리형은 `count_distinct` + 사용자 집계(`applies` 참)만, boolean 은 `applies("boolean")` 인 것만
|
|
56
|
+
* (내장: `count_true`·`count_distinct`·`ratio`) → `AggregateChip`
|
|
57
|
+
* (선택된 함수 = `selection[columnId]`, 없으면 첫 값. 함수가 둘 이상이면 칩을 눌러 바꾼다).
|
|
58
|
+
* 대상은 `meta.enableAgg` / `meta.aggRender` 가 있거나 `node.aggregates[columnId]` 가 있는 컬럼뿐이다.
|
|
59
|
+
*/
|
|
60
|
+
export declare function renderAggregateCellContent<T extends RowData>({ column, node, selection, onSelect, chip, registry: givenRegistry, }: AggregateCellContentOptions<T>): ReactNode;
|
|
61
|
+
/** 그룹값 텍스트 — 굵게 */
|
|
62
|
+
export declare function GroupValueText({ children }: {
|
|
63
|
+
children: ReactNode;
|
|
64
|
+
}): import("react").JSX.Element;
|
|
65
|
+
/** 집계 셀 내용을 가운데 놓는다 (ERP `aggCellContentSx`) */
|
|
66
|
+
export declare function AggregateCellBox({ children }: {
|
|
67
|
+
children: ReactNode;
|
|
68
|
+
}): import("react").JSX.Element;
|