@shadng/sng-ui 1.0.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 (271) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +75 -0
  3. package/cli/sng-ui.js +331 -0
  4. package/ng-package.json +29 -0
  5. package/package.json +64 -0
  6. package/registry.json +72 -0
  7. package/src/lib/accordion/cn.ts +6 -0
  8. package/src/lib/accordion/index.ts +18 -0
  9. package/src/lib/accordion/sng-accordion-content.ts +131 -0
  10. package/src/lib/accordion/sng-accordion-item.ts +299 -0
  11. package/src/lib/accordion/sng-accordion-trigger.ts +137 -0
  12. package/src/lib/accordion/sng-accordion.ts +118 -0
  13. package/src/lib/accordion/sng-accordion.types.ts +82 -0
  14. package/src/lib/alert/cn.ts +6 -0
  15. package/src/lib/alert/index.ts +3 -0
  16. package/src/lib/alert/sng-alert-description.ts +49 -0
  17. package/src/lib/alert/sng-alert-title.ts +46 -0
  18. package/src/lib/alert/sng-alert.ts +48 -0
  19. package/src/lib/avatar/cn.ts +6 -0
  20. package/src/lib/avatar/index.ts +3 -0
  21. package/src/lib/avatar/sng-avatar-fallback.ts +50 -0
  22. package/src/lib/avatar/sng-avatar-image.ts +73 -0
  23. package/src/lib/avatar/sng-avatar.ts +60 -0
  24. package/src/lib/badge/cn.ts +6 -0
  25. package/src/lib/badge/index.ts +1 -0
  26. package/src/lib/badge/sng-badge.ts +36 -0
  27. package/src/lib/breadcrumb/cn.ts +6 -0
  28. package/src/lib/breadcrumb/index.ts +7 -0
  29. package/src/lib/breadcrumb/sng-breadcrumb-ellipsis.ts +61 -0
  30. package/src/lib/breadcrumb/sng-breadcrumb-item.ts +47 -0
  31. package/src/lib/breadcrumb/sng-breadcrumb-link.ts +43 -0
  32. package/src/lib/breadcrumb/sng-breadcrumb-list.ts +42 -0
  33. package/src/lib/breadcrumb/sng-breadcrumb-page.ts +44 -0
  34. package/src/lib/breadcrumb/sng-breadcrumb-separator.ts +60 -0
  35. package/src/lib/breadcrumb/sng-breadcrumb.ts +52 -0
  36. package/src/lib/button/cn.ts +6 -0
  37. package/src/lib/button/index.ts +2 -0
  38. package/src/lib/button/sng-button.ts +264 -0
  39. package/src/lib/calendar/cn.ts +6 -0
  40. package/src/lib/calendar/index.ts +2 -0
  41. package/src/lib/calendar/sng-calendar.ts +753 -0
  42. package/src/lib/card/cn.ts +6 -0
  43. package/src/lib/card/index.ts +6 -0
  44. package/src/lib/card/sng-card-content.ts +36 -0
  45. package/src/lib/card/sng-card-description.ts +38 -0
  46. package/src/lib/card/sng-card-footer.ts +34 -0
  47. package/src/lib/card/sng-card-header.ts +34 -0
  48. package/src/lib/card/sng-card-title.ts +48 -0
  49. package/src/lib/card/sng-card.ts +43 -0
  50. package/src/lib/carousel/cn.ts +6 -0
  51. package/src/lib/carousel/index.ts +18 -0
  52. package/src/lib/carousel/sng-carousel.ts +526 -0
  53. package/src/lib/checkbox/cn.ts +6 -0
  54. package/src/lib/checkbox/index.ts +1 -0
  55. package/src/lib/checkbox/sng-checkbox.ts +154 -0
  56. package/src/lib/code-block/cn.ts +6 -0
  57. package/src/lib/code-block/index.ts +1 -0
  58. package/src/lib/code-block/sng-code-block.ts +296 -0
  59. package/src/lib/dialog/cn.ts +6 -0
  60. package/src/lib/dialog/index.ts +37 -0
  61. package/src/lib/dialog/sng-dialog-close.ts +76 -0
  62. package/src/lib/dialog/sng-dialog-content.ts +132 -0
  63. package/src/lib/dialog/sng-dialog-description.ts +36 -0
  64. package/src/lib/dialog/sng-dialog-footer.ts +39 -0
  65. package/src/lib/dialog/sng-dialog-header.ts +39 -0
  66. package/src/lib/dialog/sng-dialog-title.ts +52 -0
  67. package/src/lib/dialog/sng-dialog.service.ts +222 -0
  68. package/src/lib/dialog/sng-dialog.ts +224 -0
  69. package/src/lib/drawer/cn.ts +6 -0
  70. package/src/lib/drawer/index.ts +36 -0
  71. package/src/lib/drawer/sng-drawer-close.ts +28 -0
  72. package/src/lib/drawer/sng-drawer-content.ts +135 -0
  73. package/src/lib/drawer/sng-drawer-description.ts +29 -0
  74. package/src/lib/drawer/sng-drawer-footer.ts +34 -0
  75. package/src/lib/drawer/sng-drawer-handle.ts +30 -0
  76. package/src/lib/drawer/sng-drawer-header.ts +30 -0
  77. package/src/lib/drawer/sng-drawer-title.ts +27 -0
  78. package/src/lib/drawer/sng-drawer-trigger.ts +21 -0
  79. package/src/lib/drawer/sng-drawer-wrapper.ts +27 -0
  80. package/src/lib/drawer/sng-drawer.ts +166 -0
  81. package/src/lib/file-input/cn.ts +6 -0
  82. package/src/lib/file-input/index.ts +1 -0
  83. package/src/lib/file-input/sng-file-input.ts +288 -0
  84. package/src/lib/hover-card/cn.ts +6 -0
  85. package/src/lib/hover-card/index.ts +3 -0
  86. package/src/lib/hover-card/sng-hover-card-content.ts +100 -0
  87. package/src/lib/hover-card/sng-hover-card-trigger.ts +43 -0
  88. package/src/lib/hover-card/sng-hover-card.ts +246 -0
  89. package/src/lib/input/cn.ts +6 -0
  90. package/src/lib/input/index.ts +1 -0
  91. package/src/lib/input/sng-input.ts +160 -0
  92. package/src/lib/layout/cn.ts +6 -0
  93. package/src/lib/layout/index.ts +98 -0
  94. package/src/lib/layout/sng-layout-footer.ts +37 -0
  95. package/src/lib/layout/sng-layout-header.ts +38 -0
  96. package/src/lib/layout/sng-layout-sidebar-content.ts +149 -0
  97. package/src/lib/layout/sng-layout-sidebar-footer.ts +54 -0
  98. package/src/lib/layout/sng-layout-sidebar-group-action.ts +67 -0
  99. package/src/lib/layout/sng-layout-sidebar-group-content.ts +41 -0
  100. package/src/lib/layout/sng-layout-sidebar-group-label.ts +53 -0
  101. package/src/lib/layout/sng-layout-sidebar-group.ts +41 -0
  102. package/src/lib/layout/sng-layout-sidebar-header.ts +54 -0
  103. package/src/lib/layout/sng-layout-sidebar-input.ts +112 -0
  104. package/src/lib/layout/sng-layout-sidebar-inset.ts +45 -0
  105. package/src/lib/layout/sng-layout-sidebar-menu-action.ts +84 -0
  106. package/src/lib/layout/sng-layout-sidebar-menu-badge.ts +47 -0
  107. package/src/lib/layout/sng-layout-sidebar-menu-button.ts +160 -0
  108. package/src/lib/layout/sng-layout-sidebar-menu-item.ts +40 -0
  109. package/src/lib/layout/sng-layout-sidebar-menu-skeleton.ts +71 -0
  110. package/src/lib/layout/sng-layout-sidebar-menu-sub-button.ts +142 -0
  111. package/src/lib/layout/sng-layout-sidebar-menu-sub-item.ts +38 -0
  112. package/src/lib/layout/sng-layout-sidebar-menu-sub.ts +48 -0
  113. package/src/lib/layout/sng-layout-sidebar-menu.ts +41 -0
  114. package/src/lib/layout/sng-layout-sidebar-provider.ts +189 -0
  115. package/src/lib/layout/sng-layout-sidebar-rail.ts +60 -0
  116. package/src/lib/layout/sng-layout-sidebar-separator.ts +38 -0
  117. package/src/lib/layout/sng-layout-sidebar-trigger.ts +97 -0
  118. package/src/lib/layout/sng-layout-sidebar.ts +254 -0
  119. package/src/lib/menu/cn.ts +6 -0
  120. package/src/lib/menu/index.ts +21 -0
  121. package/src/lib/menu/sng-context-trigger.ts +128 -0
  122. package/src/lib/menu/sng-menu-checkbox-item.ts +91 -0
  123. package/src/lib/menu/sng-menu-item.ts +80 -0
  124. package/src/lib/menu/sng-menu-label.ts +47 -0
  125. package/src/lib/menu/sng-menu-radio-group.ts +38 -0
  126. package/src/lib/menu/sng-menu-radio-item.ts +94 -0
  127. package/src/lib/menu/sng-menu-separator.ts +27 -0
  128. package/src/lib/menu/sng-menu-shortcut.ts +25 -0
  129. package/src/lib/menu/sng-menu-sub-content.ts +267 -0
  130. package/src/lib/menu/sng-menu-sub-trigger.ts +68 -0
  131. package/src/lib/menu/sng-menu-sub.ts +124 -0
  132. package/src/lib/menu/sng-menu-tokens.ts +52 -0
  133. package/src/lib/menu/sng-menu-trigger.ts +266 -0
  134. package/src/lib/menu/sng-menu.ts +100 -0
  135. package/src/lib/nav-menu/cn.ts +6 -0
  136. package/src/lib/nav-menu/index.ts +6 -0
  137. package/src/lib/nav-menu/sng-nav-menu-content.ts +72 -0
  138. package/src/lib/nav-menu/sng-nav-menu-item.ts +109 -0
  139. package/src/lib/nav-menu/sng-nav-menu-link.ts +54 -0
  140. package/src/lib/nav-menu/sng-nav-menu-list.ts +43 -0
  141. package/src/lib/nav-menu/sng-nav-menu-trigger.ts +98 -0
  142. package/src/lib/nav-menu/sng-nav-menu.ts +99 -0
  143. package/src/lib/otp-input/cn.ts +6 -0
  144. package/src/lib/otp-input/index.ts +14 -0
  145. package/src/lib/otp-input/sng-otp-input-group.ts +38 -0
  146. package/src/lib/otp-input/sng-otp-input-separator.ts +43 -0
  147. package/src/lib/otp-input/sng-otp-input-slot.ts +128 -0
  148. package/src/lib/otp-input/sng-otp-input-tokens.ts +20 -0
  149. package/src/lib/otp-input/sng-otp-input.ts +301 -0
  150. package/src/lib/popover/cn.ts +6 -0
  151. package/src/lib/popover/index.ts +3 -0
  152. package/src/lib/popover/sng-popover-content.ts +66 -0
  153. package/src/lib/popover/sng-popover-trigger.ts +44 -0
  154. package/src/lib/popover/sng-popover.ts +218 -0
  155. package/src/lib/preview-box/cn.ts +6 -0
  156. package/src/lib/preview-box/index.ts +5 -0
  157. package/src/lib/preview-box/sng-code-block.ts +80 -0
  158. package/src/lib/preview-box/sng-html-block.ts +79 -0
  159. package/src/lib/preview-box/sng-preview-block.ts +47 -0
  160. package/src/lib/preview-box/sng-preview-box.ts +369 -0
  161. package/src/lib/preview-box/sng-style-block.ts +80 -0
  162. package/src/lib/progress/cn.ts +6 -0
  163. package/src/lib/progress/index.ts +1 -0
  164. package/src/lib/progress/sng-progress.ts +65 -0
  165. package/src/lib/radio/cn.ts +6 -0
  166. package/src/lib/radio/index.ts +5 -0
  167. package/src/lib/radio/sng-radio-item.ts +100 -0
  168. package/src/lib/radio/sng-radio.ts +54 -0
  169. package/src/lib/resizable/cn.ts +6 -0
  170. package/src/lib/resizable/index.ts +3 -0
  171. package/src/lib/resizable/sng-resizable-group.ts +188 -0
  172. package/src/lib/resizable/sng-resizable-handle.ts +236 -0
  173. package/src/lib/resizable/sng-resizable-panel.ts +71 -0
  174. package/src/lib/search-input/cn.ts +6 -0
  175. package/src/lib/search-input/index.ts +16 -0
  176. package/src/lib/search-input/sng-search-input-context.ts +24 -0
  177. package/src/lib/search-input/sng-search-input-empty.ts +42 -0
  178. package/src/lib/search-input/sng-search-input-group.ts +69 -0
  179. package/src/lib/search-input/sng-search-input-item.ts +164 -0
  180. package/src/lib/search-input/sng-search-input-list.ts +34 -0
  181. package/src/lib/search-input/sng-search-input-separator.ts +32 -0
  182. package/src/lib/search-input/sng-search-input-shortcut.ts +29 -0
  183. package/src/lib/search-input/sng-search-input.ts +368 -0
  184. package/src/lib/select/cn.ts +6 -0
  185. package/src/lib/select/index.ts +7 -0
  186. package/src/lib/select/sng-select-content.ts +27 -0
  187. package/src/lib/select/sng-select-empty.ts +48 -0
  188. package/src/lib/select/sng-select-group.ts +29 -0
  189. package/src/lib/select/sng-select-item.ts +140 -0
  190. package/src/lib/select/sng-select-label.ts +29 -0
  191. package/src/lib/select/sng-select-separator.ts +29 -0
  192. package/src/lib/select/sng-select.ts +326 -0
  193. package/src/lib/separator/cn.ts +6 -0
  194. package/src/lib/separator/index.ts +1 -0
  195. package/src/lib/separator/sng-separator.ts +40 -0
  196. package/src/lib/skeleton/cn.ts +6 -0
  197. package/src/lib/skeleton/index.ts +1 -0
  198. package/src/lib/skeleton/sng-skeleton.ts +49 -0
  199. package/src/lib/slider/cn.ts +6 -0
  200. package/src/lib/slider/index.ts +2 -0
  201. package/src/lib/slider/sng-slider.ts +137 -0
  202. package/src/lib/sng-table/cn.ts +6 -0
  203. package/src/lib/sng-table/flex-render.ts +222 -0
  204. package/src/lib/sng-table/index.ts +85 -0
  205. package/src/lib/sng-table/sng-table-body.ts +59 -0
  206. package/src/lib/sng-table/sng-table-caption.ts +49 -0
  207. package/src/lib/sng-table/sng-table-cell.ts +62 -0
  208. package/src/lib/sng-table/sng-table-footer.ts +60 -0
  209. package/src/lib/sng-table/sng-table-head.ts +66 -0
  210. package/src/lib/sng-table/sng-table-header.ts +48 -0
  211. package/src/lib/sng-table/sng-table-pagination.ts +265 -0
  212. package/src/lib/sng-table/sng-table-row.ts +65 -0
  213. package/src/lib/sng-table/sng-table.ts +67 -0
  214. package/src/lib/sng-table-core/core/create-cell.ts +117 -0
  215. package/src/lib/sng-table-core/core/create-column.ts +266 -0
  216. package/src/lib/sng-table-core/core/create-header.ts +271 -0
  217. package/src/lib/sng-table-core/core/create-row.ts +293 -0
  218. package/src/lib/sng-table-core/core/create-table.ts +534 -0
  219. package/src/lib/sng-table-core/core/types.ts +1197 -0
  220. package/src/lib/sng-table-core/core/utils.ts +307 -0
  221. package/src/lib/sng-table-core/features/column-filtering.ts +376 -0
  222. package/src/lib/sng-table-core/features/column-ordering.ts +159 -0
  223. package/src/lib/sng-table-core/features/column-pinning.ts +219 -0
  224. package/src/lib/sng-table-core/features/column-sizing.ts +268 -0
  225. package/src/lib/sng-table-core/features/column-visibility.ts +128 -0
  226. package/src/lib/sng-table-core/features/faceting.ts +279 -0
  227. package/src/lib/sng-table-core/features/fuzzy-filtering.ts +188 -0
  228. package/src/lib/sng-table-core/features/global-filtering.ts +128 -0
  229. package/src/lib/sng-table-core/features/pagination.ts +179 -0
  230. package/src/lib/sng-table-core/features/row-expanding.ts +181 -0
  231. package/src/lib/sng-table-core/features/row-grouping.ts +235 -0
  232. package/src/lib/sng-table-core/features/row-pinning.ts +196 -0
  233. package/src/lib/sng-table-core/features/row-selection.ts +298 -0
  234. package/src/lib/sng-table-core/features/sorting.ts +425 -0
  235. package/src/lib/sng-table-core/features/virtualization.ts +298 -0
  236. package/src/lib/sng-table-core/index.ts +235 -0
  237. package/src/lib/sng-table-core/row-models/core-row-model.ts +256 -0
  238. package/src/lib/sng-table-core/row-models/expanded-row-model.ts +175 -0
  239. package/src/lib/sng-table-core/row-models/filtered-row-model.ts +307 -0
  240. package/src/lib/sng-table-core/row-models/grouped-row-model.ts +290 -0
  241. package/src/lib/sng-table-core/row-models/paginated-row-model.ts +135 -0
  242. package/src/lib/sng-table-core/row-models/sorted-row-model.ts +197 -0
  243. package/src/lib/styles/sng-themes.css +164 -0
  244. package/src/lib/switch/cn.ts +6 -0
  245. package/src/lib/switch/index.ts +1 -0
  246. package/src/lib/switch/sng-switch.ts +137 -0
  247. package/src/lib/tabs/cn.ts +6 -0
  248. package/src/lib/tabs/index.ts +4 -0
  249. package/src/lib/tabs/sng-tabs-content.ts +66 -0
  250. package/src/lib/tabs/sng-tabs-list.ts +55 -0
  251. package/src/lib/tabs/sng-tabs-trigger.ts +86 -0
  252. package/src/lib/tabs/sng-tabs.ts +83 -0
  253. package/src/lib/toast/cn.ts +6 -0
  254. package/src/lib/toast/index.ts +3 -0
  255. package/src/lib/toast/sng-toast.service.ts +258 -0
  256. package/src/lib/toast/sng-toast.ts +101 -0
  257. package/src/lib/toast/sng-toaster.ts +67 -0
  258. package/src/lib/toggle/cn.ts +6 -0
  259. package/src/lib/toggle/index.ts +6 -0
  260. package/src/lib/toggle/sng-toggle-group-item.ts +89 -0
  261. package/src/lib/toggle/sng-toggle-group.ts +85 -0
  262. package/src/lib/toggle/sng-toggle.ts +78 -0
  263. package/src/lib/toggle-group/index.ts +6 -0
  264. package/src/lib/tooltip/cn.ts +6 -0
  265. package/src/lib/tooltip/index.ts +5 -0
  266. package/src/lib/tooltip/sng-tooltip-content.ts +64 -0
  267. package/src/lib/tooltip/sng-tooltip.ts +216 -0
  268. package/src/public-api.ts +207 -0
  269. package/tsconfig.json +24 -0
  270. package/tsconfig.lib.json +17 -0
  271. package/tsconfig.lib.prod.json +11 -0
