@siyahi/ui 0.0.0-stage → 0.1.0-next.4

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 (192) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -2
  3. package/THIRD_PARTY_NOTICES.md +51 -0
  4. package/dist/components/registry/dashboard-01/section-cards.d.ts +17 -0
  5. package/dist/components/registry/dashboard-01/section-cards.js +39 -0
  6. package/dist/components/registry/dashboardcn/bar-list.d.ts +24 -0
  7. package/dist/components/registry/dashboardcn/bar-list.js +71 -0
  8. package/dist/components/registry/heatmap/calendar-heatmap.d.ts +134 -0
  9. package/dist/components/registry/heatmap/calendar-heatmap.js +497 -0
  10. package/dist/components/registry/heatmap/status-heatmap.d.ts +120 -0
  11. package/dist/components/registry/heatmap/status-heatmap.js +330 -0
  12. package/dist/components/registry/heatmap/weekday-heatmap.d.ts +145 -0
  13. package/dist/components/registry/heatmap/weekday-heatmap.js +489 -0
  14. package/dist/components/registry/niko-table/components/data-table-column-actions.d.ts +26 -0
  15. package/dist/components/registry/niko-table/components/data-table-column-actions.js +32 -0
  16. package/dist/components/registry/niko-table/components/data-table-column-filter.d.ts +13 -0
  17. package/dist/components/registry/niko-table/components/data-table-column-filter.js +17 -0
  18. package/dist/components/registry/niko-table/components/data-table-column-header.d.ts +30 -0
  19. package/dist/components/registry/niko-table/components/data-table-column-header.js +35 -0
  20. package/dist/components/registry/niko-table/components/data-table-column-sort.d.ts +21 -0
  21. package/dist/components/registry/niko-table/components/data-table-column-sort.js +36 -0
  22. package/dist/components/registry/niko-table/components/data-table-column-title.d.ts +12 -0
  23. package/dist/components/registry/niko-table/components/data-table-column-title.js +18 -0
  24. package/dist/components/registry/niko-table/components/data-table-empty-state.d.ts +108 -0
  25. package/dist/components/registry/niko-table/components/data-table-empty-state.js +148 -0
  26. package/dist/components/registry/niko-table/components/data-table-faceted-filter.d.ts +46 -0
  27. package/dist/components/registry/niko-table/components/data-table-faceted-filter.js +133 -0
  28. package/dist/components/registry/niko-table/components/data-table-grouped-rows.d.ts +111 -0
  29. package/dist/components/registry/niko-table/components/data-table-grouped-rows.js +77 -0
  30. package/dist/components/registry/niko-table/components/data-table-row-context-menu-slot.d.ts +65 -0
  31. package/dist/components/registry/niko-table/components/data-table-row-context-menu-slot.js +65 -0
  32. package/dist/components/registry/niko-table/components/data-table-row-context-menu.d.ts +50 -0
  33. package/dist/components/registry/niko-table/components/data-table-row-context-menu.js +42 -0
  34. package/dist/components/registry/niko-table/components/data-table-row-menu.d.ts +51 -0
  35. package/dist/components/registry/niko-table/components/data-table-row-menu.js +65 -0
  36. package/dist/components/registry/niko-table/components/data-table-toolbar-section.d.ts +7 -0
  37. package/dist/components/registry/niko-table/components/data-table-toolbar-section.js +63 -0
  38. package/dist/components/registry/niko-table/config/data-table.d.ts +156 -0
  39. package/dist/components/registry/niko-table/config/data-table.js +279 -0
  40. package/dist/components/registry/niko-table/config/feature-detection.d.ts +39 -0
  41. package/dist/components/registry/niko-table/config/feature-detection.js +174 -0
  42. package/dist/components/registry/niko-table/core/data-table-context.d.ts +157 -0
  43. package/dist/components/registry/niko-table/core/data-table-context.js +326 -0
  44. package/dist/components/registry/niko-table/core/data-table-error-boundary.d.ts +85 -0
  45. package/dist/components/registry/niko-table/core/data-table-error-boundary.js +116 -0
  46. package/dist/components/registry/niko-table/core/data-table-grouped-row.d.ts +49 -0
  47. package/dist/components/registry/niko-table/core/data-table-grouped-row.js +112 -0
  48. package/dist/components/registry/niko-table/core/data-table-root.d.ts +62 -0
  49. package/dist/components/registry/niko-table/core/data-table-root.js +406 -0
  50. package/dist/components/registry/niko-table/core/data-table-structure.d.ts +153 -0
  51. package/dist/components/registry/niko-table/core/data-table-structure.js +370 -0
  52. package/dist/components/registry/niko-table/core/data-table.d.ts +30 -0
  53. package/dist/components/registry/niko-table/core/data-table.js +126 -0
  54. package/dist/components/registry/niko-table/filters/table-column-actions.d.ts +49 -0
  55. package/dist/components/registry/niko-table/filters/table-column-actions.js +48 -0
  56. package/dist/components/registry/niko-table/filters/table-column-sort.d.ts +51 -0
  57. package/dist/components/registry/niko-table/filters/table-column-sort.js +184 -0
  58. package/dist/components/registry/niko-table/filters/table-column-title.d.ts +17 -0
  59. package/dist/components/registry/niko-table/filters/table-column-title.js +20 -0
  60. package/dist/components/registry/niko-table/filters/table-faceted-filter.d.ts +40 -0
  61. package/dist/components/registry/niko-table/filters/table-faceted-filter.js +210 -0
  62. package/dist/components/registry/niko-table/hooks/use-derived-column-title.d.ts +29 -0
  63. package/dist/components/registry/niko-table/hooks/use-derived-column-title.js +40 -0
  64. package/dist/components/registry/niko-table/hooks/use-generated-options.d.ts +49 -0
  65. package/dist/components/registry/niko-table/hooks/use-generated-options.js +144 -0
  66. package/dist/components/registry/niko-table/lib/build-faceted-options.d.ts +59 -0
  67. package/dist/components/registry/niko-table/lib/build-faceted-options.js +82 -0
  68. package/dist/components/registry/niko-table/lib/column-resize-handle.d.ts +33 -0
  69. package/dist/components/registry/niko-table/lib/column-resize-handle.js +175 -0
  70. package/dist/components/registry/niko-table/lib/constants.d.ts +128 -0
  71. package/dist/components/registry/niko-table/lib/constants.js +123 -0
  72. package/dist/components/registry/niko-table/lib/create-scroll-handler.d.ts +35 -0
  73. package/dist/components/registry/niko-table/lib/create-scroll-handler.js +63 -0
  74. package/dist/components/registry/niko-table/lib/data-table-features.d.ts +218 -0
  75. package/dist/components/registry/niko-table/lib/data-table-features.js +127 -0
  76. package/dist/components/registry/niko-table/lib/filter-functions.d.ts +47 -0
  77. package/dist/components/registry/niko-table/lib/filter-functions.js +309 -0
  78. package/dist/components/registry/niko-table/lib/filter-rows.d.ts +14 -0
  79. package/dist/components/registry/niko-table/lib/filter-rows.js +28 -0
  80. package/dist/components/registry/niko-table/lib/flex-columns.d.ts +48 -0
  81. package/dist/components/registry/niko-table/lib/flex-columns.js +65 -0
  82. package/dist/components/registry/niko-table/lib/format.d.ts +33 -0
  83. package/dist/components/registry/niko-table/lib/format.js +99 -0
  84. package/dist/components/registry/niko-table/lib/render-cell-content.d.ts +17 -0
  85. package/dist/components/registry/niko-table/lib/render-cell-content.js +65 -0
  86. package/dist/components/registry/niko-table/lib/row-click.d.ts +67 -0
  87. package/dist/components/registry/niko-table/lib/row-click.js +28 -0
  88. package/dist/components/registry/niko-table/lib/styles.d.ts +6 -0
  89. package/dist/components/registry/niko-table/lib/styles.js +24 -0
  90. package/dist/components/registry/niko-table/lib/use-header-min-widths.d.ts +17 -0
  91. package/dist/components/registry/niko-table/lib/use-header-min-widths.js +82 -0
  92. package/dist/components/registry/niko-table/types/index.d.ts +141 -0
  93. package/dist/components/registry/niko-table/types/index.js +2 -0
  94. package/dist/components/registry/niko-table/ui/table.d.ts +13 -0
  95. package/dist/components/registry/niko-table/ui/table.js +74 -0
  96. package/dist/components/registry/sidebar-07/app-sidebar.d.ts +18 -0
  97. package/dist/components/registry/sidebar-07/app-sidebar.js +26 -0
  98. package/dist/components/registry/sidebar-07/nav-main.d.ts +15 -0
  99. package/dist/components/registry/sidebar-07/nav-main.js +25 -0
  100. package/dist/components/registry/sidebar-07/nav-projects.d.ts +9 -0
  101. package/dist/components/registry/sidebar-07/nav-projects.js +43 -0
  102. package/dist/components/registry/sidebar-07/nav-user.d.ts +9 -0
  103. package/dist/components/registry/sidebar-07/nav-user.js +70 -0
  104. package/dist/components/registry/sidebar-07/team-switcher.d.ts +10 -0
  105. package/dist/components/registry/sidebar-07/team-switcher.js +70 -0
  106. package/dist/components/siyahi/delta.d.ts +10 -0
  107. package/dist/components/siyahi/delta.js +27 -0
  108. package/dist/components/siyahi/local-time.d.ts +7 -0
  109. package/dist/components/siyahi/local-time.js +22 -0
  110. package/dist/components/siyahi/price-chart.d.ts +12 -0
  111. package/dist/components/siyahi/price-chart.js +80 -0
  112. package/dist/components/siyahi/siyahi-provider.d.ts +12 -0
  113. package/dist/components/siyahi/siyahi-provider.js +31 -0
  114. package/dist/components/siyahi/treemap.d.ts +14 -0
  115. package/dist/components/siyahi/treemap.js +58 -0
  116. package/dist/components/ui/alert.d.ts +12 -0
  117. package/dist/components/ui/alert.js +43 -0
  118. package/dist/components/ui/avatar.d.ts +13 -0
  119. package/dist/components/ui/avatar.js +50 -0
  120. package/dist/components/ui/badge.d.ts +9 -0
  121. package/dist/components/ui/badge.js +29 -0
  122. package/dist/components/ui/breadcrumb.d.ts +12 -0
  123. package/dist/components/ui/breadcrumb.js +72 -0
  124. package/dist/components/ui/button.d.ts +10 -0
  125. package/dist/components/ui/button.js +44 -0
  126. package/dist/components/ui/calendar.d.ts +12 -0
  127. package/dist/components/ui/calendar.js +107 -0
  128. package/dist/components/ui/card.d.ts +13 -0
  129. package/dist/components/ui/card.js +56 -0
  130. package/dist/components/ui/chart.d.ts +46 -0
  131. package/dist/components/ui/chart.js +149 -0
  132. package/dist/components/ui/collapsible.d.ts +7 -0
  133. package/dist/components/ui/collapsible.js +24 -0
  134. package/dist/components/ui/command.d.ts +21 -0
  135. package/dist/components/ui/command.js +88 -0
  136. package/dist/components/ui/context-menu.d.ts +31 -0
  137. package/dist/components/ui/context-menu.js +132 -0
  138. package/dist/components/ui/dialog.d.ts +19 -0
  139. package/dist/components/ui/dialog.js +91 -0
  140. package/dist/components/ui/dropdown-menu.d.ts +31 -0
  141. package/dist/components/ui/dropdown-menu.js +136 -0
  142. package/dist/components/ui/input-group.d.ts +20 -0
  143. package/dist/components/ui/input-group.js +79 -0
  144. package/dist/components/ui/input.d.ts +5 -0
  145. package/dist/components/ui/input.js +15 -0
  146. package/dist/components/ui/popover.d.ts +11 -0
  147. package/dist/components/ui/popover.js +55 -0
  148. package/dist/components/ui/progress.d.ts +9 -0
  149. package/dist/components/ui/progress.js +43 -0
  150. package/dist/components/ui/scroll-area.d.ts +7 -0
  151. package/dist/components/ui/scroll-area.js +36 -0
  152. package/dist/components/ui/select.d.ts +17 -0
  153. package/dist/components/ui/select.js +97 -0
  154. package/dist/components/ui/separator.d.ts +5 -0
  155. package/dist/components/ui/separator.js +15 -0
  156. package/dist/components/ui/sheet.d.ts +16 -0
  157. package/dist/components/ui/sheet.js +89 -0
  158. package/dist/components/ui/sidebar.d.ts +65 -0
  159. package/dist/components/ui/sidebar.js +385 -0
  160. package/dist/components/ui/skeleton.d.ts +4 -0
  161. package/dist/components/ui/skeleton.js +12 -0
  162. package/dist/components/ui/slider.d.ts +5 -0
  163. package/dist/components/ui/slider.js +33 -0
  164. package/dist/components/ui/table.d.ts +12 -0
  165. package/dist/components/ui/table.js +66 -0
  166. package/dist/components/ui/tabs.d.ts +12 -0
  167. package/dist/components/ui/tabs.js +45 -0
  168. package/dist/components/ui/textarea.d.ts +5 -0
  169. package/dist/components/ui/textarea.js +13 -0
  170. package/dist/components/ui/toggle-group.d.ts +13 -0
  171. package/dist/components/ui/toggle-group.js +52 -0
  172. package/dist/components/ui/toggle.d.ts +10 -0
  173. package/dist/components/ui/toggle.js +36 -0
  174. package/dist/components/ui/tooltip.d.ts +8 -0
  175. package/dist/components/ui/tooltip.js +41 -0
  176. package/dist/hooks/use-mobile.d.ts +3 -0
  177. package/dist/hooks/use-mobile.js +18 -0
  178. package/dist/index.d.ts +73 -0
  179. package/dist/index.js +71 -0
  180. package/dist/lib/format.d.ts +24 -0
  181. package/dist/lib/format.js +58 -0
  182. package/dist/lib/locale.d.ts +12 -0
  183. package/dist/lib/locale.js +8 -0
  184. package/dist/lib/recharts.d.ts +2 -0
  185. package/dist/lib/recharts.js +1 -0
  186. package/dist/lib/utils.d.ts +2 -0
  187. package/dist/lib/utils.js +1 -0
  188. package/package.json +78 -3
  189. package/registry.lock.json +413 -0
  190. package/report.css +129 -0
  191. package/styles/shadcn.css +643 -0
  192. package/styles/siyahi.css +189 -0
