@versini/ui-datagrid 0.8.1 → 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 (32) hide show
  1. package/dist/{DataGridBody/useColumnMeasurement.js → 131.js} +14 -7
  2. package/dist/{utilities/classes.js → 298.js} +4 -9
  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/package.json +3 -3
  18. package/dist/DataGrid/DataGrid.js +0 -183
  19. package/dist/DataGrid/DataGridContext.js +0 -16
  20. package/dist/DataGrid/DataGridTypes.js +0 -9
  21. package/dist/DataGridAnimated/AnimatedWrapper.js +0 -53
  22. package/dist/DataGridAnimated/useAnimatedHeight.js +0 -131
  23. package/dist/DataGridBody/DataGridBody.js +0 -55
  24. package/dist/DataGridBody/getBodyClass.js +0 -23
  25. package/dist/DataGridCell/DataGridCell.js +0 -51
  26. package/dist/DataGridCellSort/ButtonSort.js +0 -67
  27. package/dist/DataGridCellSort/DataGridCellSort.js +0 -111
  28. package/dist/DataGridFooter/DataGridFooter.js +0 -98
  29. package/dist/DataGridHeader/DataGridHeader.js +0 -129
  30. package/dist/DataGridInfinite/DataGridInfiniteBody.js +0 -334
  31. package/dist/DataGridRow/DataGridRow.js +0 -108
  32. package/dist/DataGridSorting/sortingUtils.js +0 -234
