@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,159 @@
1
+ /**
2
+ * @fileoverview Column ordering feature for sng-table-core
3
+ *
4
+ * Provides column reordering functionality via drag-and-drop or programmatic API.
5
+ */
6
+
7
+ import {
8
+ Column,
9
+ ColumnOrderState,
10
+ Updater,
11
+ functionalUpdate,
12
+ TableFeature,
13
+ ColumnPinningPosition,
14
+ } from '../core/types';
15
+
16
+ // ============================================================================
17
+ // COLUMN ORDERING FEATURE
18
+ // ============================================================================
19
+
20
+ /**
21
+ * Create the column ordering feature
22
+ */
23
+ export function createColumnOrderingFeature<TData>(): TableFeature<TData> {
24
+ return {
25
+ getInitialState: (initialState) => ({
26
+ columnOrder: initialState?.columnOrder ?? [],
27
+ }),
28
+
29
+ createTable: (table) => {
30
+ table.setColumnOrder = (updater: Updater<ColumnOrderState>) => {
31
+ table.setState((prev) => ({
32
+ ...prev,
33
+ columnOrder: functionalUpdate(updater, prev.columnOrder),
34
+ }));
35
+ table.options.onColumnOrderChange?.(updater);
36
+ };
37
+
38
+ table.resetColumnOrder = (defaultState?: boolean) => {
39
+ table.setColumnOrder?.(
40
+ defaultState ? [] : (table.initialState.columnOrder ?? [])
41
+ );
42
+ };
43
+ },
44
+
45
+ createColumn: (column, table) => {
46
+ // Add getCanDrag method to check if column can be reordered
47
+ column.getCanDrag = () => {
48
+ // Check column-level enableOrdering (defaults to true if not specified)
49
+ const columnDef = column.columnDef;
50
+ if (columnDef.enableOrdering === false) {
51
+ return false;
52
+ }
53
+ // Check table-level enableColumnOrdering (defaults to true)
54
+ if (table.options.enableColumnOrdering === false) {
55
+ return false;
56
+ }
57
+ return true;
58
+ };
59
+
60
+ // Extend existing getIndex to respect column order
61
+ const originalGetIndex = column.getIndex;
62
+
63
+ column.getIndex = (position?: ColumnPinningPosition) => {
64
+ const { columnOrder } = table.getState();
65
+
66
+ // If no explicit order, use original behavior
67
+ if (!columnOrder.length) {
68
+ return originalGetIndex(position);
69
+ }
70
+
71
+ // Get ordered columns
72
+ const orderedColumns = orderColumnsByState(
73
+ table.getAllLeafColumns(),
74
+ columnOrder
75
+ );
76
+
77
+ // Filter by position if specified
78
+ let columns = orderedColumns;
79
+ if (position === 'left') {
80
+ columns = orderedColumns.filter((c) => c.getIsPinned?.() === 'left');
81
+ } else if (position === 'right') {
82
+ columns = orderedColumns.filter((c) => c.getIsPinned?.() === 'right');
83
+ } else if (position === false) {
84
+ columns = orderedColumns.filter((c) => !c.getIsPinned?.());
85
+ }
86
+
87
+ return columns.findIndex((c) => c.id === column.id);
88
+ };
89
+ },
90
+ };
91
+ }
92
+
93
+ // ============================================================================
94
+ // HELPERS
95
+ // ============================================================================
96
+
97
+ /**
98
+ * Order columns by column order state
99
+ */
100
+ function orderColumnsByState<TData>(
101
+ columns: Column<TData, unknown>[],
102
+ columnOrder: string[]
103
+ ): Column<TData, unknown>[] {
104
+ if (!columnOrder.length) return columns;
105
+
106
+ const columnMap = new Map(columns.map((col) => [col.id, col]));
107
+ const ordered: Column<TData, unknown>[] = [];
108
+
109
+ // Add columns in specified order
110
+ for (const id of columnOrder) {
111
+ const col = columnMap.get(id);
112
+ if (col) {
113
+ ordered.push(col);
114
+ columnMap.delete(id);
115
+ }
116
+ }
117
+
118
+ // Add remaining columns
119
+ for (const col of columnMap.values()) {
120
+ ordered.push(col);
121
+ }
122
+
123
+ return ordered;
124
+ }
125
+
126
+ /**
127
+ * Move a column to a new position in the order
128
+ */
129
+ export function moveColumn(
130
+ columnOrder: ColumnOrderState,
131
+ columnId: string,
132
+ targetIndex: number
133
+ ): ColumnOrderState {
134
+ const newOrder = columnOrder.filter((id) => id !== columnId);
135
+ newOrder.splice(targetIndex, 0, columnId);
136
+ return newOrder;
137
+ }
138
+
139
+ /**
140
+ * Swap two columns in the order
141
+ */
142
+ export function swapColumns(
143
+ columnOrder: ColumnOrderState,
144
+ columnIdA: string,
145
+ columnIdB: string
146
+ ): ColumnOrderState {
147
+ const newOrder = [...columnOrder];
148
+ const indexA = newOrder.indexOf(columnIdA);
149
+ const indexB = newOrder.indexOf(columnIdB);
150
+
151
+ if (indexA === -1 || indexB === -1) {
152
+ return columnOrder;
153
+ }
154
+
155
+ newOrder[indexA] = columnIdB;
156
+ newOrder[indexB] = columnIdA;
157
+
158
+ return newOrder;
159
+ }
@@ -0,0 +1,219 @@
1
+ /**
2
+ * @fileoverview Column pinning feature for sng-table-core
3
+ *
4
+ * Provides column pinning (left/right sticky columns) functionality.
5
+ */
6
+
7
+ import {
8
+ Column,
9
+ HeaderGroup,
10
+ ColumnPinningState,
11
+ ColumnPinningPosition,
12
+ Updater,
13
+ functionalUpdate,
14
+ TableFeature,
15
+ } from '../core/types';
16
+
17
+ // ============================================================================
18
+ // COLUMN PINNING FEATURE
19
+ // ============================================================================
20
+
21
+ /**
22
+ * Create the column pinning feature
23
+ */
24
+ export function createColumnPinningFeature<TData>(): TableFeature<TData> {
25
+ return {
26
+ getInitialState: (initialState) => ({
27
+ columnPinning: initialState?.columnPinning ?? {
28
+ left: [],
29
+ right: [],
30
+ },
31
+ }),
32
+
33
+ getDefaultOptions: (_table) => ({
34
+ enablePinning: true,
35
+ enableColumnPinning: true,
36
+ }),
37
+
38
+ createTable: (table) => {
39
+ table.setColumnPinning = (updater: Updater<ColumnPinningState>) => {
40
+ table.setState((prev) => ({
41
+ ...prev,
42
+ columnPinning: functionalUpdate(updater, prev.columnPinning),
43
+ }));
44
+ table.options.onColumnPinningChange?.(updater);
45
+ };
46
+
47
+ table.resetColumnPinning = (defaultState?: boolean) => {
48
+ table.setColumnPinning?.(
49
+ defaultState
50
+ ? { left: [], right: [] }
51
+ : (table.initialState.columnPinning ?? { left: [], right: [] })
52
+ );
53
+ };
54
+
55
+ table.getIsSomeColumnsPinned = (position?: ColumnPinningPosition) => {
56
+ const { columnPinning } = table.getState();
57
+
58
+ if (position === 'left') {
59
+ return (columnPinning.left?.length ?? 0) > 0;
60
+ }
61
+ if (position === 'right') {
62
+ return (columnPinning.right?.length ?? 0) > 0;
63
+ }
64
+
65
+ return (
66
+ (columnPinning.left?.length ?? 0) > 0 ||
67
+ (columnPinning.right?.length ?? 0) > 0
68
+ );
69
+ };
70
+
71
+ table.getLeftLeafColumns = () => {
72
+ const { columnPinning } = table.getState();
73
+ const leftIds = columnPinning.left ?? [];
74
+ return table.getAllLeafColumns().filter((col) => leftIds.includes(col.id));
75
+ };
76
+
77
+ table.getRightLeafColumns = () => {
78
+ const { columnPinning } = table.getState();
79
+ const rightIds = columnPinning.right ?? [];
80
+ return table.getAllLeafColumns().filter((col) => rightIds.includes(col.id));
81
+ };
82
+
83
+ table.getCenterLeafColumns = () => {
84
+ const { columnPinning } = table.getState();
85
+ const pinnedIds = [
86
+ ...(columnPinning.left ?? []),
87
+ ...(columnPinning.right ?? []),
88
+ ];
89
+ return table.getAllLeafColumns().filter((col) => !pinnedIds.includes(col.id));
90
+ };
91
+
92
+ table.getLeftVisibleLeafColumns = () => {
93
+ return table.getLeftLeafColumns?.()?.filter(
94
+ (col) => col.getIsVisible?.() ?? true
95
+ ) ?? [];
96
+ };
97
+
98
+ table.getRightVisibleLeafColumns = () => {
99
+ return table.getRightLeafColumns?.()?.filter(
100
+ (col) => col.getIsVisible?.() ?? true
101
+ ) ?? [];
102
+ };
103
+
104
+ table.getCenterVisibleLeafColumns = () => {
105
+ return table.getCenterLeafColumns?.()?.filter(
106
+ (col) => col.getIsVisible?.() ?? true
107
+ ) ?? [];
108
+ };
109
+
110
+ // Header groups by position
111
+ table.getLeftHeaderGroups = () => {
112
+ const leftColumns = table.getLeftLeafColumns?.() ?? [];
113
+ return filterHeaderGroupsByColumns(table.getHeaderGroups(), leftColumns);
114
+ };
115
+
116
+ table.getRightHeaderGroups = () => {
117
+ const rightColumns = table.getRightLeafColumns?.() ?? [];
118
+ return filterHeaderGroupsByColumns(table.getHeaderGroups(), rightColumns);
119
+ };
120
+
121
+ table.getCenterHeaderGroups = () => {
122
+ const centerColumns = table.getCenterLeafColumns?.() ?? [];
123
+ return filterHeaderGroupsByColumns(table.getHeaderGroups(), centerColumns);
124
+ };
125
+
126
+ // Flat headers by position
127
+ table.getLeftFlatHeaders = () => {
128
+ return table.getLeftHeaderGroups?.()?.flatMap((g) => g.headers) ?? [];
129
+ };
130
+
131
+ table.getRightFlatHeaders = () => {
132
+ return table.getRightHeaderGroups?.()?.flatMap((g) => g.headers) ?? [];
133
+ };
134
+
135
+ table.getCenterFlatHeaders = () => {
136
+ return table.getCenterHeaderGroups?.()?.flatMap((g) => g.headers) ?? [];
137
+ };
138
+ },
139
+
140
+ createColumn: (column, table) => {
141
+ column.getCanPin = () => {
142
+ const { enablePinning, enableColumnPinning } = table.options;
143
+ const { enablePinning: columnEnablePinning } = column.columnDef;
144
+
145
+ if (columnEnablePinning === false) return false;
146
+ if (enableColumnPinning === false) return false;
147
+ if (enablePinning === false) return false;
148
+
149
+ return true;
150
+ };
151
+
152
+ column.getIsPinned = (): ColumnPinningPosition => {
153
+ const { columnPinning } = table.getState();
154
+
155
+ if (columnPinning.left?.includes(column.id)) {
156
+ return 'left';
157
+ }
158
+ if (columnPinning.right?.includes(column.id)) {
159
+ return 'right';
160
+ }
161
+
162
+ return false;
163
+ };
164
+
165
+ column.getPinnedIndex = () => {
166
+ const position = column.getIsPinned?.();
167
+ if (!position) return -1;
168
+
169
+ const { columnPinning } = table.getState();
170
+ const pinnedIds = position === 'left'
171
+ ? columnPinning.left ?? []
172
+ : columnPinning.right ?? [];
173
+
174
+ return pinnedIds.indexOf(column.id);
175
+ };
176
+
177
+ column.pin = (position: ColumnPinningPosition) => {
178
+ if (!column.getCanPin?.()) return;
179
+
180
+ table.setColumnPinning?.((prev) => {
181
+ const left = (prev.left ?? []).filter((id) => id !== column.id);
182
+ const right = (prev.right ?? []).filter((id) => id !== column.id);
183
+
184
+ if (position === 'left') {
185
+ left.push(column.id);
186
+ } else if (position === 'right') {
187
+ right.push(column.id);
188
+ }
189
+ // If position is false, column is unpinned (already removed above)
190
+
191
+ return { left, right };
192
+ });
193
+ };
194
+ },
195
+ };
196
+ }
197
+
198
+ // ============================================================================
199
+ // HELPERS
200
+ // ============================================================================
201
+
202
+ /**
203
+ * Filter header groups to only include headers for specified columns
204
+ */
205
+ function filterHeaderGroupsByColumns<TData>(
206
+ headerGroups: HeaderGroup<TData>[],
207
+ columns: Column<TData, unknown>[]
208
+ ): HeaderGroup<TData>[] {
209
+ const columnIds = new Set(columns.map((col) => col.id));
210
+
211
+ return headerGroups.map((group) => ({
212
+ ...group,
213
+ headers: group.headers.filter((header) => {
214
+ // Check if this header or any of its leaf columns are in the set
215
+ const leafColumns = header.column.getLeafColumns();
216
+ return leafColumns.some((col) => columnIds.has(col.id));
217
+ }),
218
+ })).filter((group) => group.headers.length > 0);
219
+ }
@@ -0,0 +1,268 @@
1
+ /**
2
+ * @fileoverview Column sizing feature for sng-table-core
3
+ *
4
+ * Provides column resizing functionality.
5
+ */
6
+
7
+ import {
8
+ ColumnSizingState,
9
+ ColumnSizingInfoState,
10
+ Updater,
11
+ functionalUpdate,
12
+ TableFeature,
13
+ ColumnPinningPosition,
14
+ } from '../core/types';
15
+
16
+ // ============================================================================
17
+ // DEFAULT VALUES
18
+ // ============================================================================
19
+
20
+ const DEFAULT_COLUMN_SIZE = 150;
21
+ const DEFAULT_COLUMN_MIN_SIZE = 20;
22
+ const DEFAULT_COLUMN_MAX_SIZE = Number.MAX_SAFE_INTEGER;
23
+
24
+ // ============================================================================
25
+ // COLUMN SIZING FEATURE
26
+ // ============================================================================
27
+
28
+ /**
29
+ * Create the column sizing feature
30
+ */
31
+ export function createColumnSizingFeature<TData>(): TableFeature<TData> {
32
+ return {
33
+ getInitialState: (initialState) => ({
34
+ columnSizing: initialState?.columnSizing ?? {},
35
+ columnSizingInfo: initialState?.columnSizingInfo ?? {
36
+ startOffset: null,
37
+ startSize: null,
38
+ deltaOffset: null,
39
+ deltaPercentage: null,
40
+ columnSizingStart: [],
41
+ isResizingColumn: false,
42
+ },
43
+ }),
44
+
45
+ getDefaultOptions: (_table) => ({
46
+ enableColumnResizing: true,
47
+ columnResizeMode: 'onEnd' as const,
48
+ columnResizeDirection: 'ltr' as const,
49
+ }),
50
+
51
+ createTable: (table) => {
52
+ table.setColumnSizing = (updater: Updater<ColumnSizingState>) => {
53
+ table.setState((prev) => ({
54
+ ...prev,
55
+ columnSizing: functionalUpdate(updater, prev.columnSizing),
56
+ }));
57
+ table.options.onColumnSizingChange?.(updater);
58
+ };
59
+
60
+ table.setColumnSizingInfo = (updater: Updater<ColumnSizingInfoState>) => {
61
+ table.setState((prev) => ({
62
+ ...prev,
63
+ columnSizingInfo: functionalUpdate(updater, prev.columnSizingInfo),
64
+ }));
65
+ table.options.onColumnSizingInfoChange?.(updater);
66
+ };
67
+
68
+ table.resetColumnSizing = (defaultState?: boolean) => {
69
+ table.setColumnSizing?.(
70
+ defaultState ? {} : (table.initialState.columnSizing ?? {})
71
+ );
72
+ };
73
+
74
+ table.getTotalSize = () => {
75
+ return table.getAllLeafColumns().reduce(
76
+ (sum, column) => sum + (column.getSize?.() ?? DEFAULT_COLUMN_SIZE),
77
+ 0
78
+ );
79
+ };
80
+
81
+ table.getLeftTotalSize = () => {
82
+ const leftColumns = table.getLeftVisibleLeafColumns?.() ?? [];
83
+ return leftColumns.reduce(
84
+ (sum, column) => sum + (column.getSize?.() ?? DEFAULT_COLUMN_SIZE),
85
+ 0
86
+ );
87
+ };
88
+
89
+ table.getCenterTotalSize = () => {
90
+ const centerColumns = table.getCenterVisibleLeafColumns?.() ?? table.getVisibleLeafColumns?.() ?? [];
91
+ return centerColumns.reduce(
92
+ (sum, column) => sum + (column.getSize?.() ?? DEFAULT_COLUMN_SIZE),
93
+ 0
94
+ );
95
+ };
96
+
97
+ table.getRightTotalSize = () => {
98
+ const rightColumns = table.getRightVisibleLeafColumns?.() ?? [];
99
+ return rightColumns.reduce(
100
+ (sum, column) => sum + (column.getSize?.() ?? DEFAULT_COLUMN_SIZE),
101
+ 0
102
+ );
103
+ };
104
+ },
105
+
106
+ createColumn: (column, table) => {
107
+ column.getCanResize = () => {
108
+ const { enableColumnResizing } = table.options;
109
+ const { enableResizing } = column.columnDef;
110
+
111
+ if (enableResizing === false) return false;
112
+ if (enableColumnResizing === false) return false;
113
+
114
+ return true;
115
+ };
116
+
117
+ column.getIsResizing = () => {
118
+ const { columnSizingInfo } = table.getState();
119
+ return columnSizingInfo.isResizingColumn === column.id;
120
+ };
121
+
122
+ column.getSize = () => {
123
+ const { columnSizing } = table.getState();
124
+ const { size, minSize, maxSize } = column.columnDef;
125
+
126
+ const configSize = size ?? DEFAULT_COLUMN_SIZE;
127
+ const stateSize = columnSizing[column.id];
128
+
129
+ const currentSize = stateSize ?? configSize;
130
+ const min = minSize ?? DEFAULT_COLUMN_MIN_SIZE;
131
+ const max = maxSize ?? DEFAULT_COLUMN_MAX_SIZE;
132
+
133
+ return Math.min(Math.max(currentSize, min), max);
134
+ };
135
+
136
+ column.getStart = (position?: ColumnPinningPosition) => {
137
+ const columns = position === 'left'
138
+ ? table.getLeftVisibleLeafColumns?.() ?? []
139
+ : position === 'right'
140
+ ? table.getRightVisibleLeafColumns?.() ?? []
141
+ : table.getVisibleLeafColumns?.() ?? [];
142
+
143
+ const index = columns.findIndex((c) => c.id === column.id);
144
+ if (index === -1) return 0;
145
+
146
+ return columns
147
+ .slice(0, index)
148
+ .reduce((sum, col) => sum + (col.getSize?.() ?? DEFAULT_COLUMN_SIZE), 0);
149
+ };
150
+
151
+ column.resetSize = () => {
152
+ table.setColumnSizing?.((prev) => {
153
+ const { [column.id]: _, ...rest } = prev;
154
+ return rest;
155
+ });
156
+ };
157
+ },
158
+
159
+ createHeader: (header, table) => {
160
+ header.getSize = () => {
161
+ let sum = 0;
162
+ const leafHeaders = header.getLeafHeaders();
163
+
164
+ for (const leafHeader of leafHeaders) {
165
+ sum += leafHeader.column.getSize?.() ?? DEFAULT_COLUMN_SIZE;
166
+ }
167
+
168
+ return sum;
169
+ };
170
+
171
+ header.getStart = (position?: ColumnPinningPosition) => {
172
+ if (header.isPlaceholder) return 0;
173
+ return header.column.getStart?.(position) ?? 0;
174
+ };
175
+
176
+ header.getResizeHandler = () => {
177
+ if (!header.column.getCanResize?.()) {
178
+ return () => { /* no-op - column cannot be resized */ };
179
+ }
180
+
181
+ return (event: unknown) => {
182
+ // This is a simplified version - full implementation would handle
183
+ // mouse/touch events for drag resizing
184
+ const mouseEvent = event as MouseEvent;
185
+
186
+ const startX = mouseEvent.clientX;
187
+ const startSize = header.column.getSize?.() ?? DEFAULT_COLUMN_SIZE;
188
+
189
+ // Start resizing
190
+ table.setColumnSizingInfo?.({
191
+ startOffset: startX,
192
+ startSize,
193
+ deltaOffset: 0,
194
+ deltaPercentage: 0,
195
+ columnSizingStart: [[header.column.id, startSize]],
196
+ isResizingColumn: header.column.id,
197
+ });
198
+
199
+ const doc = globalThis.document;
200
+ if (!doc) {
201
+ return;
202
+ }
203
+
204
+ const onMove = (e: MouseEvent) => {
205
+ const deltaX = e.clientX - startX;
206
+ const { columnResizeDirection } = table.options;
207
+ const delta = columnResizeDirection === 'rtl' ? -deltaX : deltaX;
208
+
209
+ const newSize = Math.max(
210
+ header.column.columnDef.minSize ?? DEFAULT_COLUMN_MIN_SIZE,
211
+ Math.min(
212
+ header.column.columnDef.maxSize ?? DEFAULT_COLUMN_MAX_SIZE,
213
+ startSize + delta
214
+ )
215
+ );
216
+
217
+ if (table.options.columnResizeMode === 'onChange') {
218
+ table.setColumnSizing?.((prev) => ({
219
+ ...prev,
220
+ [header.column.id]: newSize,
221
+ }));
222
+ }
223
+
224
+ table.setColumnSizingInfo?.((prev) => ({
225
+ ...prev,
226
+ deltaOffset: delta,
227
+ deltaPercentage: delta / startSize,
228
+ }));
229
+ };
230
+
231
+ const onEnd = () => {
232
+ const { deltaOffset } = table.getState().columnSizingInfo;
233
+
234
+ if (table.options.columnResizeMode === 'onEnd' && deltaOffset) {
235
+ const newSize = Math.max(
236
+ header.column.columnDef.minSize ?? DEFAULT_COLUMN_MIN_SIZE,
237
+ Math.min(
238
+ header.column.columnDef.maxSize ?? DEFAULT_COLUMN_MAX_SIZE,
239
+ startSize + deltaOffset
240
+ )
241
+ );
242
+
243
+ table.setColumnSizing?.((prev) => ({
244
+ ...prev,
245
+ [header.column.id]: newSize,
246
+ }));
247
+ }
248
+
249
+ table.setColumnSizingInfo?.({
250
+ startOffset: null,
251
+ startSize: null,
252
+ deltaOffset: null,
253
+ deltaPercentage: null,
254
+ columnSizingStart: [],
255
+ isResizingColumn: false,
256
+ });
257
+
258
+ doc.removeEventListener('mousemove', onMove);
259
+ doc.removeEventListener('mouseup', onEnd);
260
+ };
261
+
262
+ doc.addEventListener('mousemove', onMove);
263
+ doc.addEventListener('mouseup', onEnd);
264
+ };
265
+ };
266
+ },
267
+ };
268
+ }