@versini/ui-datagrid 0.8.0 → 0.8.2
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/dist/{DataGridBody/useColumnMeasurement.js → 131.js} +14 -7
- package/dist/298.js +215 -0
- package/dist/{DataGridConstants/DataGridConstants.js → 46.js} +1 -3
- package/dist/511.js +9 -0
- package/dist/926.js +15 -0
- package/dist/DataGrid/index.js +165 -7
- package/dist/DataGridAnimated/index.js +159 -6
- package/dist/DataGridBody/index.js +33 -4
- package/dist/DataGridCell/index.js +37 -4
- package/dist/DataGridCellSort/index.js +138 -4
- package/dist/DataGridConstants/index.js +2 -6
- package/dist/DataGridFooter/index.js +79 -4
- package/dist/DataGridHeader/index.js +110 -4
- package/dist/DataGridInfinite/index.js +312 -4
- package/dist/DataGridRow/index.js +89 -4
- package/dist/DataGridSorting/index.js +225 -7
- package/dist/utilities/classes.d.ts +7 -29
- package/package.json +3 -3
- package/dist/DataGrid/DataGrid.js +0 -183
- package/dist/DataGrid/DataGridContext.js +0 -16
- package/dist/DataGrid/DataGridTypes.js +0 -9
- package/dist/DataGridAnimated/AnimatedWrapper.js +0 -53
- package/dist/DataGridAnimated/useAnimatedHeight.js +0 -131
- package/dist/DataGridBody/DataGridBody.js +0 -55
- package/dist/DataGridBody/getBodyClass.js +0 -23
- package/dist/DataGridCell/DataGridCell.js +0 -51
- package/dist/DataGridCellSort/ButtonSort.js +0 -67
- package/dist/DataGridCellSort/DataGridCellSort.js +0 -111
- package/dist/DataGridFooter/DataGridFooter.js +0 -98
- package/dist/DataGridHeader/DataGridHeader.js +0 -129
- package/dist/DataGridInfinite/DataGridInfiniteBody.js +0 -334
- package/dist/DataGridRow/DataGridRow.js +0 -108
- package/dist/DataGridSorting/sortingUtils.js +0 -234
- package/dist/utilities/classes.js +0 -267
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
@versini/ui-datagrid v0.8.
|
|
2
|
+
@versini/ui-datagrid v0.8.2
|
|
3
3
|
© 2026 gizmette.com
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { DataGridContext, clsx } from "./926.js";
|
|
7
|
+
import { useRef, useContext, useLayoutEffect } from "./511.js";
|
|
8
8
|
|
|
9
|
-
;// CONCATENATED MODULE: external "react"
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Get the CSS class for a DataGrid body element. Uses display:contents so the
|
|
12
|
+
* body doesn't interfere with the grid flow. Rows will use subgrid when columns
|
|
13
|
+
* are provided, or define their own grid otherwise.
|
|
14
|
+
*
|
|
15
|
+
* @param className - Additional class name to merge
|
|
16
|
+
*
|
|
17
|
+
*/ function getBodyClass(className) {
|
|
18
|
+
return clsx("contents", className);
|
|
19
|
+
}
|
|
12
20
|
|
|
13
|
-
;// CONCATENATED MODULE: ./src/DataGridBody/useColumnMeasurement.ts
|
|
14
21
|
|
|
15
22
|
|
|
16
23
|
/**
|
|
@@ -72,4 +79,4 @@ import { DataGridContext } from "../DataGrid/DataGridContext.js";
|
|
|
72
79
|
]);
|
|
73
80
|
}
|
|
74
81
|
|
|
75
|
-
export { useColumnMeasurement };
|
|
82
|
+
export { getBodyClass, useColumnMeasurement };
|
package/dist/298.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
@versini/ui-datagrid v0.8.2
|
|
3
|
+
© 2026 gizmette.com
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { clsx } from "./926.js";
|
|
7
|
+
import { BlurEffects, CellWrapper } from "./46.js";
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Theme mode helpers - reused across many functions.
|
|
13
|
+
*/ const isDark = (m)=>m === "dark";
|
|
14
|
+
const isLight = (m)=>m === "light";
|
|
15
|
+
const isSys = (m)=>m === "system";
|
|
16
|
+
const isAlt = (m)=>m === "alt-system";
|
|
17
|
+
const isDarkish = (m)=>isDark(m) || isSys(m);
|
|
18
|
+
const isLightish = (m)=>isLight(m) || isAlt(m);
|
|
19
|
+
const hasBlurEffect = (b)=>Boolean(b && b !== BlurEffects.NONE);
|
|
20
|
+
const isHeaderCell = (c)=>c === CellWrapper.HEADER;
|
|
21
|
+
const isFooterCell = (c)=>c === CellWrapper.FOOTER;
|
|
22
|
+
/**
|
|
23
|
+
* Text color classes - reused in multiple functions.
|
|
24
|
+
*/ const TXT_LIGHT = "text-copy-light";
|
|
25
|
+
const TXT_DARK = "text-copy-dark";
|
|
26
|
+
const TXT_SYS = "text-copy-light dark:text-copy-dark";
|
|
27
|
+
const TXT_ALT = "text-copy-dark dark:text-copy-light";
|
|
28
|
+
/**
|
|
29
|
+
* Text color classes based on theme mode.
|
|
30
|
+
*/ const getTextColorClasses = ({ mode })=>clsx({
|
|
31
|
+
[TXT_LIGHT]: isDark(mode),
|
|
32
|
+
[TXT_DARK]: isLight(mode),
|
|
33
|
+
[TXT_SYS]: isSys(mode),
|
|
34
|
+
[TXT_ALT]: isAlt(mode)
|
|
35
|
+
});
|
|
36
|
+
const getTextColorClassesForHeaderFooter = ({ mode, hasBlur })=>clsx({
|
|
37
|
+
[TXT_DARK]: isDark(mode) && !hasBlur || isLight(mode),
|
|
38
|
+
[TXT_SYS]: isSys(mode) && hasBlur,
|
|
39
|
+
"text-copy-dark dark:text-copy-dark": (isSys(mode) || isAlt(mode)) && !hasBlur
|
|
40
|
+
});
|
|
41
|
+
/**
|
|
42
|
+
* Surface background classes (main container backgrounds).
|
|
43
|
+
*/ const getSurfaceBackgroundClasses = ({ mode })=>clsx({
|
|
44
|
+
"bg-surface-darker": isDarkish(mode),
|
|
45
|
+
"bg-surface-light": isLightish(mode),
|
|
46
|
+
"dark:bg-surface-light": isSys(mode),
|
|
47
|
+
"dark:bg-surface-darker": isAlt(mode)
|
|
48
|
+
});
|
|
49
|
+
/**
|
|
50
|
+
* Header/footer background classes (with optional blur transparency).
|
|
51
|
+
*/ const getHeaderFooterBackgroundClasses = ({ hasBlur, sticky })=>clsx({
|
|
52
|
+
"bg-surface-medium/50 dark:bg-surface-medium/50": hasBlur && sticky,
|
|
53
|
+
"bg-surface-medium-light dark:bg-surface-medium-light": !hasBlur
|
|
54
|
+
});
|
|
55
|
+
/**
|
|
56
|
+
* Border color classes for rows.
|
|
57
|
+
*/ const getBorderClasses = ({ mode })=>clsx({
|
|
58
|
+
"border-table-dark": isDarkish(mode),
|
|
59
|
+
"border-table-light": isLightish(mode),
|
|
60
|
+
"dark:border-table-light": isSys(mode),
|
|
61
|
+
"dark:border-table-dark": isAlt(mode)
|
|
62
|
+
});
|
|
63
|
+
/**
|
|
64
|
+
* Blur effect classes for sticky elements.
|
|
65
|
+
*/ const getBlurClasses = ({ blurEffect })=>clsx({
|
|
66
|
+
"backdrop-blur-sm": blurEffect === BlurEffects.SMALL,
|
|
67
|
+
"backdrop-blur-md": blurEffect === BlurEffects.MEDIUM,
|
|
68
|
+
"backdrop-blur-lg": blurEffect === BlurEffects.LARGE
|
|
69
|
+
});
|
|
70
|
+
/**
|
|
71
|
+
* Loading text classes for loading state.
|
|
72
|
+
*/ const getLoadingTextClasses = ({ mode })=>clsx("text-lg font-medium", {
|
|
73
|
+
[TXT_DARK]: isLight(mode),
|
|
74
|
+
[TXT_LIGHT]: isDark(mode),
|
|
75
|
+
[TXT_ALT]: isAlt(mode),
|
|
76
|
+
[TXT_SYS]: isSys(mode)
|
|
77
|
+
});
|
|
78
|
+
/**
|
|
79
|
+
* Overlay classes for loading state.
|
|
80
|
+
*/ const getOverlayClasses = ({ mode })=>({
|
|
81
|
+
inner: "relative",
|
|
82
|
+
overlay: clsx("absolute inset-0 z-20 cursor-not-allowed rounded-lg", "backdrop-blur-xs bg-white/30 dark:bg-black/30"),
|
|
83
|
+
loadingWrapper: clsx("absolute z-30 top-0 left-0 right-0 h-[min(100%,100vh)]", "flex items-center justify-center", "pointer-events-none"),
|
|
84
|
+
loadingText: getLoadingTextClasses({
|
|
85
|
+
mode
|
|
86
|
+
})
|
|
87
|
+
});
|
|
88
|
+
/**
|
|
89
|
+
* Generates classes for the main DataGrid wrapper and grid.
|
|
90
|
+
*/ const getDataGridClasses = ({ mode, className, wrapperClassName, stickyHeader, stickyFooter, loading })=>{
|
|
91
|
+
const overlayClasses = loading ? getOverlayClasses({
|
|
92
|
+
mode
|
|
93
|
+
}) : null;
|
|
94
|
+
const hasSticky = stickyHeader || stickyFooter;
|
|
95
|
+
return {
|
|
96
|
+
overlay: overlayClasses?.overlay ?? "",
|
|
97
|
+
inner: overlayClasses?.inner ?? "",
|
|
98
|
+
loadingWrapper: overlayClasses?.loadingWrapper ?? "",
|
|
99
|
+
loadingText: overlayClasses?.loadingText ?? "",
|
|
100
|
+
wrapper: clsx("not-prose relative w-full rounded-lg shadow-md", {
|
|
101
|
+
"overflow-x-auto": !hasSticky && !loading,
|
|
102
|
+
"overflow-hidden flex flex-col": hasSticky || loading
|
|
103
|
+
}, getSurfaceBackgroundClasses({
|
|
104
|
+
mode
|
|
105
|
+
}), getTextColorClasses({
|
|
106
|
+
mode
|
|
107
|
+
}), wrapperClassName),
|
|
108
|
+
scrollableContent: clsx("overflow-y-auto overflow-x-hidden rounded-[inherit] flex-1 min-h-0"),
|
|
109
|
+
grid: clsx("my-0 w-full text-left text-sm grid", className, getTextColorClasses({
|
|
110
|
+
mode
|
|
111
|
+
}))
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Generates classes for the DataGridHeader wrapper.
|
|
116
|
+
*/ const getHeaderClasses = ({ className, stickyHeader, mode, blurEffect })=>{
|
|
117
|
+
const hasBlur = hasBlurEffect(blurEffect);
|
|
118
|
+
const textClasses = getTextColorClassesForHeaderFooter({
|
|
119
|
+
mode,
|
|
120
|
+
hasBlur
|
|
121
|
+
});
|
|
122
|
+
if (!stickyHeader) {
|
|
123
|
+
return clsx("contents", getHeaderFooterBackgroundClasses({
|
|
124
|
+
hasBlur,
|
|
125
|
+
sticky: false
|
|
126
|
+
}), textClasses, className);
|
|
127
|
+
}
|
|
128
|
+
return clsx("flex flex-col border-b border-table-medium shadow-md", "absolute left-0 right-0 z-20 top-0 rounded-t-lg", getHeaderFooterBackgroundClasses({
|
|
129
|
+
hasBlur,
|
|
130
|
+
sticky: true
|
|
131
|
+
}), textClasses, getBlurClasses({
|
|
132
|
+
blurEffect
|
|
133
|
+
}), className);
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Generates classes for the caption element inside DataGridHeader.
|
|
137
|
+
*/ const getCaptionClasses = ({ captionClassName, mode, hasBlur, stickyHeader })=>clsx("py-2 text-sm text-center font-bold col-span-full", getTextColorClassesForHeaderFooter({
|
|
138
|
+
mode,
|
|
139
|
+
hasBlur
|
|
140
|
+
}), !stickyHeader && getHeaderFooterBackgroundClasses({
|
|
141
|
+
hasBlur,
|
|
142
|
+
sticky: false
|
|
143
|
+
}), captionClassName);
|
|
144
|
+
/**
|
|
145
|
+
* Generates classes for the DataGridFooter.
|
|
146
|
+
*/ const getFooterClasses = ({ className, stickyFooter, mode, blurEffect })=>{
|
|
147
|
+
const hasBlur = hasBlurEffect(blurEffect);
|
|
148
|
+
const textClasses = getTextColorClassesForHeaderFooter({
|
|
149
|
+
mode,
|
|
150
|
+
hasBlur
|
|
151
|
+
});
|
|
152
|
+
if (!stickyFooter) {
|
|
153
|
+
return clsx("contents", textClasses, className);
|
|
154
|
+
}
|
|
155
|
+
return clsx("flex flex-col border-t border-table-medium", "absolute left-0 right-0 z-20 bottom-0 rounded-b-lg", "shadow-[0_-10px_15px_-3px_rgba(0,0,0,0.1)]", getHeaderFooterBackgroundClasses({
|
|
156
|
+
hasBlur,
|
|
157
|
+
sticky: true
|
|
158
|
+
}), getBlurClasses({
|
|
159
|
+
blurEffect
|
|
160
|
+
}), textClasses, className);
|
|
161
|
+
};
|
|
162
|
+
const ROW_LAYOUT = "group grid items-center";
|
|
163
|
+
/**
|
|
164
|
+
* Generates classes for DataGridRow.
|
|
165
|
+
*/ const getRowClasses = ({ mode, className, cellWrapper, isLastRow, stickyHeader, stickyFooter })=>{
|
|
166
|
+
if (isHeaderCell(cellWrapper) || isFooterCell(cellWrapper)) {
|
|
167
|
+
const isHeader = isHeaderCell(cellWrapper);
|
|
168
|
+
const parentUsesContents = isHeader ? !stickyHeader : !stickyFooter;
|
|
169
|
+
if (parentUsesContents) {
|
|
170
|
+
return clsx(ROW_LAYOUT, getHeaderFooterBackgroundClasses({
|
|
171
|
+
hasBlur: false,
|
|
172
|
+
sticky: false
|
|
173
|
+
}), isHeader ? "border-b border-table-medium shadow-md" : "border-t border-table-medium", className);
|
|
174
|
+
}
|
|
175
|
+
return clsx(ROW_LAYOUT, className);
|
|
176
|
+
}
|
|
177
|
+
const borderClasses = isLastRow !== undefined ? isLastRow ? "border-b border-b-transparent dark:border-b-transparent" : "border-b" : "border-b last:border-0";
|
|
178
|
+
return clsx(ROW_LAYOUT, borderClasses, "hover:bg-surface-medium hover:text-copy-light", getBorderClasses({
|
|
179
|
+
mode
|
|
180
|
+
}), className);
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* Generates classes for DataGridCell.
|
|
184
|
+
*/ const getCellClasses = ({ cellWrapper, className, compact, align, mode, borderLeft, borderRight })=>{
|
|
185
|
+
return clsx({
|
|
186
|
+
"px-2 py-1": compact,
|
|
187
|
+
"px-4 py-3": !compact
|
|
188
|
+
}, {
|
|
189
|
+
"text-left justify-start": align === "left" || !align,
|
|
190
|
+
"text-center justify-center": align === "center",
|
|
191
|
+
"text-right justify-end": align === "right"
|
|
192
|
+
}, {
|
|
193
|
+
"font-bold": isHeaderCell(cellWrapper) || isFooterCell(cellWrapper)
|
|
194
|
+
}, "first:group-data-active:relative", "first:group-data-active:before:absolute first:group-data-active:before:left-0 first:group-data-active:before:top-0 first:group-data-active:before:bottom-0 first:group-data-active:before:w-1", "first:group-data-active:self-stretch first:group-data-active:flex first:group-data-active:items-center", {
|
|
195
|
+
"first:group-data-active:before:bg-table-active-dark": isDark(mode),
|
|
196
|
+
"first:group-data-active:before:bg-table-active-light": isLight(mode),
|
|
197
|
+
"first:group-data-active:before:bg-table-active-dark dark:first:group-data-active:before:bg-table-active-light": isSys(mode),
|
|
198
|
+
"first:group-data-active:before:bg-table-active-light dark:first:group-data-active:before:bg-table-active-dark": isAlt(mode)
|
|
199
|
+
}, {
|
|
200
|
+
"self-stretch flex items-center": borderLeft || borderRight,
|
|
201
|
+
"border-l border-l-table-dark": borderLeft && isDark(mode),
|
|
202
|
+
"border-l border-l-table-light": borderLeft && isLight(mode),
|
|
203
|
+
"border-l border-l-table-dark dark:border-l-table-light": borderLeft && isSys(mode),
|
|
204
|
+
"border-l border-l-table-light dark:border-l-table-dark": borderLeft && isAlt(mode),
|
|
205
|
+
"border-r border-r-table-dark": borderRight && isDark(mode),
|
|
206
|
+
"border-r border-r-table-light": borderRight && isLight(mode),
|
|
207
|
+
"border-r border-r-table-dark dark:border-r-table-light": borderRight && isSys(mode),
|
|
208
|
+
"border-r border-r-table-light dark:border-r-table-dark": borderRight && isAlt(mode)
|
|
209
|
+
}, className);
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* Returns the appropriate ARIA role for the cell based on the cell wrapper type.
|
|
213
|
+
*/ const getCellRole = (cellWrapper)=>isHeaderCell(cellWrapper) ? "columnheader" : "gridcell";
|
|
214
|
+
|
|
215
|
+
export { getCaptionClasses, getCellClasses, getCellRole, getDataGridClasses, getFooterClasses, getHeaderClasses, getRowClasses, getTextColorClassesForHeaderFooter };
|
package/dist/511.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
@versini/ui-datagrid v0.8.2
|
|
3
|
+
© 2026 gizmette.com
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
export { createContext, default as react, forwardRef, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
|
package/dist/926.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
@versini/ui-datagrid v0.8.2
|
|
3
|
+
© 2026 gizmette.com
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createContext } from "./511.js";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const DataGridContext = createContext({
|
|
11
|
+
mode: "system"
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export { DataGridContext };
|
|
15
|
+
export { default as clsx } from "clsx";
|
package/dist/DataGrid/index.js
CHANGED
|
@@ -1,17 +1,175 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
@versini/ui-datagrid v0.8.
|
|
2
|
+
@versini/ui-datagrid v0.8.2
|
|
3
3
|
© 2026 gizmette.com
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { jsxs, Fragment, useState, useCallback, useMemo, jsx } from "../511.js";
|
|
7
|
+
import { BlurEffects } from "../46.js";
|
|
8
|
+
import { getDataGridClasses } from "../298.js";
|
|
9
|
+
import { DataGridContext } from "../926.js";
|
|
8
10
|
|
|
9
|
-
;// CONCATENATED MODULE: external "./DataGrid.js"
|
|
10
11
|
|
|
11
|
-
;// CONCATENATED MODULE: external "./DataGridContext.js"
|
|
12
12
|
|
|
13
|
-
;// CONCATENATED MODULE: ./src/DataGrid/index.ts
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
/* =============================================================================
|
|
17
|
+
* DataGrid (main component)
|
|
18
|
+
* ========================================================================== */ const DataGrid = ({ className, wrapperClassName, children, mode = "system", compact = false, stickyHeader = false, stickyFooter = false, blurEffect = BlurEffects.NONE, maxHeight, loading = false, columns, ...rest })=>{
|
|
19
|
+
/**
|
|
20
|
+
* Track registered header/footer components via context registration. Uses
|
|
21
|
+
* counter-based tracking to properly handle multiple instances. Components
|
|
22
|
+
* register themselves when they mount, regardless of nesting depth. This
|
|
23
|
+
* replaces the previous displayName-based child inspection approach.
|
|
24
|
+
*/ const [headerCount, setHeaderCount] = useState(0);
|
|
25
|
+
const [footerCount, setFooterCount] = useState(0);
|
|
26
|
+
/**
|
|
27
|
+
* Track measured heights of header/footer for dynamic padding. Reported by
|
|
28
|
+
* DataGridHeader/Footer via ResizeObserver. This replaces the brittle
|
|
29
|
+
* hard-coded Tailwind padding classes.
|
|
30
|
+
*/ const [headerHeight, setHeaderHeight] = useState(0);
|
|
31
|
+
const [footerHeight, setFooterHeight] = useState(0);
|
|
32
|
+
/**
|
|
33
|
+
* Track measured column widths from the body. Used by sticky header/footer to
|
|
34
|
+
* sync column widths since absolutely positioned elements can't use CSS
|
|
35
|
+
* subgrid.
|
|
36
|
+
*/ const [measuredColumnWidths, setMeasuredColumnWidths] = useState([]);
|
|
37
|
+
/**
|
|
38
|
+
* Registration callbacks with stable references. Called by
|
|
39
|
+
* DataGridHeader/DataGridFooter on mount/unmount. Uses increment/decrement to
|
|
40
|
+
* handle multiple instances correctly.
|
|
41
|
+
*/ const registerHeader = useCallback(()=>setHeaderCount((c)=>c + 1), []);
|
|
42
|
+
const unregisterHeader = useCallback(()=>setHeaderCount((c)=>c - 1), []);
|
|
43
|
+
const registerFooter = useCallback(()=>setFooterCount((c)=>c + 1), []);
|
|
44
|
+
const unregisterFooter = useCallback(()=>setFooterCount((c)=>c - 1), []);
|
|
45
|
+
const hasRegisteredHeader = headerCount > 0;
|
|
46
|
+
const hasRegisteredFooter = footerCount > 0;
|
|
47
|
+
/**
|
|
48
|
+
* Only apply sticky behavior if both the prop is true AND the corresponding
|
|
49
|
+
* component exists. This prevents adding padding/styles for non-existent
|
|
50
|
+
* headers/footers.
|
|
51
|
+
*/ const effectiveStickyHeader = stickyHeader && hasRegisteredHeader;
|
|
52
|
+
const effectiveStickyFooter = stickyFooter && hasRegisteredFooter;
|
|
53
|
+
/**
|
|
54
|
+
* State to hold the caption ID registered by DataGridHeader. Used for
|
|
55
|
+
* aria-labelledby on the grid element for accessibility.
|
|
56
|
+
*/ const [captionId, setCaptionId] = useState(undefined);
|
|
57
|
+
const handleSetCaptionId = useCallback((id)=>{
|
|
58
|
+
setCaptionId(id);
|
|
59
|
+
}, []);
|
|
60
|
+
const classes = useMemo(()=>getDataGridClasses({
|
|
61
|
+
mode,
|
|
62
|
+
className,
|
|
63
|
+
wrapperClassName,
|
|
64
|
+
stickyHeader: effectiveStickyHeader,
|
|
65
|
+
stickyFooter: effectiveStickyFooter,
|
|
66
|
+
loading: Boolean(loading)
|
|
67
|
+
}), [
|
|
68
|
+
mode,
|
|
69
|
+
className,
|
|
70
|
+
wrapperClassName,
|
|
71
|
+
effectiveStickyHeader,
|
|
72
|
+
effectiveStickyFooter,
|
|
73
|
+
loading
|
|
74
|
+
]);
|
|
75
|
+
const contextValue = useMemo(()=>({
|
|
76
|
+
mode,
|
|
77
|
+
compact,
|
|
78
|
+
stickyHeader: effectiveStickyHeader,
|
|
79
|
+
stickyFooter: effectiveStickyFooter,
|
|
80
|
+
blurEffect,
|
|
81
|
+
columns,
|
|
82
|
+
measuredColumnWidths,
|
|
83
|
+
setCaptionId: handleSetCaptionId,
|
|
84
|
+
registerHeader,
|
|
85
|
+
unregisterHeader,
|
|
86
|
+
registerFooter,
|
|
87
|
+
unregisterFooter,
|
|
88
|
+
setHeaderHeight,
|
|
89
|
+
setFooterHeight,
|
|
90
|
+
setMeasuredColumnWidths
|
|
91
|
+
}), [
|
|
92
|
+
mode,
|
|
93
|
+
compact,
|
|
94
|
+
effectiveStickyHeader,
|
|
95
|
+
effectiveStickyFooter,
|
|
96
|
+
blurEffect,
|
|
97
|
+
columns,
|
|
98
|
+
measuredColumnWidths,
|
|
99
|
+
handleSetCaptionId,
|
|
100
|
+
registerHeader,
|
|
101
|
+
unregisterHeader,
|
|
102
|
+
registerFooter,
|
|
103
|
+
unregisterFooter
|
|
104
|
+
]);
|
|
105
|
+
const wrapperStyle = maxHeight ? {
|
|
106
|
+
maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight
|
|
107
|
+
} : undefined;
|
|
108
|
+
/**
|
|
109
|
+
* Dynamic padding for scrollable content based on measured header/footer
|
|
110
|
+
* heights. This replaces the brittle hard-coded Tailwind padding classes.
|
|
111
|
+
*/ const scrollableContentStyle = {
|
|
112
|
+
...wrapperStyle,
|
|
113
|
+
paddingTop: effectiveStickyHeader ? headerHeight : undefined,
|
|
114
|
+
paddingBottom: effectiveStickyFooter ? footerHeight : undefined
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* When sticky header/footer is enabled, use Panel-like structure: - Outer
|
|
118
|
+
* wrapper has overflow-hidden - Scrollable content area in the middle with
|
|
119
|
+
* padding - Header/footer are absolutely positioned.
|
|
120
|
+
*/ const hasSticky = effectiveStickyHeader || effectiveStickyFooter;
|
|
121
|
+
/**
|
|
122
|
+
* When columns are provided, apply grid-template-columns at the grid level so
|
|
123
|
+
* all rows can use subgrid to inherit the same column sizing.
|
|
124
|
+
*/ const gridStyle = columns ? {
|
|
125
|
+
gridTemplateColumns: columns.join(" ")
|
|
126
|
+
} : undefined;
|
|
127
|
+
const gridContent = /*#__PURE__*/ jsx("div", {
|
|
128
|
+
role: "grid",
|
|
129
|
+
"aria-labelledby": captionId,
|
|
130
|
+
className: classes.grid,
|
|
131
|
+
style: gridStyle,
|
|
132
|
+
...rest,
|
|
133
|
+
children: children
|
|
134
|
+
});
|
|
135
|
+
const loadingText = typeof loading === "string" ? loading : loading ? "Loading..." : null;
|
|
136
|
+
return /*#__PURE__*/ jsx(DataGridContext.Provider, {
|
|
137
|
+
value: contextValue,
|
|
138
|
+
children: /*#__PURE__*/ jsxs("div", {
|
|
139
|
+
className: classes.inner,
|
|
140
|
+
children: [
|
|
141
|
+
loading && /*#__PURE__*/ jsxs(Fragment, {
|
|
142
|
+
children: [
|
|
143
|
+
/*#__PURE__*/ jsx("div", {
|
|
144
|
+
className: classes.overlay,
|
|
145
|
+
"aria-hidden": "true"
|
|
146
|
+
}),
|
|
147
|
+
/*#__PURE__*/ jsx("div", {
|
|
148
|
+
className: classes.loadingWrapper,
|
|
149
|
+
children: /*#__PURE__*/ jsx("span", {
|
|
150
|
+
className: classes.loadingText,
|
|
151
|
+
role: "status",
|
|
152
|
+
children: loadingText
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
]
|
|
156
|
+
}),
|
|
157
|
+
/*#__PURE__*/ jsx("div", {
|
|
158
|
+
className: classes.wrapper,
|
|
159
|
+
style: wrapperStyle,
|
|
160
|
+
children: hasSticky ? /*#__PURE__*/ jsx("div", {
|
|
161
|
+
className: classes.scrollableContent,
|
|
162
|
+
style: scrollableContentStyle,
|
|
163
|
+
children: gridContent
|
|
164
|
+
}) : gridContent
|
|
165
|
+
})
|
|
166
|
+
]
|
|
167
|
+
})
|
|
168
|
+
});
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
export { DataGrid };
|
|
175
|
+
export { DataGridContext } from "../926.js";
|
|
@@ -1,16 +1,169 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
@versini/ui-datagrid v0.8.
|
|
2
|
+
@versini/ui-datagrid v0.8.2
|
|
3
3
|
© 2026 gizmette.com
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import { useAnimatedHeight } from "./useAnimatedHeight.js";
|
|
6
|
+
import { useState, useLayoutEffect, useRef, jsx } from "../511.js";
|
|
8
7
|
|
|
9
|
-
;// CONCATENATED MODULE: external "./AnimatedWrapper.js"
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
const DEFAULT_ANIMATION_DURATION = 300;
|
|
10
|
+
/**
|
|
11
|
+
* Hook that provides smooth height animations when content changes.
|
|
12
|
+
*
|
|
13
|
+
* Uses FLIP technique: captures height before render via a persistent ref, then
|
|
14
|
+
* animates from old height to new height after the DOM updates.
|
|
15
|
+
*
|
|
16
|
+
* @param dependency - Value that triggers animation when changed.
|
|
17
|
+
* @param options - Configuration options.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* const items = useMemo(() => data.slice(0, visibleCount), [data, visibleCount]);
|
|
22
|
+
* const { ref, style } = useAnimatedHeight(items.length);
|
|
23
|
+
*
|
|
24
|
+
* return (
|
|
25
|
+
* <div ref={ref} style={style}>
|
|
26
|
+
* {items.map((item) => <Row key={item.id} {...item} />)}
|
|
27
|
+
* </div>
|
|
28
|
+
* );
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
*/ function useAnimatedHeight(dependency, options) {
|
|
32
|
+
const { duration = DEFAULT_ANIMATION_DURATION, enabled = true } = options ?? {};
|
|
33
|
+
const ref = useRef(null);
|
|
34
|
+
const [animationState, setAnimationState] = useState({
|
|
35
|
+
height: "auto",
|
|
36
|
+
isAnimating: false
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Track the last measured height persistently. This ref is updated AFTER each
|
|
40
|
+
* animation completes, so it holds the "before" value when dependency changes.
|
|
41
|
+
*/ const lastHeightRef = useRef(0);
|
|
42
|
+
const prevDependencyRef = useRef(dependency);
|
|
43
|
+
const animationFrameRef = useRef(0);
|
|
44
|
+
const timeoutRef = useRef(null);
|
|
45
|
+
useLayoutEffect(()=>{
|
|
46
|
+
if (!ref.current || !enabled) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* On first render or when not animating, just record the current height.
|
|
51
|
+
*/ if (lastHeightRef.current === 0) {
|
|
52
|
+
lastHeightRef.current = ref.current.offsetHeight;
|
|
53
|
+
prevDependencyRef.current = dependency;
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* If dependency hasn't changed, nothing to animate.
|
|
58
|
+
*/ if (prevDependencyRef.current === dependency) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
prevDependencyRef.current = dependency;
|
|
62
|
+
const previousHeight = lastHeightRef.current;
|
|
63
|
+
const newHeight = ref.current.scrollHeight;
|
|
64
|
+
/**
|
|
65
|
+
* Update the stored height for next time.
|
|
66
|
+
*/ lastHeightRef.current = newHeight;
|
|
67
|
+
/**
|
|
68
|
+
* If heights are the same or previous was 0, no animation needed.
|
|
69
|
+
*/ if (previousHeight === newHeight || previousHeight === 0) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Cancel any ongoing animation.
|
|
74
|
+
*/ if (timeoutRef.current) {
|
|
75
|
+
clearTimeout(timeoutRef.current);
|
|
76
|
+
}
|
|
77
|
+
if (animationFrameRef.current) {
|
|
78
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Lock to the previous height immediately.
|
|
82
|
+
*/ setAnimationState({
|
|
83
|
+
height: previousHeight,
|
|
84
|
+
isAnimating: true
|
|
85
|
+
});
|
|
86
|
+
/**
|
|
87
|
+
* Use double RAF to ensure the browser has painted the locked height, then
|
|
88
|
+
* transition to the new height.
|
|
89
|
+
*/ animationFrameRef.current = requestAnimationFrame(()=>{
|
|
90
|
+
animationFrameRef.current = requestAnimationFrame(()=>{
|
|
91
|
+
setAnimationState({
|
|
92
|
+
height: newHeight,
|
|
93
|
+
isAnimating: true
|
|
94
|
+
});
|
|
95
|
+
timeoutRef.current = setTimeout(()=>{
|
|
96
|
+
setAnimationState({
|
|
97
|
+
height: "auto",
|
|
98
|
+
isAnimating: false
|
|
99
|
+
});
|
|
100
|
+
}, duration);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
return ()=>{
|
|
104
|
+
if (timeoutRef.current) {
|
|
105
|
+
clearTimeout(timeoutRef.current);
|
|
106
|
+
}
|
|
107
|
+
if (animationFrameRef.current) {
|
|
108
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}, [
|
|
112
|
+
dependency,
|
|
113
|
+
duration,
|
|
114
|
+
enabled
|
|
115
|
+
]);
|
|
116
|
+
const style = enabled ? {
|
|
117
|
+
height: animationState.height === "auto" ? "auto" : `${animationState.height}px`,
|
|
118
|
+
overflow: animationState.isAnimating ? "hidden" : undefined,
|
|
119
|
+
transition: animationState.isAnimating ? `height ${duration}ms ease-out` : undefined
|
|
120
|
+
} : {};
|
|
121
|
+
return {
|
|
122
|
+
ref,
|
|
123
|
+
style,
|
|
124
|
+
isAnimating: animationState.isAnimating
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Wrapper component that animates height changes when content changes.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```tsx
|
|
135
|
+
* const [visibleCount, setVisibleCount] = useState(0);
|
|
136
|
+
*
|
|
137
|
+
* return (
|
|
138
|
+
* <AnimatedWrapper dependency={visibleCount}>
|
|
139
|
+
* <DataGrid maxHeight="400px" stickyHeader>
|
|
140
|
+
* <DataGridInfiniteBody
|
|
141
|
+
* data={largeData}
|
|
142
|
+
* batchSize={25}
|
|
143
|
+
* onVisibleCountChange={(count) => setVisibleCount(count)}
|
|
144
|
+
* >
|
|
145
|
+
* {(row) => (
|
|
146
|
+
* <DataGridRow key={row.id}>...</DataGridRow>
|
|
147
|
+
* )}
|
|
148
|
+
* </DataGridInfiniteBody>
|
|
149
|
+
* </DataGrid>
|
|
150
|
+
* </AnimatedWrapper>
|
|
151
|
+
* );
|
|
152
|
+
* ```
|
|
153
|
+
*
|
|
154
|
+
*/ const AnimatedWrapper = ({ children, dependency, duration, enabled = true, className })=>{
|
|
155
|
+
const { ref, style } = useAnimatedHeight(dependency, {
|
|
156
|
+
duration,
|
|
157
|
+
enabled
|
|
158
|
+
});
|
|
159
|
+
return /*#__PURE__*/ jsx("div", {
|
|
160
|
+
ref: ref,
|
|
161
|
+
style: style,
|
|
162
|
+
className: className,
|
|
163
|
+
children: children
|
|
164
|
+
});
|
|
165
|
+
};
|
|
12
166
|
|
|
13
|
-
;// CONCATENATED MODULE: ./src/DataGridAnimated/index.ts
|
|
14
167
|
|
|
15
168
|
|
|
16
169
|
|