@@ -0,0 +1,266 @@
1
+ /**
2
+ * @fileoverview Column creation and management for sng-table-core
3
+ */
4
+
5
+ import {
6
+ Column,
7
+ ColumnDef,
8
+ Table,
9
+ AccessorFn,
10
+ ColumnPinningPosition,
11
+ } from './types';
12
+ import { flattenBy, getValueAtPath } from './utils';
13
+
14
+ // ============================================================================
15
+ // COLUMN CREATION
16
+ // ============================================================================
17
+
18
+ /**
19
+ * Create a column instance from a column definition
20
+ */
21
+ export function createColumn<TData, TValue = unknown>(
22
+ table: Table<TData>,
23
+ columnDef: ColumnDef<TData, TValue>,
24
+ depth: number,
25
+ parent?: Column<TData, unknown>,
26
+ path: number[] = []
27
+ ): Column<TData, TValue> {
28
+ // Resolve column ID
29
+ const id = resolveColumnId(columnDef, path);
30
+
31
+ // Merge with default column options
32
+ const resolvedDef = {
33
+ ...table.options.defaultColumn,
34
+ ...columnDef,
35
+ } as ColumnDef<TData, TValue>;
36
+
37
+ // Create the column instance
38
+ const column: Column<TData, TValue> = {
39
+ id,
40
+ columnDef: resolvedDef,
41
+ depth,
42
+ parent,
43
+ columns: [], // Will be populated for group columns
44
+
45
+ getLeafColumns: () => getLeafColumns(column as Column<TData, unknown>),
46
+ getFlatColumns: () => getFlatColumns(column as Column<TData, unknown>),
47
+ getAccessorFn: () => getAccessorFn(column),
48
+ getIndex: (position?: ColumnPinningPosition) =>
49
+ getColumnIndex(table, column as Column<TData, unknown>, position),
50
+ };
51
+
52
+ // Recursively create child columns if this is a group column
53
+ if (resolvedDef.columns?.length) {
54
+ column.columns = resolvedDef.columns.map((childDef, childIndex) =>
55
+ createColumn(table, childDef, depth + 1, column as Column<TData, unknown>, [...path, childIndex])
56
+ );
57
+ }
58
+
59
+ return column;
60
+ }
61
+
62
+ /**
63
+ * Resolve column ID from column definition
64
+ */
65
+ function resolveColumnId<TData, TValue>(
66
+ columnDef: ColumnDef<TData, TValue>,
67
+ path: number[]
68
+ ): string {
69
+ // Explicit ID takes precedence
70
+ if (columnDef.id) {
71
+ return columnDef.id;
72
+ }
73
+
74
+ // Use accessor key as ID
75
+ if (columnDef.accessorKey) {
76
+ return columnDef.accessorKey;
77
+ }
78
+
79
+ // Use accessor function name if available
80
+ if (columnDef.accessorFn) {
81
+ if (columnDef.accessorFn.name) {
82
+ return columnDef.accessorFn.name;
83
+ }
84
+ }
85
+
86
+ // No ID can be determined - this is an error for non-group columns
87
+ if (!columnDef.columns?.length) {
88
+ console.warn(
89
+ '[sng-table] Column definition requires either id, accessorKey, or accessorFn with a name:',
90
+ columnDef
91
+ );
92
+ return `column_${path.join('_') || '0'}`;
93
+ }
94
+
95
+ // Group columns without explicit ID get a deterministic one.
96
+ return `group_${path.join('_') || '0'}`;
97
+ }
98
+
99
+ /**
100
+ * Get accessor function for a column
101
+ */
102
+ function getAccessorFn<TData, TValue>(
103
+ column: Column<TData, TValue>
104
+ ): AccessorFn<TData, TValue> | undefined {
105
+ const { accessorFn, accessorKey } = column.columnDef;
106
+
107
+ // Direct accessor function
108
+ if (accessorFn) {
109
+ return accessorFn;
110
+ }
111
+
112
+ // Create accessor from key
113
+ if (accessorKey) {
114
+ // Handle nested keys (e.g., 'user.profile.name')
115
+ if (accessorKey.includes('.')) {
116
+ return (row: TData) =>
117
+ getValueAtPath(row as Record<string, unknown>, accessorKey) as TValue;
118
+ }
119
+
120
+ // Simple key access
121
+ return (row: TData) => (row as Record<string, unknown>)[accessorKey] as TValue;
122
+ }
123
+
124
+ // No accessor - this is a display/group column
125
+ return undefined;
126
+ }
127
+
128
+ // ============================================================================
129
+ // COLUMN TRAVERSAL
130
+ // ============================================================================
131
+
132
+ /**
133
+ * Get all leaf columns (columns without children) from this column's subtree
134
+ */
135
+ function getLeafColumns<TData>(
136
+ column: Column<TData, unknown>
137
+ ): Column<TData, unknown>[] {
138
+ if (!column.columns.length) {
139
+ return [column];
140
+ }
141
+
142
+ return column.columns.flatMap((child) => getLeafColumns(child));
143
+ }
144
+
145
+ /**
146
+ * Get all columns (including groups) flattened from this column's subtree
147
+ */
148
+ function getFlatColumns<TData>(
149
+ column: Column<TData, unknown>
150
+ ): Column<TData, unknown>[] {
151
+ return [column, ...column.columns.flatMap((child) => getFlatColumns(child))];
152
+ }
153
+
154
+ /**
155
+ * Get column index among siblings (considering pinning position if specified)
156
+ */
157
+ function getColumnIndex<TData>(
158
+ table: Table<TData>,
159
+ column: Column<TData, unknown>,
160
+ position?: ColumnPinningPosition
161
+ ): number {
162
+ // Get the appropriate column list based on position
163
+ let columns: Column<TData, unknown>[];
164
+
165
+ if (position === 'left') {
166
+ columns = table.getLeftVisibleLeafColumns?.() ?? [];
167
+ } else if (position === 'right') {
168
+ columns = table.getRightVisibleLeafColumns?.() ?? [];
169
+ } else if (position === false) {
170
+ columns = table.getCenterVisibleLeafColumns?.() ?? [];
171
+ } else {
172
+ // Default: use all visible leaf columns
173
+ columns = table.getVisibleLeafColumns?.() ?? table.getAllLeafColumns();
174
+ }
175
+
176
+ return columns.findIndex((c) => c.id === column.id);
177
+ }
178
+
179
+ // ============================================================================
180
+ // TABLE COLUMN HELPERS
181
+ // ============================================================================
182
+
183
+ /**
184
+ * Build all columns from column definitions
185
+ */
186
+ export function buildColumns<TData>(
187
+ table: Table<TData>,
188
+ columnDefs: ColumnDef<TData, unknown>[]
189
+ ): Column<TData, unknown>[] {
190
+ return columnDefs.map((columnDef, columnIndex) =>
191
+ createColumn(table, columnDef, 0, undefined, [columnIndex])
192
+ );
193
+ }
194
+
195
+ /**
196
+ * Get all columns from column tree
197
+ */
198
+ export function getAllColumns<TData>(
199
+ columns: Column<TData, unknown>[]
200
+ ): Column<TData, unknown>[] {
201
+ return columns;
202
+ }
203
+
204
+ /**
205
+ * Get all columns flattened (including nested)
206
+ */
207
+ export function getAllFlatColumns<TData>(
208
+ columns: Column<TData, unknown>[]
209
+ ): Column<TData, unknown>[] {
210
+ return flattenBy(columns, (col) =>
211
+ col.columns.length ? col.columns : undefined
212
+ );
213
+ }
214
+
215
+ /**
216
+ * Get all leaf columns (no children)
217
+ */
218
+ export function getAllLeafColumns<TData>(
219
+ columns: Column<TData, unknown>[]
220
+ ): Column<TData, unknown>[] {
221
+ return getAllFlatColumns(columns).filter((col) => !col.columns.length);
222
+ }
223
+
224
+ /**
225
+ * Get column by ID from flat columns
226
+ */
227
+ export function getColumnById<TData>(
228
+ flatColumns: Column<TData, unknown>[],
229
+ columnId: string
230
+ ): Column<TData, unknown> | undefined {
231
+ return flatColumns.find((col) => col.id === columnId);
232
+ }
233
+
234
+ /**
235
+ * Order columns by column order state
236
+ */
237
+ export function orderColumns<TData>(
238
+ columns: Column<TData, unknown>[],
239
+ columnOrder: string[]
240
+ ): Column<TData, unknown>[] {
241
+ if (!columnOrder.length) {
242
+ return columns;
243
+ }
244
+
245
+ // Create a map for fast lookup
246
+ const columnMap = new Map(columns.map((col) => [col.id, col]));
247
+
248
+ // Build ordered array
249
+ const ordered: Column<TData, unknown>[] = [];
250
+
251
+ // First, add columns in specified order
252
+ for (const id of columnOrder) {
253
+ const col = columnMap.get(id);
254
+ if (col) {
255
+ ordered.push(col);
256
+ columnMap.delete(id);
257
+ }
258
+ }
259
+
260
+ // Then, add any remaining columns not in order
261
+ for (const col of columnMap.values()) {
262
+ ordered.push(col);
263
+ }
264
+
265
+ return ordered;
266
+ }
@@ -0,0 +1,271 @@
1
+ /**
2
+ * @fileoverview Header creation and management for sng-table-core
3
+ */
4
+
5
+ import {
6
+ Header,
7
+ HeaderContext,
8
+ HeaderGroup,
9
+ Column,
10
+ Table,
11
+ ColumnPinningPosition,
12
+ } from './types';
13
+
14
+ // ============================================================================
15
+ // HEADER CREATION
16
+ // ============================================================================
17
+
18
+ /**
19
+ * Create a header instance for a column
20
+ */
21
+ export function createHeader<TData, TValue = unknown>(
22
+ table: Table<TData>,
23
+ column: Column<TData, TValue>,
24
+ options: {
25
+ id?: string;
26
+ isPlaceholder?: boolean;
27
+ depth: number;
28
+ index: number;
29
+ }
30
+ ): Header<TData, TValue> {
31
+ const id = options.id ?? `${column.id}_${options.depth}`;
32
+
33
+ // Create the header instance
34
+ const header: Header<TData, TValue> = {
35
+ id,
36
+ column,
37
+ depth: options.depth,
38
+ index: options.index,
39
+ isPlaceholder: options.isPlaceholder ?? false,
40
+ colSpan: 1, // Will be calculated
41
+ rowSpan: 1, // Will be calculated
42
+ subHeaders: [], // Will be populated for group columns
43
+
44
+ getContext: () => getHeaderContext(table, column, header),
45
+
46
+ getLeafHeaders: () => getLeafHeaders(header),
47
+ };
48
+
49
+ return header;
50
+ }
51
+
52
+ // ============================================================================
53
+ // HEADER CONTEXT
54
+ // ============================================================================
55
+
56
+ /**
57
+ * Create the header context for rendering
58
+ */
59
+ function getHeaderContext<TData, TValue>(
60
+ table: Table<TData>,
61
+ column: Column<TData, TValue>,
62
+ header: Header<TData, TValue>
63
+ ): HeaderContext<TData, TValue> {
64
+ return {
65
+ table,
66
+ column,
67
+ header,
68
+ };
69
+ }
70
+
71
+ // ============================================================================
72
+ // HEADER TRAVERSAL
73
+ // ============================================================================
74
+
75
+ /**
76
+ * Get all leaf headers from a header's subtree
77
+ */
78
+ function getLeafHeaders<TData>(
79
+ header: Header<TData, unknown>
80
+ ): Header<TData, unknown>[] {
81
+ if (!header.subHeaders.length) {
82
+ return [header];
83
+ }
84
+
85
+ return header.subHeaders.flatMap((subHeader) => getLeafHeaders(subHeader));
86
+ }
87
+
88
+ // ============================================================================
89
+ // HEADER GROUPS
90
+ // ============================================================================
91
+
92
+ /**
93
+ * Build header groups from columns
94
+ *
95
+ * Header groups represent rows of headers in the table head.
96
+ * For nested columns, this creates multiple rows with appropriate colSpan/rowSpan.
97
+ */
98
+ export function buildHeaderGroups<TData>(
99
+ table: Table<TData>,
100
+ columns: Column<TData, unknown>[],
101
+ _position?: ColumnPinningPosition
102
+ ): HeaderGroup<TData>[] {
103
+ // Find the maximum depth of nested columns
104
+ const maxDepth = getMaxColumnDepth(columns);
105
+
106
+ // Build header groups from bottom to top
107
+ const headerGroups: HeaderGroup<TData>[] = [];
108
+
109
+ // Build each depth level
110
+ for (let depth = 0; depth <= maxDepth; depth++) {
111
+ const headers: Header<TData, unknown>[] = [];
112
+
113
+ // Get columns that should appear at this depth
114
+ const columnsAtDepth = getColumnsForDepth(columns, depth, maxDepth);
115
+
116
+ columnsAtDepth.forEach((col, index) => {
117
+ const isLeaf = !col.columns.length;
118
+ const actualDepth = col.depth;
119
+
120
+ // Create header for this column
121
+ const header = createHeader(table, col, {
122
+ depth,
123
+ index,
124
+ isPlaceholder: depth < actualDepth, // Placeholder if we're above the column's actual depth
125
+ });
126
+
127
+ // Calculate spans
128
+ if (isLeaf) {
129
+ // Leaf columns span all remaining rows
130
+ header.rowSpan = maxDepth - actualDepth + 1;
131
+ header.colSpan = 1;
132
+ } else {
133
+ // Group columns span their children
134
+ header.rowSpan = 1;
135
+ header.colSpan = col.getLeafColumns().length;
136
+ }
137
+
138
+ // Build sub-headers for group columns
139
+ if (col.columns.length) {
140
+ header.subHeaders = col.columns.map((childCol, childIndex) =>
141
+ createHeader(table, childCol, {
142
+ depth: depth + 1,
143
+ index: childIndex,
144
+ isPlaceholder: false,
145
+ })
146
+ );
147
+ }
148
+
149
+ headers.push(header);
150
+ });
151
+
152
+ headerGroups.push({
153
+ id: `header_group_${depth}`,
154
+ depth,
155
+ headers,
156
+ });
157
+ }
158
+
159
+ return headerGroups;
160
+ }
161
+
162
+ /**
163
+ * Get the maximum depth of nested columns
164
+ */
165
+ function getMaxColumnDepth<TData>(columns: Column<TData, unknown>[]): number {
166
+ let maxDepth = 0;
167
+
168
+ function traverse(cols: Column<TData, unknown>[], currentDepth: number) {
169
+ for (const col of cols) {
170
+ maxDepth = Math.max(maxDepth, currentDepth);
171
+ if (col.columns.length) {
172
+ traverse(col.columns, currentDepth + 1);
173
+ }
174
+ }
175
+ }
176
+
177
+ traverse(columns, 0);
178
+ return maxDepth;
179
+ }
180
+
181
+ /**
182
+ * Group columns by their depth
183
+ * @internal Reserved for future use
184
+ */
185
+ function _groupColumnsByDepth<TData>(
186
+ columns: Column<TData, unknown>[]
187
+ ): Map<number, Column<TData, unknown>[]> {
188
+ const result = new Map<number, Column<TData, unknown>[]>();
189
+
190
+ function traverse(cols: Column<TData, unknown>[]) {
191
+ for (const col of cols) {
192
+ const depth = col.depth;
193
+ if (!result.has(depth)) {
194
+ result.set(depth, []);
195
+ }
196
+ result.get(depth)!.push(col);
197
+
198
+ if (col.columns.length) {
199
+ traverse(col.columns);
200
+ }
201
+ }
202
+ }
203
+
204
+ traverse(columns);
205
+ return result;
206
+ }
207
+
208
+ /**
209
+ * Get columns that should appear at a specific depth level
210
+ * Includes placeholders for columns that appear at deeper levels
211
+ */
212
+ function getColumnsForDepth<TData>(
213
+ columns: Column<TData, unknown>[],
214
+ targetDepth: number,
215
+ _maxDepth: number
216
+ ): Column<TData, unknown>[] {
217
+ const result: Column<TData, unknown>[] = [];
218
+
219
+ function traverse(cols: Column<TData, unknown>[], currentDepth: number) {
220
+ for (const col of cols) {
221
+ if (currentDepth === targetDepth) {
222
+ // This column should appear at this depth
223
+ result.push(col);
224
+ } else if (currentDepth < targetDepth && col.columns.length) {
225
+ // Recurse into children
226
+ traverse(col.columns, currentDepth + 1);
227
+ } else if (currentDepth < targetDepth && !col.columns.length) {
228
+ // Leaf column that starts earlier - need placeholder at this depth
229
+ // But we only show it once at its actual depth with rowSpan
230
+ // So skip here
231
+ }
232
+ }
233
+ }
234
+
235
+ traverse(columns, 0);
236
+ return result;
237
+ }
238
+
239
+ /**
240
+ * Build flattened header list from header groups
241
+ */
242
+ export function getFlatHeaders<TData>(
243
+ headerGroups: HeaderGroup<TData>[]
244
+ ): Header<TData, unknown>[] {
245
+ return headerGroups.flatMap((group) => group.headers);
246
+ }
247
+
248
+ /**
249
+ * Get leaf headers (headers without sub-headers)
250
+ */
251
+ export function getLeafHeadersFromGroups<TData>(
252
+ headerGroups: HeaderGroup<TData>[]
253
+ ): Header<TData, unknown>[] {
254
+ const lastGroup = headerGroups[headerGroups.length - 1];
255
+ return lastGroup?.headers ?? [];
256
+ }
257
+
258
+ /**
259
+ * Build footer groups (same structure as header groups, just from bottom of table)
260
+ */
261
+ export function buildFooterGroups<TData>(
262
+ table: Table<TData>,
263
+ columns: Column<TData, unknown>[]
264
+ ): HeaderGroup<TData>[] {
265
+ // Footers are built the same way as headers
266
+ // The difference is in how they're rendered (at bottom of table)
267
+ return buildHeaderGroups(table, columns).map((group) => ({
268
+ ...group,
269
+ id: `footer_group_${group.depth}`,
270
+ }));
271
+ }