@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.
Files changed (34) hide show
  1. package/dist/{DataGridBody/useColumnMeasurement.js → 131.js} +14 -7
  2. package/dist/298.js +215 -0
  3. package/dist/{DataGridConstants/DataGridConstants.js → 46.js} +1 -3
  4. package/dist/511.js +9 -0
  5. package/dist/926.js +15 -0
  6. package/dist/DataGrid/index.js +165 -7
  7. package/dist/DataGridAnimated/index.js +159 -6
  8. package/dist/DataGridBody/index.js +33 -4
  9. package/dist/DataGridCell/index.js +37 -4
  10. package/dist/DataGridCellSort/index.js +138 -4
  11. package/dist/DataGridConstants/index.js +2 -6
  12. package/dist/DataGridFooter/index.js +79 -4
  13. package/dist/DataGridHeader/index.js +110 -4
  14. package/dist/DataGridInfinite/index.js +312 -4
  15. package/dist/DataGridRow/index.js +89 -4
  16. package/dist/DataGridSorting/index.js +225 -7
  17. package/dist/utilities/classes.d.ts +7 -29
  18. package/package.json +3 -3
  19. package/dist/DataGrid/DataGrid.js +0 -183
  20. package/dist/DataGrid/DataGridContext.js +0 -16
  21. package/dist/DataGrid/DataGridTypes.js +0 -9
  22. package/dist/DataGridAnimated/AnimatedWrapper.js +0 -53
  23. package/dist/DataGridAnimated/useAnimatedHeight.js +0 -131
  24. package/dist/DataGridBody/DataGridBody.js +0 -55
  25. package/dist/DataGridBody/getBodyClass.js +0 -23
  26. package/dist/DataGridCell/DataGridCell.js +0 -51
  27. package/dist/DataGridCellSort/ButtonSort.js +0 -67
  28. package/dist/DataGridCellSort/DataGridCellSort.js +0 -111
  29. package/dist/DataGridFooter/DataGridFooter.js +0 -98
  30. package/dist/DataGridHeader/DataGridHeader.js +0 -129
  31. package/dist/DataGridInfinite/DataGridInfiniteBody.js +0 -334
  32. package/dist/DataGridRow/DataGridRow.js +0 -108
  33. package/dist/DataGridSorting/sortingUtils.js +0 -234
  34. package/dist/utilities/classes.js +0 -267
@@ -1,16 +1,23 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.0
2
+ @versini/ui-datagrid v0.8.2
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
6
- import { useContext, useLayoutEffect, useRef } from "react";
7
- import { DataGridContext } from "../DataGrid/DataGridContext.js";
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
- ;// CONCATENATED MODULE: external "../DataGrid/DataGridContext.js"
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 };
@@ -1,10 +1,8 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.0
2
+ @versini/ui-datagrid v0.8.2
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
6
-
7
- ;// CONCATENATED MODULE: ./src/DataGridConstants/DataGridConstants.ts
8
6
  /**
9
7
  * Sort direction constants for DataGrid sorting.
10
8
  */ const SortDirections = {
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";
@@ -1,17 +1,175 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.0
2
+ @versini/ui-datagrid v0.8.2
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
6
- import { DataGrid } from "./DataGrid.js";
7
- import { DataGridContext } from "./DataGridContext.js";
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
- export { DataGrid, DataGridContext };
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.0
2
+ @versini/ui-datagrid v0.8.2
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
6
- import { AnimatedWrapper } from "./AnimatedWrapper.js";
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
- ;// CONCATENATED MODULE: external "./useAnimatedHeight.js"
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