@@ -1,111 +0,0 @@
1
- /*!
2
- @versini/ui-datagrid v0.8.1
3
- © 2026 gizmette.com
4
- */
5
-
6
- import { jsx, jsxs } from "react/jsx-runtime";
7
- import { IconSort, IconSortDown, IconSortUp } from "@versini/ui-icons";
8
- import clsx from "clsx";
9
- import { DataGridContext } from "../DataGrid/DataGridContext.js";
10
- import { getCellClasses } from "../utilities/classes.js";
11
- import { ButtonSort } from "./ButtonSort.js";
12
-
13
- ;// CONCATENATED MODULE: external "react/jsx-runtime"
14
-
15
- ;// CONCATENATED MODULE: external "@versini/ui-icons"
16
-
17
- ;// CONCATENATED MODULE: external "clsx"
18
-
19
- ;// CONCATENATED MODULE: external "../DataGrid/DataGridContext.js"
20
-
21
- ;// CONCATENATED MODULE: external "../utilities/classes.js"
22
-
23
- ;// CONCATENATED MODULE: external "./ButtonSort.js"
24
-
25
- ;// CONCATENATED MODULE: ./src/DataGridCellSort/DataGridCellSort.tsx
26
-
27
-
28
-
29
-
30
-
31
-
32
- /* =============================================================================
33
- * DataGridCellSort
34
- * ========================================================================== */ const DataGridCellSort = ({ className, children, cellId, onSort, sortDirection, sortedCell, slotLeft, slotRight, buttonClassName, align, ...rest })=>{
35
- const isSorted = sortedCell === cellId;
36
- const handleSort = ()=>{
37
- if (onSort) {
38
- // Convert false to undefined for the callback.
39
- const currentDirection = isSorted && sortDirection ? sortDirection : undefined;
40
- onSort(cellId, currentDirection);
41
- }
42
- };
43
- const getSortIcon = ()=>{
44
- if (isSorted && sortDirection === "asc") {
45
- return /*#__PURE__*/ jsx(IconSortUp, {
46
- size: "size-4",
47
- monotone: true
48
- });
49
- }
50
- if (isSorted && sortDirection === "desc") {
51
- return /*#__PURE__*/ jsx(IconSortDown, {
52
- size: "size-4",
53
- monotone: true
54
- });
55
- }
56
- return /*#__PURE__*/ jsx(IconSort, {
57
- size: "size-4",
58
- monotone: true
59
- });
60
- };
61
- const getAriaSort = ()=>{
62
- if (isSorted && sortDirection === "asc") {
63
- return "ascending";
64
- }
65
- if (isSorted && sortDirection === "desc") {
66
- return "descending";
67
- }
68
- return "other";
69
- };
70
- return /*#__PURE__*/ jsx(DataGridContext.Consumer, {
71
- children: ({ mode, compact, cellWrapper, blurEffect })=>{
72
- const mainClasses = getCellClasses({
73
- cellWrapper,
74
- className,
75
- mode,
76
- compact,
77
- align
78
- });
79
- // Flex container for alignment of button within the cell.
80
- const contentClasses = clsx("flex", {
81
- "justify-start": align === "left" || !align,
82
- "justify-center": align === "center",
83
- "justify-end": align === "right"
84
- });
85
- return /*#__PURE__*/ jsx("div", {
86
- className: mainClasses,
87
- role: "columnheader",
88
- "aria-sort": getAriaSort(),
89
- ...rest,
90
- children: /*#__PURE__*/ jsxs("div", {
91
- className: contentClasses,
92
- children: [
93
- slotLeft,
94
- /*#__PURE__*/ jsx(ButtonSort, {
95
- mode: mode,
96
- blurEffect: blurEffect,
97
- active: isSorted,
98
- className: buttonClassName,
99
- onClick: handleSort,
100
- labelRight: children,
101
- children: getSortIcon()
102
- }),
103
- slotRight
104
- ]
105
- })
106
- });
107
- }
108
- });
109
- };
110
-
111
- export { DataGridCellSort };
@@ -1,98 +0,0 @@
1
- /*!
2
- @versini/ui-datagrid v0.8.1
3
- © 2026 gizmette.com
4
- */
5
-
6
- import { jsx } from "react/jsx-runtime";
7
- import { useEffect, useLayoutEffect, useRef } from "react";
8
- import { DataGridContext } from "../DataGrid/DataGridContext.js";
9
- import { CellWrapper } from "../DataGridConstants/DataGridConstants.js";
10
- import { getFooterClasses } from "../utilities/classes.js";
11
-
12
- ;// CONCATENATED MODULE: external "react/jsx-runtime"
13
-
14
- ;// CONCATENATED MODULE: external "react"
15
-
16
- ;// CONCATENATED MODULE: external "../DataGrid/DataGridContext.js"
17
-
18
- ;// CONCATENATED MODULE: external "../DataGridConstants/DataGridConstants.js"
19
-
20
- ;// CONCATENATED MODULE: external "../utilities/classes.js"
21
-
22
- ;// CONCATENATED MODULE: ./src/DataGridFooter/DataGridFooter.tsx
23
-
24
-
25
-
26
-
27
-
28
- /* =============================================================================
29
- * DataGridFooter
30
- * ========================================================================== */ const DataGridFooter = ({ className, children, ...rest })=>{
31
- return /*#__PURE__*/ jsx(DataGridContext.Consumer, {
32
- children: (ctx)=>/*#__PURE__*/ jsx(DataGridFooterInner, {
33
- className: className,
34
- ctx: ctx,
35
- ...rest,
36
- children: children
37
- })
38
- });
39
- };
40
- DataGridFooter.displayName = "DataGridFooter";
41
- /**
42
- * Inner component to handle the useEffect for registering footer. Separated to
43
- * avoid hooks inside Consumer render prop.
44
- */ const DataGridFooterInner = ({ className, ctx, children, ...rest })=>{
45
- const footerRef = useRef(null);
46
- /**
47
- * Register this footer with the parent DataGrid on mount. This enables sticky
48
- * footer behavior regardless of nesting depth.
49
- */ useEffect(()=>{
50
- ctx.registerFooter?.();
51
- return ()=>{
52
- ctx.unregisterFooter?.();
53
- };
54
- }, [
55
- ctx.registerFooter,
56
- ctx.unregisterFooter
57
- ]);
58
- /**
59
- * Measure and report footer height for dynamic padding calculation. Uses
60
- * ResizeObserver to handle dynamic content changes.
61
- */ useLayoutEffect(()=>{
62
- const element = footerRef.current;
63
- if (!element || !ctx.stickyFooter || !ctx.setFooterHeight) {
64
- return;
65
- }
66
- const observer = new ResizeObserver((entries)=>{
67
- const height = entries[0]?.borderBoxSize?.[0]?.blockSize;
68
- if (height !== undefined) {
69
- ctx.setFooterHeight?.(height);
70
- }
71
- });
72
- observer.observe(element);
73
- return ()=>observer.disconnect();
74
- }, [
75
- ctx.stickyFooter,
76
- ctx.setFooterHeight
77
- ]);
78
- return /*#__PURE__*/ jsx(DataGridContext.Provider, {
79
- value: {
80
- ...ctx,
81
- cellWrapper: CellWrapper.FOOTER
82
- },
83
- children: /*#__PURE__*/ jsx("div", {
84
- ref: footerRef,
85
- role: "rowgroup",
86
- className: getFooterClasses({
87
- className,
88
- stickyFooter: ctx.stickyFooter,
89
- mode: ctx.mode,
90
- blurEffect: ctx.blurEffect
91
- }),
92
- ...rest,
93
- children: children
94
- })
95
- });
96
- };
97
-
98
- export { DataGridFooter };
@@ -1,129 +0,0 @@
1
- /*!
2
- @versini/ui-datagrid v0.8.1
3
- © 2026 gizmette.com
4
- */
5
-
6
- import { jsx, jsxs } from "react/jsx-runtime";
7
- import { useEffect, useId, useLayoutEffect, useRef } from "react";
8
- import { DataGridContext } from "../DataGrid/DataGridContext.js";
9
- import { CellWrapper } from "../DataGridConstants/DataGridConstants.js";
10
- import { getCaptionClasses, getHeaderClasses } from "../utilities/classes.js";
11
-
12
- ;// CONCATENATED MODULE: external "react/jsx-runtime"
13
-
14
- ;// CONCATENATED MODULE: external "react"
15
-
16
- ;// CONCATENATED MODULE: external "../DataGrid/DataGridContext.js"
17
-
18
- ;// CONCATENATED MODULE: external "../DataGridConstants/DataGridConstants.js"
19
-
20
- ;// CONCATENATED MODULE: external "../utilities/classes.js"
21
-
22
- ;// CONCATENATED MODULE: ./src/DataGridHeader/DataGridHeader.tsx
23
-
24
-
25
-
26
-
27
-
28
- /* =============================================================================
29
- * DataGridHeader
30
- * ========================================================================== */ const DataGridHeader = ({ caption, captionClassName, className, children, ...rest })=>{
31
- const captionId = useId();
32
- return /*#__PURE__*/ jsx(DataGridContext.Consumer, {
33
- children: (ctx)=>/*#__PURE__*/ jsx(DataGridHeaderInner, {
34
- caption: caption,
35
- captionClassName: captionClassName,
36
- captionId: captionId,
37
- className: className,
38
- ctx: ctx,
39
- ...rest,
40
- children: children
41
- })
42
- });
43
- };
44
- DataGridHeader.displayName = "DataGridHeader";
45
- /**
46
- * Inner component to handle the useEffect for registering captionId and header.
47
- * Separated to avoid hooks inside Consumer render prop.
48
- */ const DataGridHeaderInner = ({ caption, captionClassName, captionId, className, ctx, children, ...rest })=>{
49
- const headerRef = useRef(null);
50
- /**
51
- * Register this header with the parent DataGrid on mount. This enables sticky
52
- * header behavior regardless of nesting depth.
53
- */ useEffect(()=>{
54
- ctx.registerHeader?.();
55
- return ()=>{
56
- ctx.unregisterHeader?.();
57
- };
58
- }, [
59
- ctx.registerHeader,
60
- ctx.unregisterHeader
61
- ]);
62
- // Register the caption ID with the parent DataGrid for aria-labelledby.
63
- useEffect(()=>{
64
- if (caption && ctx.setCaptionId) {
65
- ctx.setCaptionId(captionId);
66
- }
67
- return ()=>{
68
- if (ctx.setCaptionId) {
69
- ctx.setCaptionId(undefined);
70
- }
71
- };
72
- }, [
73
- caption,
74
- captionId,
75
- ctx.setCaptionId
76
- ]);
77
- /**
78
- * Measure and report header height for dynamic padding calculation. Uses
79
- * ResizeObserver to handle dynamic content changes (text wrapping, etc.).
80
- */ useLayoutEffect(()=>{
81
- const element = headerRef.current;
82
- if (!element || !ctx.stickyHeader || !ctx.setHeaderHeight) {
83
- return;
84
- }
85
- const observer = new ResizeObserver((entries)=>{
86
- const height = entries[0]?.borderBoxSize?.[0]?.blockSize;
87
- if (height !== undefined) {
88
- ctx.setHeaderHeight?.(height);
89
- }
90
- });
91
- observer.observe(element);
92
- return ()=>observer.disconnect();
93
- }, [
94
- ctx.stickyHeader,
95
- ctx.setHeaderHeight
96
- ]);
97
- return /*#__PURE__*/ jsx(DataGridContext.Provider, {
98
- value: {
99
- ...ctx,
100
- cellWrapper: CellWrapper.HEADER
101
- },
102
- children: /*#__PURE__*/ jsxs("div", {
103
- ref: headerRef,
104
- role: "rowgroup",
105
- className: getHeaderClasses({
106
- className,
107
- stickyHeader: ctx.stickyHeader,
108
- mode: ctx.mode,
109
- blurEffect: ctx.blurEffect
110
- }),
111
- ...rest,
112
- children: [
113
- caption && /*#__PURE__*/ jsx("div", {
114
- id: captionId,
115
- className: getCaptionClasses({
116
- captionClassName,
117
- mode: ctx.mode,
118
- hasBlur: Boolean(ctx.blurEffect && ctx.blurEffect !== "none"),
119
- stickyHeader: Boolean(ctx.stickyHeader)
120
- }),
121
- children: caption
122
- }),
123
- children
124
- ]
125
- })
126
- });
127
- };
128
-
129
- export { DataGridHeader };
@@ -1,334 +0,0 @@
1
- /*!
2
- @versini/ui-datagrid v0.8.1
3
- © 2026 gizmette.com
4
- */
5
-
6
- import { jsx } from "react/jsx-runtime";
7
- import { forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
8
- import { DataGridContext } from "../DataGrid/DataGridContext.js";
9
- import { getBodyClass } from "../DataGridBody/getBodyClass.js";
10
- import { useColumnMeasurement } from "../DataGridBody/useColumnMeasurement.js";
11
- import { CellWrapper } from "../DataGridConstants/index.js";
12
-
13
- ;// CONCATENATED MODULE: external "react/jsx-runtime"
14
-
15
- ;// CONCATENATED MODULE: external "react"
16
-
17
- ;// CONCATENATED MODULE: external "../DataGrid/DataGridContext.js"
18
-
19
- ;// CONCATENATED MODULE: external "../DataGridBody/getBodyClass.js"
20
-
21
- ;// CONCATENATED MODULE: external "../DataGridBody/useColumnMeasurement.js"
22
-
23
- ;// CONCATENATED MODULE: external "../DataGridConstants/index.js"
24
-
25
- ;// CONCATENATED MODULE: ./src/DataGridInfinite/DataGridInfiniteBody.tsx
26
-
27
-
28
-
29
-
30
-
31
-
32
- const DEFAULT_BATCH_SIZE = 20;
33
- const DEFAULT_THRESHOLD = 5;
34
- const DEFAULT_ROOT_MARGIN = "20px";
35
- const ROW_INDEX_DATA_ATTR = "data-row-index";
36
- /**
37
- * Finds the nearest scrollable ancestor of an element. Returns null if no
38
- * scrollable ancestor is found (uses viewport).
39
- */ function findScrollableAncestor(element) {
40
- let parent = element.parentElement;
41
- while(parent){
42
- const style = getComputedStyle(parent);
43
- const overflowY = style.overflowY;
44
- if (overflowY === "auto" || overflowY === "scroll") {
45
- return parent;
46
- }
47
- parent = parent.parentElement;
48
- }
49
- return null;
50
- }
51
- /**
52
- * A DataGridBody variant that handles infinite scroll internally.
53
- *
54
- * This component manages all the complexity of infinite scroll:
55
- * - Progressive data loading with IntersectionObserver
56
- * - Correct marker placement for seamless scrolling
57
- * - Automatic data slicing and memoization
58
- * - Programmatic scroll-to-row with smooth animation
59
- *
60
- * @example
61
- * ```tsx
62
- * const infiniteBodyRef = useRef<DataGridInfiniteBodyRef>(null);
63
- *
64
- * // Jump to a specific row
65
- * const handleJumpToRow = () => {
66
- * infiniteBodyRef.current?.scrollToIndex(134);
67
- * };
68
- *
69
- * <DataGrid maxHeight="400px" stickyHeader>
70
- * <DataGridHeader caption={`Showing ${visibleCount} of ${data.length}`}>
71
- * <DataGridRow>
72
- * <DataGridCell>Name</DataGridCell>
73
- * <DataGridCell>Role</DataGridCell>
74
- * </DataGridRow>
75
- * </DataGridHeader>
76
- *
77
- * <DataGridInfiniteBody
78
- * ref={infiniteBodyRef}
79
- * data={largeData}
80
- * batchSize={25}
81
- * onVisibleCountChange={(count) => setVisibleCount(count)}
82
- * >
83
- * {(row) => (
84
- * <DataGridRow key={row.id}>
85
- * <DataGridCell>{row.name}</DataGridCell>
86
- * <DataGridCell>{row.role}</DataGridCell>
87
- * </DataGridRow>
88
- * )}
89
- * </DataGridInfiniteBody>
90
- * </DataGrid>
91
- * ```
92
- *
93
- */ function DataGridInfiniteBodyInner({ data, children: renderRow, batchSize = DEFAULT_BATCH_SIZE, threshold = DEFAULT_THRESHOLD, rootMargin = DEFAULT_ROOT_MARGIN, onVisibleCountChange, className }, ref) {
94
- const ctx = useContext(DataGridContext);
95
- const bodyRef = useRef(null);
96
- const observerRef = useRef(null);
97
- const pendingScrollRef = useRef(null);
98
- const totalItems = data.length;
99
- const initialCount = Math.min(batchSize + threshold, totalItems);
100
- const [visibleCount, setVisibleCount] = useState(initialCount);
101
- const hasMore = visibleCount < totalItems;
102
- /**
103
- * Scroll to a row by its index. Called after visibleCount updates.
104
- * NOTE: We query for the wrapper with data-row-index, then scroll to the
105
- * actual row element inside it (since the wrapper has display:contents and
106
- * doesn't have a bounding box).
107
- */ const scrollToRowElement = useCallback((index)=>{
108
- const body = bodyRef.current;
109
- if (!body) {
110
- return;
111
- }
112
- const wrapper = body.querySelector(`[${ROW_INDEX_DATA_ATTR}="${index}"]`);
113
- if (wrapper) {
114
- // The wrapper has display:contents, so scroll to the actual row inside.
115
- const row = wrapper.querySelector('[role="row"]');
116
- if (row) {
117
- row.scrollIntoView({
118
- behavior: "smooth",
119
- block: "center"
120
- });
121
- }
122
- }
123
- }, []);
124
- /**
125
- * Handle pending scroll after render (when visibleCount has been expanded).
126
- */ useEffect(()=>{
127
- if (pendingScrollRef.current !== null) {
128
- const targetIndex = pendingScrollRef.current;
129
- // Only scroll if the target is now within visible range.
130
- if (targetIndex < visibleCount) {
131
- // Use requestAnimationFrame to ensure DOM has updated.
132
- requestAnimationFrame(()=>{
133
- scrollToRowElement(targetIndex);
134
- });
135
- pendingScrollRef.current = null;
136
- }
137
- }
138
- }, [
139
- visibleCount,
140
- scrollToRowElement
141
- ]);
142
- /**
143
- * Expose imperative methods via ref.
144
- */ useImperativeHandle(ref, ()=>({
145
- scrollToIndex: (index)=>{
146
- if (index < 0 || index >= totalItems) {
147
- console.warn(`scrollToIndex: index ${index} is out of bounds (0-${totalItems - 1})`);
148
- return;
149
- }
150
- // If the row is already visible, just scroll to it.
151
- if (index < visibleCount) {
152
- scrollToRowElement(index);
153
- return;
154
- }
155
- /**
156
- * Otherwise, expand visible count to include the target row. Add some
157
- * buffer so the row isn't at the very edge.
158
- */ const targetCount = Math.min(index + threshold + 1, totalItems);
159
- pendingScrollRef.current = index;
160
- setVisibleCount(targetCount);
161
- }
162
- }), [
163
- totalItems,
164
- visibleCount,
165
- threshold,
166
- scrollToRowElement
167
- ]);
168
- // Reset visible count when data changes significantly.
169
- useEffect(()=>{
170
- setVisibleCount(Math.min(batchSize + threshold, totalItems));
171
- }, [
172
- totalItems,
173
- batchSize,
174
- threshold
175
- ]);
176
- // Notify parent of visible count changes.
177
- useEffect(()=>{
178
- onVisibleCountChange?.(visibleCount, totalItems);
179
- }, [
180
- visibleCount,
181
- totalItems,
182
- onVisibleCountChange
183
- ]);
184
- /**
185
- * IntersectionObserver callback - triggered when marker becomes visible. Loads
186
- * the next batch of items.
187
- */ const handleIntersection = useCallback((entries)=>{
188
- const target = entries[0];
189
- if (target?.isIntersecting) {
190
- setVisibleCount((prev)=>Math.min(prev + batchSize, totalItems));
191
- }
192
- }, [
193
- batchSize,
194
- totalItems
195
- ]);
196
- /**
197
- * Callback ref for the marker element. Sets up IntersectionObserver when
198
- * marker mounts, cleans up when it unmounts. This pattern ensures the observer
199
- * always watches the current marker element, even when visibleCount changes
200
- * and a new marker is created at a different position.
201
- */ const markerRefCallback = useCallback((node)=>{
202
- // Clean up previous observer.
203
- if (observerRef.current) {
204
- observerRef.current.disconnect();
205
- observerRef.current = null;
206
- }
207
- // Set up new observer if we have a marker and more items to load.
208
- if (node && hasMore) {
209
- const root = findScrollableAncestor(node);
210
- observerRef.current = new IntersectionObserver(handleIntersection, {
211
- root,
212
- rootMargin
213
- });
214
- observerRef.current.observe(node);
215
- }
216
- }, [
217
- hasMore,
218
- handleIntersection,
219
- rootMargin
220
- ]);
221
- // Clean up observer on unmount.
222
- useEffect(()=>{
223
- return ()=>{
224
- observerRef.current?.disconnect();
225
- };
226
- }, []);
227
- /**
228
- * Calculate marker position. The marker should be placed `threshold` items
229
- * from the end of visible items. This allows seamless scrolling - new items
230
- * load while user scrolls through the remaining `threshold` items.
231
- */ const markerPosition = useMemo(()=>{
232
- if (!hasMore) {
233
- return -1; // No marker needed when all items are loaded.
234
- }
235
- // Place marker at visibleCount - threshold, but ensure it's at least 0.
236
- return Math.max(0, visibleCount - threshold);
237
- }, [
238
- hasMore,
239
- visibleCount,
240
- threshold
241
- ]);
242
- /**
243
- * Context value for body rows (shared base, rowIndex added per-row).
244
- */ const bodyContextBase = useMemo(()=>({
245
- ...ctx,
246
- cellWrapper: CellWrapper.BODY
247
- }), [
248
- ctx
249
- ]);
250
- /**
251
- * Render items with marker at the correct position. Each row gets a
252
- * data-row-index attribute for scrollToIndex functionality. Each row is
253
- * wrapped with a context provider that includes the isLastRow flag for proper
254
- * border removal (CSS :last-child doesn't work with wrappers).
255
- */ const renderedContent = useMemo(()=>{
256
- const result = [];
257
- // Determine the actual last visible index (for border styling).
258
- const lastVisibleIndex = Math.min(visibleCount, totalItems) - 1;
259
- for(let i = 0; i < visibleCount && i < totalItems; i++){
260
- // Insert marker at the calculated position.
261
- if (i === markerPosition) {
262
- result.push(/*#__PURE__*/ jsx("div", {
263
- ref: markerRefCallback,
264
- "aria-hidden": "true",
265
- style: {
266
- height: "1px",
267
- background: "transparent"
268
- }
269
- }, "__infinite-scroll-marker-inline__"));
270
- }
271
- /**
272
- * Determine if this is the last row (only when all data is loaded). If
273
- * hasMore is true, no row is "last" since more will be loaded.
274
- */ const isLastRow = !hasMore && i === lastVisibleIndex;
275
- /**
276
- * Wrap row with context provider that includes rowIndex for proper odd/even
277
- * styling. Using display:contents so the wrapper doesn't affect grid layout.
278
- */ result.push(/*#__PURE__*/ jsx(DataGridContext.Provider, {
279
- value: {
280
- ...bodyContextBase,
281
- isLastRow
282
- },
283
- children: /*#__PURE__*/ jsx("div", {
284
- [ROW_INDEX_DATA_ATTR]: i,
285
- style: {
286
- display: "contents"
287
- },
288
- children: renderRow(data[i], i)
289
- })
290
- }, i));
291
- }
292
- // If marker position is at the end (edge case with small data).
293
- if (markerPosition === visibleCount && hasMore) {
294
- result.push(/*#__PURE__*/ jsx("div", {
295
- ref: markerRefCallback,
296
- "aria-hidden": "true",
297
- style: {
298
- height: "1px",
299
- background: "transparent"
300
- }
301
- }, "__infinite-scroll-marker-end__"));
302
- }
303
- return result;
304
- }, [
305
- data,
306
- visibleCount,
307
- totalItems,
308
- markerPosition,
309
- hasMore,
310
- renderRow,
311
- markerRefCallback,
312
- bodyContextBase
313
- ]);
314
- // Measure column widths for sticky header/footer sync.
315
- useColumnMeasurement(bodyRef, renderedContent);
316
- const bodyClass = getBodyClass(className);
317
- return /*#__PURE__*/ jsx(DataGridContext.Provider, {
318
- value: {
319
- ...ctx,
320
- cellWrapper: CellWrapper.BODY
321
- },
322
- children: /*#__PURE__*/ jsx("div", {
323
- ref: bodyRef,
324
- role: "rowgroup",
325
- className: bodyClass,
326
- children: renderedContent
327
- })
328
- });
329
- }
330
- /**
331
- * DataGridInfiniteBody with forwardRef support for imperative methods.
332
- */ const DataGridInfiniteBody = /*#__PURE__*/ forwardRef(DataGridInfiniteBodyInner);
333
-
334
- export { DataGridInfiniteBody };