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.
Files changed (308) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +320 -0
  3. package/contract/table-request.schema.json +424 -0
  4. package/dist/core/src/adapters/sources.d.ts +59 -0
  5. package/dist/core/src/adapters/sources.js +235 -0
  6. package/dist/core/src/client/aggregate.d.ts +94 -0
  7. package/dist/core/src/client/aggregate.js +222 -0
  8. package/dist/core/src/client/groupRows.d.ts +40 -0
  9. package/dist/core/src/client/groupRows.js +145 -0
  10. package/dist/core/src/client/runClientQuery.d.ts +67 -0
  11. package/dist/core/src/client/runClientQuery.js +157 -0
  12. package/dist/core/src/columns/ColumnBuilder.d.ts +121 -0
  13. package/dist/core/src/columns/ColumnBuilder.js +273 -0
  14. package/dist/core/src/columns/cascade.d.ts +24 -0
  15. package/dist/core/src/columns/cascade.js +93 -0
  16. package/dist/core/src/columns/deriveOperators.d.ts +38 -0
  17. package/dist/core/src/columns/deriveOperators.js +94 -0
  18. package/dist/core/src/columns/expandSorting.d.ts +19 -0
  19. package/dist/core/src/columns/expandSorting.js +52 -0
  20. package/dist/core/src/columns/helper.d.ts +196 -0
  21. package/dist/core/src/columns/helper.js +250 -0
  22. package/dist/core/src/columns/labels.d.ts +12 -0
  23. package/dist/core/src/columns/labels.js +21 -0
  24. package/dist/core/src/contract.d.ts +12 -0
  25. package/dist/core/src/contract.js +12 -0
  26. package/dist/core/src/contracts/aiColumnMeta.d.ts +102 -0
  27. package/dist/core/src/contracts/aiColumnMeta.js +197 -0
  28. package/dist/core/src/contracts/describeColumns.d.ts +48 -0
  29. package/dist/core/src/contracts/describeColumns.js +40 -0
  30. package/dist/core/src/contracts/exportData.d.ts +150 -0
  31. package/dist/core/src/contracts/exportData.js +228 -0
  32. package/dist/core/src/contracts/index.d.ts +6 -0
  33. package/dist/core/src/contracts/index.js +12 -0
  34. package/dist/core/src/contracts/legacyUrl.d.ts +47 -0
  35. package/dist/core/src/contracts/legacyUrl.js +156 -0
  36. package/dist/core/src/contracts/preset.d.ts +104 -0
  37. package/dist/core/src/contracts/preset.js +141 -0
  38. package/dist/core/src/contracts/presetStore.d.ts +81 -0
  39. package/dist/core/src/contracts/presetStore.js +146 -0
  40. package/dist/core/src/contracts/usePresets.d.ts +51 -0
  41. package/dist/core/src/contracts/usePresets.js +170 -0
  42. package/dist/core/src/defaults.d.ts +7 -0
  43. package/dist/core/src/defaults.js +9 -0
  44. package/dist/core/src/filters/codec.d.ts +20 -0
  45. package/dist/core/src/filters/codec.js +61 -0
  46. package/dist/core/src/filters/date.d.ts +30 -0
  47. package/dist/core/src/filters/date.js +179 -0
  48. package/dist/core/src/filters/describe.d.ts +45 -0
  49. package/dist/core/src/filters/describe.js +189 -0
  50. package/dist/core/src/filters/kind.d.ts +52 -0
  51. package/dist/core/src/filters/kind.js +110 -0
  52. package/dist/core/src/filters/kinds/_list.d.ts +13 -0
  53. package/dist/core/src/filters/kinds/_list.js +42 -0
  54. package/dist/core/src/filters/kinds/boolean.d.ts +3 -0
  55. package/dist/core/src/filters/kinds/boolean.js +21 -0
  56. package/dist/core/src/filters/kinds/cascade.d.ts +6 -0
  57. package/dist/core/src/filters/kinds/cascade.js +16 -0
  58. package/dist/core/src/filters/kinds/commonCode.d.ts +3 -0
  59. package/dist/core/src/filters/kinds/commonCode.js +13 -0
  60. package/dist/core/src/filters/kinds/date.d.ts +9 -0
  61. package/dist/core/src/filters/kinds/date.js +32 -0
  62. package/dist/core/src/filters/kinds/enum.d.ts +4 -0
  63. package/dist/core/src/filters/kinds/enum.js +13 -0
  64. package/dist/core/src/filters/kinds/index.d.ts +49 -0
  65. package/dist/core/src/filters/kinds/index.js +39 -0
  66. package/dist/core/src/filters/kinds/linear.d.ts +5 -0
  67. package/dist/core/src/filters/kinds/linear.js +16 -0
  68. package/dist/core/src/filters/kinds/number.d.ts +5 -0
  69. package/dist/core/src/filters/kinds/number.js +19 -0
  70. package/dist/core/src/filters/kinds/period.d.ts +4 -0
  71. package/dist/core/src/filters/kinds/period.js +17 -0
  72. package/dist/core/src/filters/kinds/relation.d.ts +5 -0
  73. package/dist/core/src/filters/kinds/relation.js +17 -0
  74. package/dist/core/src/filters/kinds/schedule.d.ts +5 -0
  75. package/dist/core/src/filters/kinds/schedule.js +16 -0
  76. package/dist/core/src/filters/kinds/text.d.ts +4 -0
  77. package/dist/core/src/filters/kinds/text.js +18 -0
  78. package/dist/core/src/filters/linear.d.ts +33 -0
  79. package/dist/core/src/filters/linear.js +65 -0
  80. package/dist/core/src/filters/list.d.ts +32 -0
  81. package/dist/core/src/filters/list.js +87 -0
  82. package/dist/core/src/filters/number.d.ts +17 -0
  83. package/dist/core/src/filters/number.js +82 -0
  84. package/dist/core/src/filters/registry.d.ts +23 -0
  85. package/dist/core/src/filters/registry.js +121 -0
  86. package/dist/core/src/filters/relation.d.ts +26 -0
  87. package/dist/core/src/filters/relation.js +75 -0
  88. package/dist/core/src/filters/schedule.d.ts +21 -0
  89. package/dist/core/src/filters/schedule.js +60 -0
  90. package/dist/core/src/filters/text.d.ts +44 -0
  91. package/dist/core/src/filters/text.js +127 -0
  92. package/dist/core/src/hooks/index.d.ts +6 -0
  93. package/dist/core/src/hooks/index.js +6 -0
  94. package/dist/core/src/hooks/useDataTable.d.ts +13 -0
  95. package/dist/core/src/hooks/useDataTable.js +109 -0
  96. package/dist/core/src/hooks/useGrouping.d.ts +33 -0
  97. package/dist/core/src/hooks/useGrouping.js +57 -0
  98. package/dist/core/src/hooks/useSearchHistory.d.ts +20 -0
  99. package/dist/core/src/hooks/useSearchHistory.js +52 -0
  100. package/dist/core/src/hooks/useTableData.d.ts +25 -0
  101. package/dist/core/src/hooks/useTableData.js +49 -0
  102. package/dist/core/src/hooks/useTableFromDefinition.d.ts +47 -0
  103. package/dist/core/src/hooks/useTableFromDefinition.js +174 -0
  104. package/dist/core/src/hooks/useTableSearch.d.ts +10 -0
  105. package/dist/core/src/hooks/useTableSearch.js +434 -0
  106. package/dist/core/src/hooks/useUrlSync.d.ts +64 -0
  107. package/dist/core/src/hooks/useUrlSync.js +145 -0
  108. package/dist/core/src/index.d.ts +38 -0
  109. package/dist/core/src/index.js +43 -0
  110. package/dist/core/src/pivot/runClientPivot.d.ts +101 -0
  111. package/dist/core/src/pivot/runClientPivot.js +230 -0
  112. package/dist/core/src/pivot/transforms.d.ts +19 -0
  113. package/dist/core/src/pivot/transforms.js +105 -0
  114. package/dist/core/src/registry.d.ts +46 -0
  115. package/dist/core/src/registry.js +67 -0
  116. package/dist/core/src/types/column.d.ts +213 -0
  117. package/dist/core/src/types/column.js +1 -0
  118. package/dist/core/src/types/data.d.ts +63 -0
  119. package/dist/core/src/types/data.js +1 -0
  120. package/dist/core/src/types/definition.d.ts +239 -0
  121. package/dist/core/src/types/definition.js +59 -0
  122. package/dist/core/src/types/filter.d.ts +94 -0
  123. package/dist/core/src/types/filter.js +1 -0
  124. package/dist/core/src/types/filterKind.d.ts +204 -0
  125. package/dist/core/src/types/filterKind.js +1 -0
  126. package/dist/core/src/types/grouping.d.ts +80 -0
  127. package/dist/core/src/types/grouping.js +4 -0
  128. package/dist/core/src/types/index.d.ts +8 -0
  129. package/dist/core/src/types/index.js +8 -0
  130. package/dist/core/src/types/operators.d.ts +64 -0
  131. package/dist/core/src/types/operators.js +108 -0
  132. package/dist/core/src/types/request.d.ts +114 -0
  133. package/dist/core/src/types/request.js +1 -0
  134. package/dist/core/src/types/search.d.ts +112 -0
  135. package/dist/core/src/types/search.js +1 -0
  136. package/dist/core/src/types/sources.d.ts +84 -0
  137. package/dist/core/src/types/sources.js +1 -0
  138. package/dist/core/src/types/table.d.ts +40 -0
  139. package/dist/core/src/types/table.js +1 -0
  140. package/dist/core/src/utils/compare.d.ts +13 -0
  141. package/dist/core/src/utils/compare.js +53 -0
  142. package/dist/core/src/utils/dateText.d.ts +12 -0
  143. package/dist/core/src/utils/dateText.js +30 -0
  144. package/dist/core/src/utils/getValue.d.ts +9 -0
  145. package/dist/core/src/utils/getValue.js +32 -0
  146. package/dist/index.d.ts +1 -0
  147. package/dist/index.js +2 -0
  148. package/dist/mui/src/TableLayout.d.ts +75 -0
  149. package/dist/mui/src/TableLayout.js +162 -0
  150. package/dist/mui/src/TableScreen.d.ts +70 -0
  151. package/dist/mui/src/TableScreen.js +174 -0
  152. package/dist/mui/src/addons/FilterModal.d.ts +45 -0
  153. package/dist/mui/src/addons/FilterModal.js +39 -0
  154. package/dist/mui/src/addons/FilterStatusModal.d.ts +40 -0
  155. package/dist/mui/src/addons/FilterStatusModal.js +68 -0
  156. package/dist/mui/src/addons/PresetBar.d.ts +56 -0
  157. package/dist/mui/src/addons/PresetBar.js +145 -0
  158. package/dist/mui/src/addons/ShareDropdown.d.ts +35 -0
  159. package/dist/mui/src/addons/ShareDropdown.js +41 -0
  160. package/dist/mui/src/addons/searchStore.d.ts +44 -0
  161. package/dist/mui/src/addons/searchStore.js +112 -0
  162. package/dist/mui/src/addons/searchSummary.d.ts +106 -0
  163. package/dist/mui/src/addons/searchSummary.js +109 -0
  164. package/dist/mui/src/columns/displayColumns.d.ts +100 -0
  165. package/dist/mui/src/columns/displayColumns.js +174 -0
  166. package/dist/mui/src/content/CardContent.d.ts +24 -0
  167. package/dist/mui/src/content/CardContent.js +25 -0
  168. package/dist/mui/src/content/ColumnMenu.d.ts +43 -0
  169. package/dist/mui/src/content/ColumnMenu.js +40 -0
  170. package/dist/mui/src/content/ColumnVisibilityMenu.d.ts +14 -0
  171. package/dist/mui/src/content/ColumnVisibilityMenu.js +37 -0
  172. package/dist/mui/src/content/EditCell.d.ts +19 -0
  173. package/dist/mui/src/content/EditCell.js +61 -0
  174. package/dist/mui/src/content/HeaderCell.d.ts +59 -0
  175. package/dist/mui/src/content/HeaderCell.js +88 -0
  176. package/dist/mui/src/content/SelectionActionBar.d.ts +17 -0
  177. package/dist/mui/src/content/SelectionActionBar.js +19 -0
  178. package/dist/mui/src/content/SortControl.d.ts +17 -0
  179. package/dist/mui/src/content/SortControl.js +45 -0
  180. package/dist/mui/src/content/TableContent.d.ts +191 -0
  181. package/dist/mui/src/content/TableContent.js +525 -0
  182. package/dist/mui/src/content/TableErrorFallback.d.ts +24 -0
  183. package/dist/mui/src/content/TableErrorFallback.js +29 -0
  184. package/dist/mui/src/content/cellError.d.ts +13 -0
  185. package/dist/mui/src/content/cellError.js +20 -0
  186. package/dist/mui/src/content/cellRenderers.d.ts +71 -0
  187. package/dist/mui/src/content/cellRenderers.js +127 -0
  188. package/dist/mui/src/content/cellValue.d.ts +27 -0
  189. package/dist/mui/src/content/cellValue.js +82 -0
  190. package/dist/mui/src/content/columnOrder.d.ts +7 -0
  191. package/dist/mui/src/content/columnOrder.js +48 -0
  192. package/dist/mui/src/content/density.d.ts +13 -0
  193. package/dist/mui/src/content/density.js +12 -0
  194. package/dist/mui/src/content/menuItems.d.ts +118 -0
  195. package/dist/mui/src/content/menuItems.js +128 -0
  196. package/dist/mui/src/content/pinning.d.ts +17 -0
  197. package/dist/mui/src/content/pinning.js +65 -0
  198. package/dist/mui/src/exporters/index.d.ts +1 -0
  199. package/dist/mui/src/exporters/index.js +1 -0
  200. package/dist/mui/src/exporters/useExportFormatters.d.ts +13 -0
  201. package/dist/mui/src/exporters/useExportFormatters.js +17 -0
  202. package/dist/mui/src/filter/FilterArea.d.ts +40 -0
  203. package/dist/mui/src/filter/FilterArea.js +37 -0
  204. package/dist/mui/src/filter/FilterInput.d.ts +14 -0
  205. package/dist/mui/src/filter/FilterInput.js +18 -0
  206. package/dist/mui/src/filter/inputs/DateFilterInput.d.ts +14 -0
  207. package/dist/mui/src/filter/inputs/DateFilterInput.js +112 -0
  208. package/dist/mui/src/filter/inputs/LinearFilterInput.d.ts +10 -0
  209. package/dist/mui/src/filter/inputs/LinearFilterInput.js +35 -0
  210. package/dist/mui/src/filter/inputs/NumberFilterInput.d.ts +12 -0
  211. package/dist/mui/src/filter/inputs/NumberFilterInput.js +111 -0
  212. package/dist/mui/src/filter/inputs/OperatorMenu.d.ts +14 -0
  213. package/dist/mui/src/filter/inputs/OperatorMenu.js +25 -0
  214. package/dist/mui/src/filter/inputs/RelationFilterInput.d.ts +11 -0
  215. package/dist/mui/src/filter/inputs/RelationFilterInput.js +67 -0
  216. package/dist/mui/src/filter/inputs/ScheduleFilterInput.d.ts +8 -0
  217. package/dist/mui/src/filter/inputs/ScheduleFilterInput.js +17 -0
  218. package/dist/mui/src/filter/inputs/SelectFilterInput.d.ts +16 -0
  219. package/dist/mui/src/filter/inputs/SelectFilterInput.js +57 -0
  220. package/dist/mui/src/filter/inputs/TextFilterInput.d.ts +13 -0
  221. package/dist/mui/src/filter/inputs/TextFilterInput.js +55 -0
  222. package/dist/mui/src/filter/kinds.d.ts +58 -0
  223. package/dist/mui/src/filter/kinds.js +103 -0
  224. package/dist/mui/src/grouping/AggregateChip.d.ts +55 -0
  225. package/dist/mui/src/grouping/AggregateChip.js +128 -0
  226. package/dist/mui/src/grouping/AggregationChips.d.ts +49 -0
  227. package/dist/mui/src/grouping/AggregationChips.js +80 -0
  228. package/dist/mui/src/grouping/AggregationSettingsButton.d.ts +14 -0
  229. package/dist/mui/src/grouping/AggregationSettingsButton.js +19 -0
  230. package/dist/mui/src/grouping/GroupingBar.d.ts +59 -0
  231. package/dist/mui/src/grouping/GroupingBar.js +267 -0
  232. package/dist/mui/src/grouping/StepExpandButton.d.ts +12 -0
  233. package/dist/mui/src/grouping/StepExpandButton.js +18 -0
  234. package/dist/mui/src/grouping/groupRow.d.ts +68 -0
  235. package/dist/mui/src/grouping/groupRow.js +129 -0
  236. package/dist/mui/src/headers/defaultHeader.d.ts +28 -0
  237. package/dist/mui/src/headers/defaultHeader.js +90 -0
  238. package/dist/mui/src/headers/defineHeader.d.ts +21 -0
  239. package/dist/mui/src/headers/defineHeader.js +68 -0
  240. package/dist/mui/src/headers/index.d.ts +5 -0
  241. package/dist/mui/src/headers/index.js +3 -0
  242. package/dist/mui/src/headers/meta.d.ts +18 -0
  243. package/dist/mui/src/headers/meta.js +11 -0
  244. package/dist/mui/src/headers/types.d.ts +84 -0
  245. package/dist/mui/src/headers/types.js +1 -0
  246. package/dist/mui/src/index.d.ts +110 -0
  247. package/dist/mui/src/index.js +79 -0
  248. package/dist/mui/src/layout/ResizeHandle.d.ts +30 -0
  249. package/dist/mui/src/layout/ResizeHandle.js +66 -0
  250. package/dist/mui/src/menus/columnMenu.d.ts +45 -0
  251. package/dist/mui/src/menus/columnMenu.js +144 -0
  252. package/dist/mui/src/menus/copy.d.ts +5 -0
  253. package/dist/mui/src/menus/copy.js +25 -0
  254. package/dist/mui/src/menus/index.d.ts +6 -0
  255. package/dist/mui/src/menus/index.js +4 -0
  256. package/dist/mui/src/menus/resolve.d.ts +15 -0
  257. package/dist/mui/src/menus/resolve.js +60 -0
  258. package/dist/mui/src/menus/rowMenu.d.ts +46 -0
  259. package/dist/mui/src/menus/rowMenu.js +76 -0
  260. package/dist/mui/src/pagination/PaginationArea.d.ts +16 -0
  261. package/dist/mui/src/pagination/PaginationArea.js +23 -0
  262. package/dist/mui/src/pivot/PivotTable.d.ts +18 -0
  263. package/dist/mui/src/pivot/PivotTable.js +100 -0
  264. package/dist/mui/src/provider/KokoTableProvider.d.ts +184 -0
  265. package/dist/mui/src/provider/KokoTableProvider.js +118 -0
  266. package/dist/mui/src/provider/labels.d.ts +85 -0
  267. package/dist/mui/src/provider/labels.js +104 -0
  268. package/dist/mui/src/registry.d.ts +30 -0
  269. package/dist/mui/src/registry.js +5 -0
  270. package/dist/mui/src/toolbar/GlobalFilterField.d.ts +36 -0
  271. package/dist/mui/src/toolbar/GlobalFilterField.js +78 -0
  272. package/dist/mui/src/virtual/VirtualTableContent.d.ts +41 -0
  273. package/dist/mui/src/virtual/VirtualTableContent.js +58 -0
  274. package/dist/mui/src/virtual/VirtualTableScreen.d.ts +13 -0
  275. package/dist/mui/src/virtual/VirtualTableScreen.js +23 -0
  276. package/dist/mui/src/virtual.d.ts +4 -0
  277. package/dist/mui/src/virtual.js +4 -0
  278. package/dist/mui/src/virtualColumns/VirtualColumnCard.d.ts +26 -0
  279. package/dist/mui/src/virtualColumns/VirtualColumnCard.js +29 -0
  280. package/dist/mui/src/virtualColumns/VirtualColumnDialog.d.ts +27 -0
  281. package/dist/mui/src/virtualColumns/VirtualColumnDialog.js +154 -0
  282. package/dist/mui/src/virtualColumns/VirtualColumnSection.d.ts +31 -0
  283. package/dist/mui/src/virtualColumns/VirtualColumnSection.js +55 -0
  284. package/dist/mui/src/virtualColumns/expression.d.ts +68 -0
  285. package/dist/mui/src/virtualColumns/expression.js +489 -0
  286. package/dist/mui/src/virtualColumns/useVirtualColumns.d.ts +67 -0
  287. package/dist/mui/src/virtualColumns/useVirtualColumns.js +111 -0
  288. package/dist/mui/src/virtualColumns/virtualColumnDefs.d.ts +29 -0
  289. package/dist/mui/src/virtualColumns/virtualColumnDefs.js +51 -0
  290. package/dist/mui-virtual.d.ts +1 -0
  291. package/dist/mui-virtual.js +2 -0
  292. package/dist/mui.d.ts +1 -0
  293. package/dist/mui.js +2 -0
  294. package/dist/query/src/index.d.ts +6 -0
  295. package/dist/query/src/index.js +7 -0
  296. package/dist/query/src/serverPivot.d.ts +42 -0
  297. package/dist/query/src/serverPivot.js +153 -0
  298. package/dist/query/src/useServerGrouping.d.ts +52 -0
  299. package/dist/query/src/useServerGrouping.js +186 -0
  300. package/dist/query/src/useServerTable.d.ts +20 -0
  301. package/dist/query/src/useServerTable.js +106 -0
  302. package/dist/query/src/useServerTableData.d.ts +48 -0
  303. package/dist/query/src/useServerTableData.js +64 -0
  304. package/dist/query/src/wire.d.ts +16 -0
  305. package/dist/query/src/wire.js +32 -0
  306. package/dist/query.d.ts +1 -0
  307. package/dist/query.js +2 -0
  308. package/package.json +125 -0