@@ -0,0 +1,326 @@
1
+ "use client";
2
+ import { useGeneratedOptions } from "../hooks/use-generated-options.js";
3
+ import { useHeaderMinWidths } from "../lib/use-header-min-widths.js";
4
+ import React, { createContext, useCallback, useContext, useReducer } from "react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/components/registry/niko-table/core/data-table-context.tsx
7
+ /**
8
+ * niko-table — created by Semir N. (Semkoo, https://github.com/Semkoo) with AI assistance.
9
+ *
10
+ * Before reporting anything: please check the changelog first.
11
+ * - In-repo: ./CHANGELOG.md
12
+ * - Docs site: https://niko-table.com/changelog
13
+ *
14
+ * Found a bug or have a fix? Open an issue or PR on GitHub so other
15
+ * users (and future LLMs reading this code) benefit:
16
+ * https://github.com/Semkoo/niko-table-registry
17
+ */
18
+ /** Internal key for a flashing cell (row id + column id). */
19
+ function flashCellKey(rowId, columnId) {
20
+ return `${rowId}:${columnId}`;
21
+ }
22
+ const ActiveCellValueContext = createContext(null);
23
+ const ActiveCellSetterContext = createContext(null);
24
+ /**
25
+ * The active-selection span for the grid-style cross-highlight (`null` when
26
+ * inactive). In a header, `activeCell.columnIds.has(column.id)` lights the
27
+ * active columns; in a body/gutter, the row's **display** index (position in
28
+ * `getRowModel().rows` / the virtualizer index) within `activeCell.rowRange`
29
+ * lights the active rows — not TanStack's source-data `row.index`.
30
+ */
31
+ function useDataTableActiveCell() {
32
+ return useContext(ActiveCellValueContext);
33
+ }
34
+ /**
35
+ * Set (or clear, with `null`) the active-cell cross-highlight. The setter is
36
+ * stable, so subscribing to it never causes re-renders. Used by the grid's
37
+ * `<DataGridCrossHighlight>` opt-in.
38
+ */
39
+ function useDataTableActiveCellSetter() {
40
+ const setter = useContext(ActiveCellSetterContext);
41
+ if (setter === null) throw new Error("useDataTableActiveCellSetter must be used within DataTableRoot");
42
+ return setter;
43
+ }
44
+ const ColumnResizeInfoContext = createContext({
45
+ resizingColumnId: null,
46
+ deltaOffset: 0
47
+ });
48
+ /**
49
+ * Live column-resize offset for the preview guide line. Returns the idle value
50
+ * (`{ resizingColumnId: null, deltaOffset: 0 }`) when no drag is in flight (or
51
+ * outside a provider), so it's safe to call unconditionally.
52
+ */
53
+ function useColumnResizeInfo() {
54
+ return useContext(ColumnResizeInfoContext);
55
+ }
56
+ const DataTableContext = createContext(void 0);
57
+ function useDataTable() {
58
+ const context = useContext(DataTableContext);
59
+ if (context === void 0) throw new Error("useDataTable must be used within DataTableRoot");
60
+ return context;
61
+ }
62
+ let DataTableActions = /* @__PURE__ */ function(DataTableActions) {
63
+ DataTableActions[DataTableActions["SET"] = 0] = "SET";
64
+ DataTableActions[DataTableActions["SET_IS_LOADING"] = 1] = "SET_IS_LOADING";
65
+ return DataTableActions;
66
+ }({});
67
+ function dataTableReducer(state, action) {
68
+ switch (action.type) {
69
+ case 1: return {
70
+ ...state,
71
+ isLoading: action.value
72
+ };
73
+ default: return state;
74
+ }
75
+ }
76
+ function deriveInitialState(isLoading) {
77
+ return { isLoading: isLoading ?? false };
78
+ }
79
+ /**
80
+ * A set of currently-"flashing" string keys with per-key auto-expiry timers.
81
+ * `flash(keys, durationMs)` adds them and removes each after its duration.
82
+ * Shared by row flash (keys = row ids) and cell flash (keys = `flashCellKey`).
83
+ */
84
+ function useFlashSet() {
85
+ const [set, setSet] = React.useState(() => /* @__PURE__ */ new Set());
86
+ const timers = React.useRef(/* @__PURE__ */ new Map());
87
+ React.useEffect(() => {
88
+ const t = timers.current;
89
+ return () => {
90
+ t.forEach((timer) => clearTimeout(timer));
91
+ t.clear();
92
+ };
93
+ }, []);
94
+ return [set, useCallback((keys, durationMs) => {
95
+ if (keys.length === 0) return;
96
+ setSet((prev) => {
97
+ const next = new Set(prev);
98
+ for (const k of keys) next.add(k);
99
+ return next;
100
+ });
101
+ for (const k of keys) {
102
+ const existing = timers.current.get(k);
103
+ if (existing) clearTimeout(existing);
104
+ timers.current.set(k, setTimeout(() => {
105
+ setSet((prev) => {
106
+ if (!prev.has(k)) return prev;
107
+ const next = new Set(prev);
108
+ next.delete(k);
109
+ return next;
110
+ });
111
+ timers.current.delete(k);
112
+ }, durationMs));
113
+ }
114
+ }, [])];
115
+ }
116
+ function DataTableProvider({ children, table, columns, isLoading: externalIsLoading }) {
117
+ const [state, dispatch] = useReducer(dataTableReducer, deriveInitialState(externalIsLoading));
118
+ const setIsLoading = useCallback((value) => {
119
+ dispatch({
120
+ type: 1,
121
+ value
122
+ });
123
+ }, []);
124
+ const rowScrollerRef = React.useRef(null);
125
+ const registerRowScroller = useCallback((fn) => {
126
+ rowScrollerRef.current = fn;
127
+ }, []);
128
+ const [scrollContainer, setScrollContainer] = React.useState(null);
129
+ const registerScrollContainer = useCallback((el) => setScrollContainer(el), []);
130
+ const scrollRowIntoView = useCallback((index, opts) => {
131
+ if (rowScrollerRef.current) {
132
+ rowScrollerRef.current(index, opts);
133
+ return;
134
+ }
135
+ if (typeof document !== "undefined") {
136
+ const block = opts?.align === void 0 || opts.align === "auto" ? "nearest" : opts.align;
137
+ document.querySelector(`[data-index="${index}"], [data-row-index="${index}"]`)?.scrollIntoView({ block });
138
+ }
139
+ }, []);
140
+ const [flashingRowIds, flashRowKeys] = useFlashSet();
141
+ const [flashingCellKeys, flashCellKeys] = useFlashSet();
142
+ const [activeCell, setActiveCell] = React.useState(null);
143
+ const rowSelectionAnchorRef = React.useRef(null);
144
+ const toggleRowSelection = useCallback((rowId, shiftKey) => {
145
+ const model = table.getRowModel();
146
+ const row = model.rowsById[rowId];
147
+ if (!row) return;
148
+ const anchorId = rowSelectionAnchorRef.current;
149
+ if (shiftKey && anchorId != null && model.rowsById[anchorId]) {
150
+ const a = model.rows.findIndex((r) => r.id === anchorId);
151
+ const b = model.rows.findIndex((r) => r.id === rowId);
152
+ if (a === -1 || b === -1) return;
153
+ const next = { ...table.state.rowSelection };
154
+ for (let i = Math.min(a, b); i <= Math.max(a, b); i++) {
155
+ const r = model.rows[i];
156
+ if (r?.getCanSelect()) next[r.id] = true;
157
+ }
158
+ table.setRowSelection(next);
159
+ return;
160
+ }
161
+ row.toggleSelected();
162
+ rowSelectionAnchorRef.current = rowId;
163
+ }, [table]);
164
+ const scrollFirstIdIntoView = useCallback((ids) => {
165
+ const model = table.getRowModel();
166
+ for (const id of ids) {
167
+ const idx = model.rows.findIndex((r) => r.id === id);
168
+ if (idx !== -1) {
169
+ scrollRowIntoView(idx, { align: "center" });
170
+ return;
171
+ }
172
+ }
173
+ }, [table, scrollRowIntoView]);
174
+ const flashRows = useCallback((ids, opts) => {
175
+ if (ids.length === 0) return;
176
+ if (opts?.scrollIntoView ?? true) scrollFirstIdIntoView(ids);
177
+ flashRowKeys(ids, opts?.durationMs ?? 1400);
178
+ }, [flashRowKeys, scrollFirstIdIntoView]);
179
+ const flashCells = useCallback((cells, opts) => {
180
+ if (cells.length === 0) return;
181
+ if (opts?.scrollIntoView ?? true) scrollFirstIdIntoView(cells.map((c) => c.rowId));
182
+ flashCellKeys(cells.map((c) => flashCellKey(c.rowId, c.columnId)), opts?.durationMs ?? 1400);
183
+ }, [flashCellKeys, scrollFirstIdIntoView]);
184
+ /**
185
+ * Derive `isLoading` at READ time, not via a sync useEffect.
186
+ *
187
+ * WHY: The previous pattern — `useEffect(() => setIsLoading(externalIsLoading))`
188
+ * — is a "copy prop into state" anti-pattern. Under React 19 + Next.js 16
189
+ * (Turbopack), it can schedule a reducer dispatch that lands on an unmounted
190
+ * component instance during HMR or Strict Mode double-mount, producing:
191
+ *
192
+ * "Can't perform a React state update on a component that hasn't mounted
193
+ * yet. This indicates that you have a side-effect in your render function
194
+ * that asynchronously tries to update the component."
195
+ *
196
+ * FIX: When `externalIsLoading` is provided it wins (matches the original
197
+ * effect's intent); otherwise fall back to the reducer-managed state.
198
+ * No cross-commit dispatch, no race, no unmount-target warning.
199
+ *
200
+ * BEHAVIOR NOTE: If a caller both passes `isLoading` as a prop AND calls
201
+ * `setIsLoading()` from inside the tree, the prop wins and the internal
202
+ * call becomes a no-op. This matches the documented contract of a
203
+ * controlled prop and was already the intended behavior of the effect.
204
+ */
205
+ const effectiveIsLoading = externalIsLoading ?? state.isLoading;
206
+ const tableState = table.state;
207
+ const globalFilter = tableState.globalFilter;
208
+ const sorting = tableState.sorting;
209
+ const columnFilters = tableState.columnFilters;
210
+ const columnVisibility = tableState.columnVisibility;
211
+ const expanded = tableState.expanded;
212
+ const rowSelection = tableState.rowSelection;
213
+ const pagination = tableState.pagination;
214
+ const columnPinning = tableState.columnPinning;
215
+ const columnOrder = tableState.columnOrder;
216
+ const columnSizing = tableState.columnSizing;
217
+ const tableStateKey = React.useMemo(() => {
218
+ const getObjectHash = (obj) => {
219
+ if (!obj) return "0";
220
+ const keys = Object.keys(obj);
221
+ if (keys.length === 0) return "0";
222
+ return keys.sort().join(",");
223
+ };
224
+ const paginationKey = `${pagination.pageIndex ?? 0}:${pagination.pageSize ?? 0}`;
225
+ return {
226
+ globalFilter: typeof globalFilter === "string" ? globalFilter : globalFilter && typeof globalFilter === "object" ? getObjectHash(globalFilter) : "",
227
+ sortingHash: JSON.stringify(sorting),
228
+ columnFiltersHash: JSON.stringify(columnFilters),
229
+ columnVisibilityHash: getObjectHash(columnVisibility),
230
+ expandedHash: getObjectHash(expanded),
231
+ rowSelectionHash: getObjectHash(rowSelection),
232
+ paginationKey,
233
+ columnPinningHash: JSON.stringify(columnPinning),
234
+ columnOrderHash: JSON.stringify(columnOrder),
235
+ columnSizingHash: JSON.stringify(columnSizing)
236
+ };
237
+ }, [
238
+ globalFilter,
239
+ sorting,
240
+ columnFilters,
241
+ columnVisibility,
242
+ expanded,
243
+ rowSelection,
244
+ pagination,
245
+ columnPinning,
246
+ columnOrder,
247
+ columnSizing
248
+ ]);
249
+ const generatedOptionsMap = useGeneratedOptions(table);
250
+ const headerMinWidths = useHeaderMinWidths(table, scrollContainer, table.options.enableColumnResizing ?? false);
251
+ const [manualResizePreview, setManualResizePreview] = React.useState(null);
252
+ const columnResizing = table.state.columnResizing;
253
+ const resizingColumnId = columnResizing.isResizingColumn || null;
254
+ const resizeDeltaOffset = columnResizing.deltaOffset ?? 0;
255
+ const columnResizeInfo = React.useMemo(() => manualResizePreview ?? {
256
+ resizingColumnId,
257
+ deltaOffset: resizeDeltaOffset
258
+ }, [
259
+ manualResizePreview,
260
+ resizingColumnId,
261
+ resizeDeltaOffset
262
+ ]);
263
+ const value = React.useMemo(() => ({
264
+ table,
265
+ columns: columns || table.options.columns,
266
+ isLoading: effectiveIsLoading,
267
+ generatedOptionsMap,
268
+ setIsLoading,
269
+ scrollRowIntoView,
270
+ registerRowScroller,
271
+ scrollContainer,
272
+ registerScrollContainer,
273
+ headerMinWidths,
274
+ setColumnResizePreview: setManualResizePreview,
275
+ flashingRowIds,
276
+ flashingCellKeys,
277
+ flashRows,
278
+ flashCells,
279
+ toggleRowSelection
280
+ }), [
281
+ table,
282
+ columns,
283
+ effectiveIsLoading,
284
+ generatedOptionsMap,
285
+ setIsLoading,
286
+ scrollRowIntoView,
287
+ registerRowScroller,
288
+ scrollContainer,
289
+ registerScrollContainer,
290
+ headerMinWidths,
291
+ setManualResizePreview,
292
+ flashingRowIds,
293
+ flashingCellKeys,
294
+ flashRows,
295
+ flashCells,
296
+ toggleRowSelection,
297
+ tableStateKey
298
+ ]);
299
+ return /* @__PURE__ */ jsx(DataTableContext.Provider, {
300
+ value,
301
+ children: /* @__PURE__ */ jsx(ActiveCellSetterContext.Provider, {
302
+ value: setActiveCell,
303
+ children: /* @__PURE__ */ jsx(ActiveCellValueContext.Provider, {
304
+ value: activeCell,
305
+ children: /* @__PURE__ */ jsxs(ColumnResizeInfoContext.Provider, {
306
+ value: columnResizeInfo,
307
+ children: [/* @__PURE__ */ jsx("style", {
308
+ href: "niko-row-flash",
309
+ precedence: "default",
310
+ children: ROW_FLASH_KEYFRAMES
311
+ }), children]
312
+ })
313
+ })
314
+ })
315
+ });
316
+ }
317
+ /**
318
+ * Self-contained flash animation (no dependency on app globals). Soft fade
319
+ * pulse: a primary-tinted background fades to transparent once, ~1.2s.
320
+ */
321
+ const ROW_FLASH_KEYFRAMES = `@keyframes niko-row-flash {
322
+ 0% { background-color: color-mix(in oklab, var(--primary) 24%, transparent); }
323
+ 100% { background-color: transparent; }
324
+ }`;
325
+ //#endregion
326
+ export { DataTableActions, DataTableContext, DataTableProvider, flashCellKey, useColumnResizeInfo, useDataTable, useDataTableActiveCell, useDataTableActiveCellSetter };
@@ -0,0 +1,85 @@
1
+ import React from "react";
2
+ //#region src/components/registry/niko-table/core/data-table-error-boundary.d.ts
3
+ export interface DataTableErrorBoundaryProps {
4
+ /**
5
+ * The content to render when there's no error
6
+ */
7
+ children: React.ReactNode;
8
+ /**
9
+ * Custom fallback UI to show when an error occurs
10
+ */
11
+ fallback?: React.ReactNode;
12
+ /**
13
+ * Callback fired when an error is caught
14
+ */
15
+ onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
16
+ /**
17
+ * Whether to show a reset button
18
+ * @default true
19
+ */
20
+ showResetButton?: boolean;
21
+ /**
22
+ * Custom reset button text
23
+ * @default "Try Again"
24
+ */
25
+ resetButtonText?: string;
26
+ }
27
+ interface DataTableErrorBoundaryState {
28
+ hasError: boolean;
29
+ error: Error | null;
30
+ }
31
+ /**
32
+ * Error boundary component for DataTable.
33
+ * Catches JavaScript errors anywhere in the data table component tree,
34
+ * logs those errors, and displays a fallback UI instead of crashing.
35
+ *
36
+ * @example
37
+ * Basic usage
38
+ * <DataTableErrorBoundary>
39
+ * <DataTableRoot data={data} columns={columns}>
40
+ * <DataTable>
41
+ * <DataTableHeader />
42
+ * <DataTableBody />
43
+ * </DataTable>
44
+ * </DataTableRoot>
45
+ * </DataTableErrorBoundary>
46
+ *
47
+ * @example
48
+ * // With custom fallback
49
+ * <DataTableErrorBoundary
50
+ * fallback={
51
+ * <div className="p-8 text-center">
52
+ * <h3>Oops! Something went wrong.</h3>
53
+ * <p>Please contact support if this persists.</p>
54
+ * </div>
55
+ * }
56
+ * >
57
+ * <DataTableRoot data={data} columns={columns}>
58
+ * {/* ... *\/}
59
+ * </DataTableRoot>
60
+ * </DataTableErrorBoundary>
61
+ *
62
+ * @example
63
+ * // With error logging
64
+ * <DataTableErrorBoundary
65
+ * onError={(error, errorInfo) => {
66
+ * console.error("DataTable Error:", error, errorInfo)
67
+ * // Send to error tracking service
68
+ * trackError(error)
69
+ * }}
70
+ * >
71
+ * <DataTableRoot data={data} columns={columns}>
72
+ * {/* ... *\/}
73
+ * </DataTableRoot>
74
+ * </DataTableErrorBoundary>
75
+ */
76
+ export declare class DataTableErrorBoundary extends React.Component<DataTableErrorBoundaryProps, DataTableErrorBoundaryState> {
77
+ static displayName: string;
78
+ constructor(props: DataTableErrorBoundaryProps);
79
+ static getDerivedStateFromError(error: Error): DataTableErrorBoundaryState;
80
+ componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void;
81
+ handleReset: () => void;
82
+ render(): string | number | bigint | boolean | React.JSX.Element | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | Iterable<React.ReactNode> | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | React.ReactPortal | null | undefined> | null | undefined;
83
+ }
84
+ //#endregion
85
+ export {};
@@ -0,0 +1,116 @@
1
+ "use client";
2
+ import { Alert, AlertDescription, AlertTitle } from "../../../ui/alert.js";
3
+ import { Button } from "../../../ui/button.js";
4
+ import React from "react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { AlertCircle } from "lucide-react";
7
+ //#region src/components/registry/niko-table/core/data-table-error-boundary.tsx
8
+ /**
9
+ * niko-table — created by Semir N. (Semkoo, https://github.com/Semkoo) with AI assistance.
10
+ *
11
+ * Before reporting anything: please check the changelog first.
12
+ * - In-repo: ./CHANGELOG.md
13
+ * - Docs site: https://niko-table.com/changelog
14
+ *
15
+ * Found a bug or have a fix? Open an issue or PR on GitHub so other
16
+ * users (and future LLMs reading this code) benefit:
17
+ * https://github.com/Semkoo/niko-table-registry
18
+ */
19
+ /**
20
+ * Error boundary component for DataTable.
21
+ * Catches JavaScript errors anywhere in the data table component tree,
22
+ * logs those errors, and displays a fallback UI instead of crashing.
23
+ *
24
+ * @example
25
+ * Basic usage
26
+ * <DataTableErrorBoundary>
27
+ * <DataTableRoot data={data} columns={columns}>
28
+ * <DataTable>
29
+ * <DataTableHeader />
30
+ * <DataTableBody />
31
+ * </DataTable>
32
+ * </DataTableRoot>
33
+ * </DataTableErrorBoundary>
34
+ *
35
+ * @example
36
+ * // With custom fallback
37
+ * <DataTableErrorBoundary
38
+ * fallback={
39
+ * <div className="p-8 text-center">
40
+ * <h3>Oops! Something went wrong.</h3>
41
+ * <p>Please contact support if this persists.</p>
42
+ * </div>
43
+ * }
44
+ * >
45
+ * <DataTableRoot data={data} columns={columns}>
46
+ * {/* ... *\/}
47
+ * </DataTableRoot>
48
+ * </DataTableErrorBoundary>
49
+ *
50
+ * @example
51
+ * // With error logging
52
+ * <DataTableErrorBoundary
53
+ * onError={(error, errorInfo) => {
54
+ * console.error("DataTable Error:", error, errorInfo)
55
+ * // Send to error tracking service
56
+ * trackError(error)
57
+ * }}
58
+ * >
59
+ * <DataTableRoot data={data} columns={columns}>
60
+ * {/* ... *\/}
61
+ * </DataTableRoot>
62
+ * </DataTableErrorBoundary>
63
+ */
64
+ var DataTableErrorBoundary = class extends React.Component {
65
+ static displayName = "DataTableErrorBoundary";
66
+ constructor(props) {
67
+ super(props);
68
+ this.state = {
69
+ hasError: false,
70
+ error: null
71
+ };
72
+ }
73
+ static getDerivedStateFromError(error) {
74
+ return {
75
+ hasError: true,
76
+ error
77
+ };
78
+ }
79
+ componentDidCatch(error, errorInfo) {
80
+ console.error("DataTable Error Boundary caught an error:", error, errorInfo);
81
+ this.props.onError?.(error, errorInfo);
82
+ }
83
+ handleReset = () => {
84
+ this.setState({
85
+ hasError: false,
86
+ error: null
87
+ });
88
+ };
89
+ render() {
90
+ if (this.state.hasError) {
91
+ if (this.props.fallback) return this.props.fallback;
92
+ const { showResetButton = true, resetButtonText = "Try Again" } = this.props;
93
+ return /* @__PURE__ */ jsxs(Alert, {
94
+ variant: "destructive",
95
+ className: "my-4",
96
+ children: [
97
+ /* @__PURE__ */ jsx(AlertCircle, { className: "h-4 w-4" }),
98
+ /* @__PURE__ */ jsx(AlertTitle, { children: "Table Error" }),
99
+ /* @__PURE__ */ jsxs(AlertDescription, {
100
+ className: "mt-2 flex flex-col gap-2",
101
+ children: [/* @__PURE__ */ jsx("p", { children: this.state.error?.message || "Something went wrong while displaying the table." }), showResetButton && /* @__PURE__ */ jsx(Button, {
102
+ variant: "outline",
103
+ size: "sm",
104
+ onClick: this.handleReset,
105
+ className: "mt-2 w-fit",
106
+ children: resetButtonText
107
+ })]
108
+ })
109
+ ]
110
+ });
111
+ }
112
+ return this.props.children;
113
+ }
114
+ };
115
+ //#endregion
116
+ export { DataTableErrorBoundary };
@@ -0,0 +1,49 @@
1
+ import { DataTableRow } from "../types/index.js";
2
+ import { DataTableGroupedRowsProps } from "../components/data-table-grouped-rows.js";
3
+ import React from "react";
4
+ import { RowData } from "@tanstack/react-table";
5
+ //#region src/components/registry/niko-table/core/data-table-grouped-row.d.ts
6
+ /**
7
+ * The built-in group row: a chevron, the group value, a `· N rows` count, and
8
+ * one aggregated cell per column that defines `aggregatedCell` /
9
+ * `aggregationFn`.
10
+ *
11
+ * Exported so a custom group row can reuse it — wrap it, or render it for the
12
+ * groups you don't want to special-case.
13
+ */
14
+ export declare function DataTableGroupedRowCells(): React.JSX.Element;
15
+ export declare namespace DataTableGroupedRowCells {
16
+ var displayName: string;
17
+ }
18
+ /**
19
+ * Row rendered in place of a leaf row wherever TanStack's grouped row model
20
+ * produces a group. Internal — reached by composing `<DataTableGroupedRows>`
21
+ * into a body, never imported directly by callers.
22
+ */
23
+ export interface GroupedBodyRowProps<TData extends RowData = RowData> {
24
+ row: DataTableRow<TData>;
25
+ displayIndex: number;
26
+ /**
27
+ * Passed as a prop (rather than read off `row`) so `React.memo` invalidates
28
+ * when the group is toggled — same contract as `BodyRow`.
29
+ */
30
+ isExpanded: boolean;
31
+ columnWidths: ReadonlyMap<string, number | string | undefined>;
32
+ /** Invalidates React.memo on visibility/order/pinning/resize change. */
33
+ columnLayoutSignature: string;
34
+ /** The nested slot's props, if the caller composed one. */
35
+ slot: DataTableGroupedRowsProps<TData> | undefined;
36
+ /**
37
+ * Virtualized bodies only: the virtualizer's measure callback. A band is
38
+ * usually a different height from a leaf row, so it has to be measured
39
+ * rather than assumed — without this the rows below it drift.
40
+ */
41
+ measureRef?: (node: HTMLTableRowElement | null) => void;
42
+ /**
43
+ * Virtualized bodies only: the row's index in the virtual window, which the
44
+ * virtualizer reads back off the DOM as `data-index`.
45
+ */
46
+ virtualIndex?: number;
47
+ }
48
+ export declare const GroupedBodyRow: <TData extends RowData = RowData>(props: GroupedBodyRowProps<TData>) => React.JSX.Element;
49
+ //#endregion
@@ -0,0 +1,112 @@
1
+ import { Button } from "../../../ui/button.js";
2
+ import { cn } from "../../../../lib/utils.js";
3
+ import { DataTableGroupRowProvider, useDataTableGroupRow } from "../components/data-table-grouped-rows.js";
4
+ import { TableCell, TableRow } from "../ui/table.js";
5
+ import { isInteractiveClickTarget } from "../lib/row-click.js";
6
+ import { getCommonPinningStyles } from "../lib/styles.js";
7
+ import React from "react";
8
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
9
+ import { ChevronRight } from "lucide-react";
10
+ import { flexRender } from "@tanstack/react-table";
11
+ //#region src/components/registry/niko-table/core/data-table-grouped-row.tsx
12
+ /**
13
+ * The built-in group row: a chevron, the group value, a `· N rows` count, and
14
+ * one aggregated cell per column that defines `aggregatedCell` /
15
+ * `aggregationFn`.
16
+ *
17
+ * Exported so a custom group row can reuse it — wrap it, or render it for the
18
+ * groups you don't want to special-case.
19
+ */
20
+ function DataTableGroupedRowCells() {
21
+ const { row, groupLabel, count, isExpanded, toggle, columnWidths } = useDataTableGroupRow();
22
+ const countLabel = `${count} ${count === 1 ? "row" : "rows"}`;
23
+ return /* @__PURE__ */ jsx(Fragment$1, { children: row.getVisibleCells().map((cell) => {
24
+ const cellStyle = {
25
+ width: columnWidths.get(cell.column.id),
26
+ ...getCommonPinningStyles(cell.column, false)
27
+ };
28
+ let content = null;
29
+ if (cell.getIsGrouped()) content = /* @__PURE__ */ jsxs("div", {
30
+ className: "flex min-w-0 items-center gap-2",
31
+ children: [
32
+ /* @__PURE__ */ jsx(Button, {
33
+ type: "button",
34
+ variant: "ghost",
35
+ size: "icon-sm",
36
+ "aria-expanded": isExpanded,
37
+ "aria-label": isExpanded ? `Collapse the ${groupLabel} group` : `Expand the ${groupLabel} group`,
38
+ onClick: (event) => {
39
+ event.stopPropagation();
40
+ toggle();
41
+ },
42
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: cn("size-3.5 transition-transform", isExpanded && "rotate-90") })
43
+ }),
44
+ /* @__PURE__ */ jsx("span", {
45
+ className: "truncate font-medium",
46
+ title: groupLabel || void 0,
47
+ children: flexRender(cell.column.columnDef.cell, cell.getContext())
48
+ }),
49
+ /* @__PURE__ */ jsx("span", {
50
+ className: "shrink-0 text-xs text-muted-foreground",
51
+ children: `· ${countLabel}`
52
+ })
53
+ ]
54
+ });
55
+ else if (cell.getIsAggregated()) content = flexRender(cell.column.columnDef.aggregatedCell ?? cell.column.columnDef.cell, cell.getContext());
56
+ return /* @__PURE__ */ jsx(TableCell, {
57
+ "data-col-id": cell.column.id,
58
+ style: cellStyle,
59
+ className: cn("truncate", cell.column.getIsPinned() && "bg-muted"),
60
+ children: content
61
+ }, cell.id);
62
+ }) });
63
+ }
64
+ DataTableGroupedRowCells.displayName = "DataTableGroupedRowCells";
65
+ const GroupedBodyRow = React.memo(function GroupedBodyRow({ row, displayIndex, isExpanded, columnWidths, slot, measureRef, virtualIndex }) {
66
+ const groupedColumnId = row.groupingColumnId;
67
+ const groupValue = groupedColumnId ? row.getGroupingValue(groupedColumnId) : void 0;
68
+ const groupLabel = groupValue === null || groupValue === void 0 ? "" : String(groupValue);
69
+ const toggle = React.useCallback(() => {
70
+ row.toggleExpanded();
71
+ }, [row]);
72
+ const handleRowClick = React.useCallback((event) => {
73
+ if (isInteractiveClickTarget(event.target)) return;
74
+ event.stopPropagation();
75
+ row.toggleExpanded();
76
+ }, [row]);
77
+ const contextValue = React.useMemo(() => ({
78
+ row,
79
+ groupValue,
80
+ groupLabel,
81
+ count: row.subRows.length,
82
+ isExpanded,
83
+ toggle,
84
+ displayIndex,
85
+ columnWidths
86
+ }), [
87
+ row,
88
+ groupValue,
89
+ groupLabel,
90
+ isExpanded,
91
+ toggle,
92
+ displayIndex,
93
+ columnWidths
94
+ ]);
95
+ const useCustom = !!slot?.children && (!slot.enabledFor || slot.enabledFor(row));
96
+ return /* @__PURE__ */ jsx(DataTableGroupRowProvider, {
97
+ value: contextValue,
98
+ children: /* @__PURE__ */ jsx(TableRow, {
99
+ ref: measureRef,
100
+ "data-index": virtualIndex,
101
+ "data-row-index": displayIndex,
102
+ "data-row-id": row.id,
103
+ "data-row-type": "grouped",
104
+ "data-expanded": isExpanded ? "true" : void 0,
105
+ onClick: handleRowClick,
106
+ className: "group cursor-pointer bg-muted/40",
107
+ children: useCustom ? slot.children : /* @__PURE__ */ jsx(DataTableGroupedRowCells, {})
108
+ })
109
+ });
110
+ });
111
+ //#endregion
112
+ export { DataTableGroupedRowCells, GroupedBodyRow };