@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.
- package/LICENSE +21 -0
- package/README.md +174 -2
- package/THIRD_PARTY_NOTICES.md +51 -0
- package/dist/components/registry/dashboard-01/section-cards.d.ts +17 -0
- package/dist/components/registry/dashboard-01/section-cards.js +39 -0
- package/dist/components/registry/dashboardcn/bar-list.d.ts +24 -0
- package/dist/components/registry/dashboardcn/bar-list.js +71 -0
- package/dist/components/registry/heatmap/calendar-heatmap.d.ts +134 -0
- package/dist/components/registry/heatmap/calendar-heatmap.js +497 -0
- package/dist/components/registry/heatmap/status-heatmap.d.ts +120 -0
- package/dist/components/registry/heatmap/status-heatmap.js +330 -0
- package/dist/components/registry/heatmap/weekday-heatmap.d.ts +145 -0
- package/dist/components/registry/heatmap/weekday-heatmap.js +489 -0
- package/dist/components/registry/niko-table/components/data-table-column-actions.d.ts +26 -0
- package/dist/components/registry/niko-table/components/data-table-column-actions.js +32 -0
- package/dist/components/registry/niko-table/components/data-table-column-filter.d.ts +13 -0
- package/dist/components/registry/niko-table/components/data-table-column-filter.js +17 -0
- package/dist/components/registry/niko-table/components/data-table-column-header.d.ts +30 -0
- package/dist/components/registry/niko-table/components/data-table-column-header.js +35 -0
- package/dist/components/registry/niko-table/components/data-table-column-sort.d.ts +21 -0
- package/dist/components/registry/niko-table/components/data-table-column-sort.js +36 -0
- package/dist/components/registry/niko-table/components/data-table-column-title.d.ts +12 -0
- package/dist/components/registry/niko-table/components/data-table-column-title.js +18 -0
- package/dist/components/registry/niko-table/components/data-table-empty-state.d.ts +108 -0
- package/dist/components/registry/niko-table/components/data-table-empty-state.js +148 -0
- package/dist/components/registry/niko-table/components/data-table-faceted-filter.d.ts +46 -0
- package/dist/components/registry/niko-table/components/data-table-faceted-filter.js +133 -0
- package/dist/components/registry/niko-table/components/data-table-grouped-rows.d.ts +111 -0
- package/dist/components/registry/niko-table/components/data-table-grouped-rows.js +77 -0
- package/dist/components/registry/niko-table/components/data-table-row-context-menu-slot.d.ts +65 -0
- package/dist/components/registry/niko-table/components/data-table-row-context-menu-slot.js +65 -0
- package/dist/components/registry/niko-table/components/data-table-row-context-menu.d.ts +50 -0
- package/dist/components/registry/niko-table/components/data-table-row-context-menu.js +42 -0
- package/dist/components/registry/niko-table/components/data-table-row-menu.d.ts +51 -0
- package/dist/components/registry/niko-table/components/data-table-row-menu.js +65 -0
- package/dist/components/registry/niko-table/components/data-table-toolbar-section.d.ts +7 -0
- package/dist/components/registry/niko-table/components/data-table-toolbar-section.js +63 -0
- package/dist/components/registry/niko-table/config/data-table.d.ts +156 -0
- package/dist/components/registry/niko-table/config/data-table.js +279 -0
- package/dist/components/registry/niko-table/config/feature-detection.d.ts +39 -0
- package/dist/components/registry/niko-table/config/feature-detection.js +174 -0
- package/dist/components/registry/niko-table/core/data-table-context.d.ts +157 -0
- package/dist/components/registry/niko-table/core/data-table-context.js +326 -0
- package/dist/components/registry/niko-table/core/data-table-error-boundary.d.ts +85 -0
- package/dist/components/registry/niko-table/core/data-table-error-boundary.js +116 -0
- package/dist/components/registry/niko-table/core/data-table-grouped-row.d.ts +49 -0
- package/dist/components/registry/niko-table/core/data-table-grouped-row.js +112 -0
- package/dist/components/registry/niko-table/core/data-table-root.d.ts +62 -0
- package/dist/components/registry/niko-table/core/data-table-root.js +406 -0
- package/dist/components/registry/niko-table/core/data-table-structure.d.ts +153 -0
- package/dist/components/registry/niko-table/core/data-table-structure.js +370 -0
- package/dist/components/registry/niko-table/core/data-table.d.ts +30 -0
- package/dist/components/registry/niko-table/core/data-table.js +126 -0
- package/dist/components/registry/niko-table/filters/table-column-actions.d.ts +49 -0
- package/dist/components/registry/niko-table/filters/table-column-actions.js +48 -0
- package/dist/components/registry/niko-table/filters/table-column-sort.d.ts +51 -0
- package/dist/components/registry/niko-table/filters/table-column-sort.js +184 -0
- package/dist/components/registry/niko-table/filters/table-column-title.d.ts +17 -0
- package/dist/components/registry/niko-table/filters/table-column-title.js +20 -0
- package/dist/components/registry/niko-table/filters/table-faceted-filter.d.ts +40 -0
- package/dist/components/registry/niko-table/filters/table-faceted-filter.js +210 -0
- package/dist/components/registry/niko-table/hooks/use-derived-column-title.d.ts +29 -0
- package/dist/components/registry/niko-table/hooks/use-derived-column-title.js +40 -0
- package/dist/components/registry/niko-table/hooks/use-generated-options.d.ts +49 -0
- package/dist/components/registry/niko-table/hooks/use-generated-options.js +144 -0
- package/dist/components/registry/niko-table/lib/build-faceted-options.d.ts +59 -0
- package/dist/components/registry/niko-table/lib/build-faceted-options.js +82 -0
- package/dist/components/registry/niko-table/lib/column-resize-handle.d.ts +33 -0
- package/dist/components/registry/niko-table/lib/column-resize-handle.js +175 -0
- package/dist/components/registry/niko-table/lib/constants.d.ts +128 -0
- package/dist/components/registry/niko-table/lib/constants.js +123 -0
- package/dist/components/registry/niko-table/lib/create-scroll-handler.d.ts +35 -0
- package/dist/components/registry/niko-table/lib/create-scroll-handler.js +63 -0
- package/dist/components/registry/niko-table/lib/data-table-features.d.ts +218 -0
- package/dist/components/registry/niko-table/lib/data-table-features.js +127 -0
- package/dist/components/registry/niko-table/lib/filter-functions.d.ts +47 -0
- package/dist/components/registry/niko-table/lib/filter-functions.js +309 -0
- package/dist/components/registry/niko-table/lib/filter-rows.d.ts +14 -0
- package/dist/components/registry/niko-table/lib/filter-rows.js +28 -0
- package/dist/components/registry/niko-table/lib/flex-columns.d.ts +48 -0
- package/dist/components/registry/niko-table/lib/flex-columns.js +65 -0
- package/dist/components/registry/niko-table/lib/format.d.ts +33 -0
- package/dist/components/registry/niko-table/lib/format.js +99 -0
- package/dist/components/registry/niko-table/lib/render-cell-content.d.ts +17 -0
- package/dist/components/registry/niko-table/lib/render-cell-content.js +65 -0
- package/dist/components/registry/niko-table/lib/row-click.d.ts +67 -0
- package/dist/components/registry/niko-table/lib/row-click.js +28 -0
- package/dist/components/registry/niko-table/lib/styles.d.ts +6 -0
- package/dist/components/registry/niko-table/lib/styles.js +24 -0
- package/dist/components/registry/niko-table/lib/use-header-min-widths.d.ts +17 -0
- package/dist/components/registry/niko-table/lib/use-header-min-widths.js +82 -0
- package/dist/components/registry/niko-table/types/index.d.ts +141 -0
- package/dist/components/registry/niko-table/types/index.js +2 -0
- package/dist/components/registry/niko-table/ui/table.d.ts +13 -0
- package/dist/components/registry/niko-table/ui/table.js +74 -0
- package/dist/components/registry/sidebar-07/app-sidebar.d.ts +18 -0
- package/dist/components/registry/sidebar-07/app-sidebar.js +26 -0
- package/dist/components/registry/sidebar-07/nav-main.d.ts +15 -0
- package/dist/components/registry/sidebar-07/nav-main.js +25 -0
- package/dist/components/registry/sidebar-07/nav-projects.d.ts +9 -0
- package/dist/components/registry/sidebar-07/nav-projects.js +43 -0
- package/dist/components/registry/sidebar-07/nav-user.d.ts +9 -0
- package/dist/components/registry/sidebar-07/nav-user.js +70 -0
- package/dist/components/registry/sidebar-07/team-switcher.d.ts +10 -0
- package/dist/components/registry/sidebar-07/team-switcher.js +70 -0
- package/dist/components/siyahi/delta.d.ts +10 -0
- package/dist/components/siyahi/delta.js +27 -0
- package/dist/components/siyahi/local-time.d.ts +7 -0
- package/dist/components/siyahi/local-time.js +22 -0
- package/dist/components/siyahi/price-chart.d.ts +12 -0
- package/dist/components/siyahi/price-chart.js +80 -0
- package/dist/components/siyahi/siyahi-provider.d.ts +12 -0
- package/dist/components/siyahi/siyahi-provider.js +31 -0
- package/dist/components/siyahi/treemap.d.ts +14 -0
- package/dist/components/siyahi/treemap.js +58 -0
- package/dist/components/ui/alert.d.ts +12 -0
- package/dist/components/ui/alert.js +43 -0
- package/dist/components/ui/avatar.d.ts +13 -0
- package/dist/components/ui/avatar.js +50 -0
- package/dist/components/ui/badge.d.ts +9 -0
- package/dist/components/ui/badge.js +29 -0
- package/dist/components/ui/breadcrumb.d.ts +12 -0
- package/dist/components/ui/breadcrumb.js +72 -0
- package/dist/components/ui/button.d.ts +10 -0
- package/dist/components/ui/button.js +44 -0
- package/dist/components/ui/calendar.d.ts +12 -0
- package/dist/components/ui/calendar.js +107 -0
- package/dist/components/ui/card.d.ts +13 -0
- package/dist/components/ui/card.js +56 -0
- package/dist/components/ui/chart.d.ts +46 -0
- package/dist/components/ui/chart.js +149 -0
- package/dist/components/ui/collapsible.d.ts +7 -0
- package/dist/components/ui/collapsible.js +24 -0
- package/dist/components/ui/command.d.ts +21 -0
- package/dist/components/ui/command.js +88 -0
- package/dist/components/ui/context-menu.d.ts +31 -0
- package/dist/components/ui/context-menu.js +132 -0
- package/dist/components/ui/dialog.d.ts +19 -0
- package/dist/components/ui/dialog.js +91 -0
- package/dist/components/ui/dropdown-menu.d.ts +31 -0
- package/dist/components/ui/dropdown-menu.js +136 -0
- package/dist/components/ui/input-group.d.ts +20 -0
- package/dist/components/ui/input-group.js +79 -0
- package/dist/components/ui/input.d.ts +5 -0
- package/dist/components/ui/input.js +15 -0
- package/dist/components/ui/popover.d.ts +11 -0
- package/dist/components/ui/popover.js +55 -0
- package/dist/components/ui/progress.d.ts +9 -0
- package/dist/components/ui/progress.js +43 -0
- package/dist/components/ui/scroll-area.d.ts +7 -0
- package/dist/components/ui/scroll-area.js +36 -0
- package/dist/components/ui/select.d.ts +17 -0
- package/dist/components/ui/select.js +97 -0
- package/dist/components/ui/separator.d.ts +5 -0
- package/dist/components/ui/separator.js +15 -0
- package/dist/components/ui/sheet.d.ts +16 -0
- package/dist/components/ui/sheet.js +89 -0
- package/dist/components/ui/sidebar.d.ts +65 -0
- package/dist/components/ui/sidebar.js +385 -0
- package/dist/components/ui/skeleton.d.ts +4 -0
- package/dist/components/ui/skeleton.js +12 -0
- package/dist/components/ui/slider.d.ts +5 -0
- package/dist/components/ui/slider.js +33 -0
- package/dist/components/ui/table.d.ts +12 -0
- package/dist/components/ui/table.js +66 -0
- package/dist/components/ui/tabs.d.ts +12 -0
- package/dist/components/ui/tabs.js +45 -0
- package/dist/components/ui/textarea.d.ts +5 -0
- package/dist/components/ui/textarea.js +13 -0
- package/dist/components/ui/toggle-group.d.ts +13 -0
- package/dist/components/ui/toggle-group.js +52 -0
- package/dist/components/ui/toggle.d.ts +10 -0
- package/dist/components/ui/toggle.js +36 -0
- package/dist/components/ui/tooltip.d.ts +8 -0
- package/dist/components/ui/tooltip.js +41 -0
- package/dist/hooks/use-mobile.d.ts +3 -0
- package/dist/hooks/use-mobile.js +18 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +71 -0
- package/dist/lib/format.d.ts +24 -0
- package/dist/lib/format.js +58 -0
- package/dist/lib/locale.d.ts +12 -0
- package/dist/lib/locale.js +8 -0
- package/dist/lib/recharts.d.ts +2 -0
- package/dist/lib/recharts.js +1 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +1 -0
- package/package.json +78 -3
- package/registry.lock.json +413 -0
- package/report.css +129 -0
- package/styles/shadcn.css +643 -0
- 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 };
|