@@ -0,0 +1,145 @@
1
+ import { pathKey } from "../types/grouping";
2
+ import { createLabelResolver } from "../columns/labels";
3
+ import { aggregateRows } from "./aggregate";
4
+ import { getValue } from "../utils/getValue";
5
+ import { compareValues } from "../utils/compare";
6
+ /**
7
+ * 행을 그룹 트리로 묶는다.
8
+ *
9
+ * 그룹 순서는 **값 오름차순**이고 NULL 이 뒤로 간다 — 정렬 규칙(`applySorting`)과
10
+ * 같은 규약이라 그룹을 켰다 껐을 때 순서가 뒤집히지 않는다.
11
+ */
12
+ export function groupRows(rows, options) {
13
+ const { grouping, aggregation, aggregations: registry } = options;
14
+ if (grouping.length === 0)
15
+ return [];
16
+ const resolveLabel = createLabelResolver(options.columns, options.labelOf);
17
+ const totalCount = rows.length;
18
+ const build = (subset, depth, parentPath) => {
19
+ const key = grouping[depth];
20
+ const buckets = new Map();
21
+ for (const row of subset) {
22
+ const value = getValue(row, key);
23
+ const bucketKey = String(value);
24
+ const bucket = buckets.get(bucketKey);
25
+ if (bucket)
26
+ bucket.rows.push(row);
27
+ else
28
+ buckets.set(bucketKey, { value, rows: [row] });
29
+ }
30
+ const nodes = [];
31
+ for (const bucket of buckets.values()) {
32
+ const path = [...parentPath, { key, value: bucket.value }];
33
+ const isLast = depth === grouping.length - 1;
34
+ nodes.push({
35
+ groupKey: key,
36
+ groupValue: bucket.value,
37
+ label: resolveLabel(key, bucket.value),
38
+ count: bucket.rows.length,
39
+ aggregates: aggregation ? aggregateRows(bucket.rows, aggregation, { totalCount }, registry) : undefined,
40
+ children: isLast ? undefined : build(bucket.rows, depth + 1, path),
41
+ rows: isLast ? bucket.rows : undefined,
42
+ path,
43
+ depth,
44
+ });
45
+ }
46
+ nodes.sort((a, b) => {
47
+ const aNull = a.groupValue === null || a.groupValue === undefined;
48
+ const bNull = b.groupValue === null || b.groupValue === undefined;
49
+ if (aNull || bNull)
50
+ return aNull && bNull ? 0 : aNull ? 1 : -1;
51
+ return compareValues(a.groupValue, b.groupValue);
52
+ });
53
+ return nodes;
54
+ };
55
+ return build(rows, 0, []);
56
+ }
57
+ /** 트리의 최대 깊이 (그룹 레벨 수) */
58
+ export function groupDepth(nodes) {
59
+ let max = 0;
60
+ const walk = (list, depth) => {
61
+ if (list.length === 0)
62
+ return;
63
+ max = Math.max(max, depth + 1);
64
+ for (const node of list)
65
+ if (node.children)
66
+ walk(node.children, depth + 1);
67
+ };
68
+ walk(nodes, 0);
69
+ return max;
70
+ }
71
+ /**
72
+ * 트리를 화면 순서대로 평탄화한다. 접힌 그룹의 하위는 건너뛴다.
73
+ * 가상화 리스트가 그대로 소비할 수 있는 형태다.
74
+ */
75
+ export function flattenGroups(nodes, options) {
76
+ const { expanded, includeLeaves = true } = options;
77
+ const items = [];
78
+ let leafIndex = 0;
79
+ const walk = (list, depth, parentIndex) => {
80
+ for (const node of list) {
81
+ const index = items.length;
82
+ items.push({ type: "group", node, depth, index, parentIndex });
83
+ if (!expanded.has(pathKey(node.path)))
84
+ continue;
85
+ if (node.children) {
86
+ walk(node.children, depth + 1, index);
87
+ }
88
+ else if (includeLeaves && node.rows) {
89
+ for (const row of node.rows) {
90
+ items.push({
91
+ type: "leaf",
92
+ row,
93
+ depth: depth + 1,
94
+ index: items.length,
95
+ parentIndex: index,
96
+ leafIndex: leafIndex++,
97
+ });
98
+ }
99
+ }
100
+ // 서버 그룹핑: 아직 못 받은 하위가 남아 있으면 "더 보기" 행을 둔다
101
+ if (node.hasMore) {
102
+ items.push({
103
+ type: "loadMore",
104
+ path: node.path,
105
+ depth: depth + 1,
106
+ index: items.length,
107
+ parentIndex: index,
108
+ loaded: node.loadedCount ?? (node.children?.length ?? node.rows?.length ?? 0),
109
+ total: node.totalCount,
110
+ isPending: node.isPending,
111
+ });
112
+ }
113
+ }
114
+ };
115
+ walk(nodes, 0);
116
+ return items;
117
+ }
118
+ /** 트리 전체의 경로 키 — expandAll 용 */
119
+ export function allGroupPaths(nodes) {
120
+ const keys = [];
121
+ const walk = (list) => {
122
+ for (const node of list) {
123
+ keys.push(pathKey(node.path));
124
+ if (node.children)
125
+ walk(node.children);
126
+ }
127
+ };
128
+ walk(nodes);
129
+ return keys;
130
+ }
131
+ /** 특정 깊이까지의 경로 키 — 단계 펼치기 용 */
132
+ export function pathsToDepth(nodes, depth) {
133
+ const keys = [];
134
+ const walk = (list, current) => {
135
+ if (current >= depth)
136
+ return;
137
+ for (const node of list) {
138
+ keys.push(pathKey(node.path));
139
+ if (node.children)
140
+ walk(node.children, current + 1);
141
+ }
142
+ };
143
+ walk(nodes, 0);
144
+ return keys;
145
+ }
@@ -0,0 +1,67 @@
1
+ import type { RowData } from "@tanstack/react-table";
2
+ import type { TableColumnDefs } from "../types/column";
3
+ import type { Pagination, TableRequest } from "../types/request";
4
+ import { type MatchContext } from "../filters/registry";
5
+ import { DEFAULT_PAGE_SIZE } from "../defaults";
6
+ export interface ClientQueryOptions<T extends RowData> extends MatchContext {
7
+ /**
8
+ * globalFilter 대상 필드. 미지정이면 **선언된 텍스트 계열 컬럼**의 경로를 쓴다
9
+ * (`defaultGlobalFilterFields` — enum/select/common_code 포함, number/date/boolean/
10
+ * 관계·선형·일정 제외).
11
+ *
12
+ * 서버는 모델의 varchar/text/enum 컬럼과 FK 대상 테이블의 텍스트 컬럼을 자동
13
+ * 탐색하므로(`GlobalFilterMixin._collect_auto_columns`), 화면에 선언하지 않은
14
+ * 컬럼까지 훑는다. 클라이언트는 선언된 컬럼만 볼 수 있어 이 지점만은 완전한
15
+ * 동치가 불가능하다 — 필요하면 여기에 명시적으로 넘긴다.
16
+ */
17
+ globalFilterFields?: string[];
18
+ }
19
+ export interface ClientQueryResult<T> {
20
+ /** 필터·정렬·페이지까지 적용된 행 */
21
+ rows: T[];
22
+ /** 페이지 적용 **전** 건수 — 서버 `meta.total_count` 에 대응 */
23
+ total: number;
24
+ /** 페이지 적용 전 전체 행 (집계·내보내기용) */
25
+ filtered: T[];
26
+ }
27
+ /** 컬럼 필터 적용 — 미입력 필터와 "전체" 선택은 건너뛴다 */
28
+ export declare function applyColumnFilters<T extends RowData>(rows: T[], columns: TableColumnDefs<T>, request: TableRequest, ctx?: MatchContext): T[];
29
+ /**
30
+ * globalFilter 기본 대상 — 선언된 컬럼 중 **텍스트 계열**(`resolveDataKind === "text"`).
31
+ *
32
+ * 서버 자동 탐색이 varchar/text/enum 만 보는 것과 맞춘 것이다. 숫자·날짜·boolean 을
33
+ * 문자열화해 검색하면 `"12"` 가 idx 나 `2024-12-31` 에 걸려 서버와 결과가 갈렸다.
34
+ * 관계·선형·일정 같은 가상 컬럼도 뺀다 (서버는 relationship 을 탐색하지 않는다).
35
+ */
36
+ export declare function defaultGlobalFilterFields<T extends RowData>(columns: TableColumnDefs<T>): string[];
37
+ /**
38
+ * globalFilter 적용 — 백엔드 fuzzy 와 같은 규칙(양쪽 공백 제거 + 소문자 부분일치).
39
+ */
40
+ export declare function applyGlobalFilter<T extends RowData>(rows: T[], columns: TableColumnDefs<T>, request: TableRequest, options?: ClientQueryOptions<T>): T[];
41
+ /**
42
+ * 정렬 — PostgreSQL 기본과 같은 NULL 위치를 쓴다 (ASC = NULLS LAST, DESC = NULLS FIRST).
43
+ * `sorting[].key` 가 있으면 그 필드로 정렬한다 (가상/통합 컬럼).
44
+ */
45
+ export declare function applySorting<T extends RowData>(rows: T[], request: TableRequest): T[];
46
+ /**
47
+ * 페이지 슬라이스 — 서버 `OFFSET/LIMIT` 과 같은 의미.
48
+ *
49
+ * "전체" 는 `pagination` 미지정 또는 `PAGE_SIZE_ALL` 로만 표현한다.
50
+ * `pageSize 0` 은 서버 `LIMIT 0` 이 그렇듯 **0건**이다 — 예전에는 이를 "전체" 로
51
+ * 해석해 client 모드에서만 전행이 나왔다.
52
+ */
53
+ export declare function paginateRows<T>(rows: T[], pagination: Pagination | undefined): T[];
54
+ export declare function applyPagination<T>(rows: T[], request: TableRequest): T[];
55
+ /**
56
+ * 페이지 **앞** 단계 — 컬럼 필터 → 전역 필터 → 정렬.
57
+ *
58
+ * `runClientQuery` 에서 떼어 낸 이유: 훅이 이 결과를 따로 메모이즈해 두면 페이지
59
+ * 이동에는 슬라이스만 다시 하고, 50k 행 정렬을 매 페이지마다 반복하지 않는다.
60
+ */
61
+ export declare function filterAndSortRows<T extends RowData>(rows: T[], columns: TableColumnDefs<T>, request: TableRequest, options?: ClientQueryOptions<T>): T[];
62
+ /**
63
+ * 클라이언트 모드의 전체 파이프라인 — 서버 QueryBuilder 와 같은 순서로 적용한다.
64
+ * (컬럼 필터 → 전역 필터 → 정렬 → 페이지)
65
+ */
66
+ export declare function runClientQuery<T extends RowData>(rows: T[], columns: TableColumnDefs<T>, request: TableRequest, options?: ClientQueryOptions<T>): ClientQueryResult<T>;
67
+ export { DEFAULT_PAGE_SIZE };
@@ -0,0 +1,157 @@
1
+ import { isValuelessOperator } from "../types/operators";
2
+ import { columnKey } from "../columns/deriveOperators";
3
+ import { isVirtualColumn, matchColumnFilter, resolveDataKind, } from "../filters/registry";
4
+ import { compareValues, normalizeSearchText } from "../utils/compare";
5
+ import { getValue, isUnsetFilterValue } from "../utils/getValue";
6
+ import { ALL_FILTER_VALUE } from "../filters/list";
7
+ import { DEFAULT_PAGE_SIZE, PAGE_SIZE_ALL } from "../defaults";
8
+ function columnMap(columns) {
9
+ const map = new Map();
10
+ for (const column of columns) {
11
+ const key = columnKey(column);
12
+ if (key)
13
+ map.set(key, column);
14
+ }
15
+ return map;
16
+ }
17
+ /** 컬럼 필터 적용 — 미입력 필터와 "전체" 선택은 건너뛴다 */
18
+ export function applyColumnFilters(rows, columns, request, ctx = {}) {
19
+ const filters = request.columnFilters ?? [];
20
+ if (filters.length === 0)
21
+ return rows;
22
+ const byId = columnMap(columns);
23
+ const operators = request.columnFilterFns ?? {};
24
+ let result = rows;
25
+ for (const filter of filters) {
26
+ if (!filter)
27
+ continue;
28
+ const operator = (operators[filter.id] ?? "fuzzy");
29
+ // 값을 안 쓰는 연산자(empty/isEmpty/hasChild …)는 미입력이어도 적용된다
30
+ if (!isValuelessOperator(operator) && isUnsetFilterValue(filter.value))
31
+ continue;
32
+ // "전체" 센티널 — 서버가 필터를 통째로 건너뛴다
33
+ if (filter.value === ALL_FILTER_VALUE)
34
+ continue;
35
+ if (Array.isArray(filter.value) && filter.value.includes(ALL_FILTER_VALUE))
36
+ continue;
37
+ const column = byId.get(filter.id);
38
+ result = result.filter((row) => matchColumnFilter(row, filter.id, filter.value, operator, column, ctx));
39
+ }
40
+ return result;
41
+ }
42
+ /**
43
+ * globalFilter 기본 대상 — 선언된 컬럼 중 **텍스트 계열**(`resolveDataKind === "text"`).
44
+ *
45
+ * 서버 자동 탐색이 varchar/text/enum 만 보는 것과 맞춘 것이다. 숫자·날짜·boolean 을
46
+ * 문자열화해 검색하면 `"12"` 가 idx 나 `2024-12-31` 에 걸려 서버와 결과가 갈렸다.
47
+ * 관계·선형·일정 같은 가상 컬럼도 뺀다 (서버는 relationship 을 탐색하지 않는다).
48
+ */
49
+ export function defaultGlobalFilterFields(columns) {
50
+ const fields = [];
51
+ for (const column of columns) {
52
+ if (isVirtualColumn(column))
53
+ continue;
54
+ if (resolveDataKind(column) !== "text")
55
+ continue;
56
+ const key = columnKey(column);
57
+ if (key)
58
+ fields.push(key);
59
+ }
60
+ return fields;
61
+ }
62
+ /**
63
+ * globalFilter 적용 — 백엔드 fuzzy 와 같은 규칙(양쪽 공백 제거 + 소문자 부분일치).
64
+ */
65
+ export function applyGlobalFilter(rows, columns, request, options = {}) {
66
+ const needle = normalizeSearchText(String(request.globalFilter ?? ""));
67
+ if (!needle)
68
+ return rows;
69
+ const fields = options.globalFilterFields ?? defaultGlobalFilterFields(columns);
70
+ if (fields.length === 0)
71
+ return rows;
72
+ return rows.filter((row) => fields.some((field) => {
73
+ const value = getValue(row, field);
74
+ if (value === null || value === undefined)
75
+ return false;
76
+ return normalizeSearchText(String(value)).includes(needle);
77
+ }));
78
+ }
79
+ /**
80
+ * 정렬 — PostgreSQL 기본과 같은 NULL 위치를 쓴다 (ASC = NULLS LAST, DESC = NULLS FIRST).
81
+ * `sorting[].key` 가 있으면 그 필드로 정렬한다 (가상/통합 컬럼).
82
+ */
83
+ export function applySorting(rows, request) {
84
+ const sorting = request.sorting ?? [];
85
+ if (sorting.length === 0)
86
+ return rows;
87
+ const sorted = [...rows];
88
+ sorted.sort((a, b) => {
89
+ for (const sort of sorting) {
90
+ const path = sort.key ?? sort.id;
91
+ const av = getValue(a, path);
92
+ const bv = getValue(b, path);
93
+ const aNull = av === null || av === undefined;
94
+ const bNull = bv === null || bv === undefined;
95
+ if (aNull || bNull) {
96
+ if (aNull && bNull)
97
+ continue;
98
+ // PostgreSQL 기본: ASC = NULLS LAST, DESC = NULLS FIRST
99
+ const nullGoesLast = !sort.desc;
100
+ if (aNull)
101
+ return nullGoesLast ? 1 : -1;
102
+ return nullGoesLast ? -1 : 1;
103
+ }
104
+ const cmp = compareValues(av, bv);
105
+ if (cmp !== 0)
106
+ return sort.desc ? -cmp : cmp;
107
+ }
108
+ return 0;
109
+ });
110
+ return sorted;
111
+ }
112
+ /**
113
+ * 페이지 슬라이스 — 서버 `OFFSET/LIMIT` 과 같은 의미.
114
+ *
115
+ * "전체" 는 `pagination` 미지정 또는 `PAGE_SIZE_ALL` 로만 표현한다.
116
+ * `pageSize 0` 은 서버 `LIMIT 0` 이 그렇듯 **0건**이다 — 예전에는 이를 "전체" 로
117
+ * 해석해 client 모드에서만 전행이 나왔다.
118
+ */
119
+ export function paginateRows(rows, pagination) {
120
+ if (!pagination)
121
+ return rows;
122
+ const { pageIndex, pageSize } = pagination;
123
+ if (!Number.isFinite(pageSize) || pageSize >= PAGE_SIZE_ALL)
124
+ return rows;
125
+ if (pageSize <= 0)
126
+ return [];
127
+ const start = Math.max(0, pageIndex) * pageSize;
128
+ return rows.slice(start, start + pageSize);
129
+ }
130
+ export function applyPagination(rows, request) {
131
+ return paginateRows(rows, request.pagination);
132
+ }
133
+ /**
134
+ * 페이지 **앞** 단계 — 컬럼 필터 → 전역 필터 → 정렬.
135
+ *
136
+ * `runClientQuery` 에서 떼어 낸 이유: 훅이 이 결과를 따로 메모이즈해 두면 페이지
137
+ * 이동에는 슬라이스만 다시 하고, 50k 행 정렬을 매 페이지마다 반복하지 않는다.
138
+ */
139
+ export function filterAndSortRows(rows, columns, request, options = {}) {
140
+ const ctx = { now: options.now };
141
+ let result = applyColumnFilters(rows, columns, request, ctx);
142
+ result = applyGlobalFilter(result, columns, request, options);
143
+ return applySorting(result, request);
144
+ }
145
+ /**
146
+ * 클라이언트 모드의 전체 파이프라인 — 서버 QueryBuilder 와 같은 순서로 적용한다.
147
+ * (컬럼 필터 → 전역 필터 → 정렬 → 페이지)
148
+ */
149
+ export function runClientQuery(rows, columns, request, options = {}) {
150
+ const filtered = filterAndSortRows(rows, columns, request, options);
151
+ return {
152
+ rows: applyPagination(filtered, request),
153
+ total: filtered.length,
154
+ filtered,
155
+ };
156
+ }
157
+ export { DEFAULT_PAGE_SIZE };
@@ -0,0 +1,121 @@
1
+ import type { ReactNode } from "react";
2
+ import type { RowData } from "@tanstack/react-table";
3
+ import type { CellAlign, ColumnDataType, ColumnMeta, DateFilterMode, EditSpec, SelectOption, TableColumnDef } from "../types/column";
4
+ import type { AnyFilterKind, FilterSpec, FilterSpecOf } from "../types/filter";
5
+ import type { FilterOperator, FilterVariant } from "../types/operators";
6
+ /** 셀 렌더러가 받는 값 — table·row 인스턴스가 아니라 순수 값이다. `V` 는 셀 값 타입(기본 `unknown`) */
7
+ export interface CellRenderProps<T extends RowData, V = unknown> {
8
+ value: V;
9
+ row: T;
10
+ rowIndex: number;
11
+ columnId: string;
12
+ /** 기본 포맷터(또는 이전 렌더러)가 만든 결과 */
13
+ renderedValue: ReactNode;
14
+ }
15
+ export type CellRender<T extends RowData, V = unknown> = (props: CellRenderProps<T, V>) => ReactNode;
16
+ /** 헤더 규칙 — `setHeaderRule` 이 `meta.headerTooltip` / `meta.align` / `meta.headerAlign` 으로 옮긴다 */
17
+ export interface HeaderRule {
18
+ tooltip?: string;
19
+ align?: CellAlign;
20
+ headerAlign?: CellAlign;
21
+ }
22
+ export type { EditSpec };
23
+ export interface SizingSpec {
24
+ size?: number;
25
+ minSize?: number;
26
+ maxSize?: number;
27
+ grow?: boolean | number;
28
+ /** 좁은 컬럼에서 말줄임 */
29
+ ellipsis?: boolean;
30
+ }
31
+ /** `dataType` → 기본 필터 UI 종류 (`deriveOperators.inferVariant` 와 같은 표) */
32
+ export declare const inferFilterVariant: (dataType: ColumnDataType) => FilterVariant;
33
+ /** 기본 포맷터 — 순수 함수라 렌더러가 없어도 동작한다 (CSV·PDF 내보내기 공용) */
34
+ export declare function formatByDataType(value: unknown, dataType: ColumnDataType): string;
35
+ /**
36
+ * 컬럼 빌더 — 체이닝으로 컬럼 정의를 조립한다.
37
+ *
38
+ * 원본과 달리 **MUI 를 모른다.** 편집 입력·칩 같은 렌더 선언은 `meta.edit` 에
39
+ * 데이터로 남기고, 실제 컴포넌트는 렌더러 패키지가 읽어서 그린다. 그래야
40
+ * 같은 컬럼 정의로 테이블도, 카드도, 내보내기도 그릴 수 있다.
41
+ */
42
+ export declare class ColumnBuilder<T extends RowData, V = any> {
43
+ column: TableColumnDef<T, V>;
44
+ readonly key: string;
45
+ dataType: ColumnDataType;
46
+ constructor(key: string, header: string, dataType?: ColumnDataType);
47
+ /**
48
+ * 점 표기 key 는 `accessorKey` 대신 `accessorFn` 으로 읽는다.
49
+ * TanStack 의 깊은 `accessorKey` 접근은 중간 객체가 없으면 행마다 경고를 낸다
50
+ * (확장 자식 행 등) — 안전 접근으로 대체해 조용히 undefined 를 돌려준다.
51
+ *
52
+ * 값은 **변환하지 않는다.** 필터·정렬·집계는 2층이 원값(`getValue`)으로 계산하므로
53
+ * 셀만 다른 값(boolean 문자열, NaN→0, Date 객체)을 보면 두 경로가 어긋난다.
54
+ * 표시 포맷은 `formatByDataType` 이 원값에서 만든다.
55
+ */
56
+ private applyAccessor;
57
+ private applyDefaultCell;
58
+ /** 필터 UI 종류 변경 — 연산자 후보와 내장 kind 도 함께 갈아끼운다 (`custom` 은 kind 없음) */
59
+ setVariant(variant: FilterVariant, operators?: readonly FilterOperator[]): this;
60
+ /**
61
+ * 필터 kind 지정 — 연산자·기본값이 kind 로 좁혀진다 (`filter: { kind }` 를 인라인으로 쓸 때와 달리
62
+ * 레지스트리에 등록하지 않은 kind 에도 연산자·기본값을 줄 수 있다).
63
+ * 연산자 후보는 `spec.operators` > kind 의 `offered` > 전체, variant 는 `spec.variant` > kind 의 힌트.
64
+ */
65
+ setFilterKind<K extends AnyFilterKind>(kind: K, spec?: Omit<FilterSpecOf<K>, "kind">): this;
66
+ /** 필터 선언 병합. 필터 값 타입은 셀 값 `V` 와 다를 수 있어 `FilterSpec<any>` 로 받는다 */
67
+ setFilter(spec: FilterSpec<any>): this;
68
+ setOperators(operators: readonly FilterOperator[], defaultOperator?: FilterOperator): this;
69
+ setDateFilterMode(mode: DateFilterMode): this;
70
+ setMeta(meta: Partial<ColumnMeta>): this;
71
+ setProps(props: Partial<TableColumnDef<T, any>>): this;
72
+ /**
73
+ * 소비 앱 확장 메타 (`meta.custom`) — 여러 번 부르면 얕게 병합된다.
74
+ * 읽는 쪽은 `columnMetaOf<Ext>(column)?.custom` 으로 타입을 살린다.
75
+ */
76
+ setCustom<E extends object>(ext: E): this;
77
+ /** 헤더 규칙 — 툴팁·셀 정렬·헤더 정렬. 렌더러가 `meta.headerTooltip` / `meta.align` / `meta.headerAlign` 을 읽는다 */
78
+ setHeaderRule(rule: HeaderRule): this;
79
+ setHeader(header: ReactNode): this;
80
+ /** 필터 패널에만 쓰는 라벨 (헤더가 ReactNode 일 때) */
81
+ setFilterLabel(label: ReactNode): this;
82
+ setSizing(options: SizingSpec): this;
83
+ /** TanStack 셀 컨텍스트 → 순수 값 props (setCell/setCellWrapper 공용) */
84
+ private toCellProps;
85
+ /** 셀 렌더러 교체 — `value` 는 셀 값 타입 `V` 로 온다 */
86
+ setCell(render: CellRender<T, V>): this;
87
+ /** 기존 렌더 결과를 감싼다 (링크·칩 등) */
88
+ setCellWrapper(wrap: (rendered: ReactNode, props: CellRenderProps<T, V>) => ReactNode): this;
89
+ /**
90
+ * 표시 여부.
91
+ *
92
+ * `forceHidden` 은 값 보관 전용 — 사용자 토글이나 프리셋 복원으로도 되살아나지
93
+ * 않는다. `isVisible: false` 는 "기본 숨김"일 뿐이라 되살아난다. 헤더가 없는
94
+ * 컬럼(통합 cascade 에 흡수된 레벨 등)은 반드시 `forceHidden` 을 쓴다.
95
+ */
96
+ setDisplay(options?: {
97
+ display?: boolean;
98
+ hideFilter?: boolean;
99
+ forceHidden?: boolean;
100
+ }): this;
101
+ setEditable(enabled?: boolean, onCommit?: (value: unknown, row: T) => void): this;
102
+ /**
103
+ * 선택 편집 — 선언만 남긴다. dropdown/chip/button 컴포넌트는 렌더러가 그린다.
104
+ */
105
+ setEditSelect(config: Omit<EditSpec<T>, "enabled" | "kind">): this;
106
+ setEdit(spec: EditSpec<T>): this;
107
+ /**
108
+ * 필터 선택지. 목록이 있는 컬럼은 자유 텍스트 입력 대상이 아니다 —
109
+ * 라벨과 저장값이 달라 드롭다운이 사라지면 화면의 라벨을 쳐서 조용히 0건이 된다.
110
+ */
111
+ setFilterOptions(options: SelectOption[], includeAll?: boolean): this;
112
+ /** 집계 활성 — 서버 집계 키와 클라이언트 그룹 집계에 함께 쓰인다 */
113
+ setAgg(enabled: boolean, aggregations?: string[]): this;
114
+ setFooter(footer: ReactNode | ((ctx: unknown) => ReactNode)): this;
115
+ /** 이 컬럼으로 정렬할 때 서버에 대신 보낼 실제 필드 (가상/통합 컬럼) */
116
+ setSortExpandsTo(fields: string[]): this;
117
+ getColumn(): TableColumnDef<T, V>;
118
+ build(): TableColumnDef<T, V>;
119
+ }
120
+ /** 빌더 배열을 컬럼 정의 배열로 */
121
+ export declare function buildColumns<T extends RowData>(builders: Array<ColumnBuilder<T, any> | ColumnBuilder<T, any>[] | TableColumnDef<T, any>>): Array<TableColumnDef<T, any>>;