@versini/ui-datagrid 0.3.7 → 0.4.0
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/README.md +106 -29
- package/dist/DataGrid/DataGrid.js +7 -7
- package/dist/DataGrid/DataGridContext.js +1 -1
- package/dist/DataGrid/index.js +1 -1
- package/dist/DataGridAnimated/AnimatedWrapper.d.ts +11 -7
- package/dist/DataGridAnimated/AnimatedWrapper.js +12 -8
- package/dist/DataGridAnimated/index.js +1 -1
- package/dist/DataGridAnimated/useAnimatedHeight.js +1 -1
- package/dist/DataGridBody/DataGridBody.js +12 -54
- package/dist/DataGridBody/getBodyClass.d.ts +9 -0
- package/dist/DataGridBody/getBodyClass.js +23 -0
- package/dist/DataGridBody/index.js +1 -1
- package/dist/DataGridBody/useColumnMeasurement.d.ts +10 -0
- package/dist/DataGridBody/useColumnMeasurement.js +67 -0
- package/dist/DataGridCell/DataGridCell.d.ts +0 -10
- package/dist/DataGridCell/DataGridCell.js +4 -58
- package/dist/DataGridCell/index.js +1 -1
- package/dist/DataGridCellSort/DataGridCellSort.js +3 -3
- package/dist/DataGridCellSort/index.js +1 -1
- package/dist/DataGridConstants/DataGridConstants.js +1 -1
- package/dist/DataGridConstants/index.js +1 -1
- package/dist/DataGridFooter/DataGridFooter.d.ts +0 -15
- package/dist/DataGridFooter/DataGridFooter.js +6 -42
- package/dist/DataGridFooter/index.js +1 -1
- package/dist/DataGridHeader/DataGridHeader.d.ts +0 -27
- package/dist/DataGridHeader/DataGridHeader.js +6 -54
- package/dist/DataGridHeader/index.js +1 -1
- package/dist/DataGridInfinite/DataGridInfiniteBody.d.ts +52 -0
- package/dist/DataGridInfinite/DataGridInfiniteBody.js +309 -0
- package/dist/DataGridInfinite/index.d.ts +2 -4
- package/dist/DataGridInfinite/index.js +4 -8
- package/dist/DataGridRow/DataGridRow.js +10 -34
- package/dist/DataGridRow/index.js +1 -1
- package/dist/DataGridSorting/index.js +1 -1
- package/dist/DataGridSorting/sortingUtils.js +1 -1
- package/dist/utilities/classes.d.ts +150 -0
- package/dist/utilities/classes.js +249 -0
- package/package.json +2 -2
- package/dist/DataGrid/utilities.d.ts +0 -44
- package/dist/DataGrid/utilities.js +0 -92
- package/dist/DataGridInfinite/InfiniteScrollMarker.d.ts +0 -31
- package/dist/DataGridInfinite/InfiniteScrollMarker.js +0 -54
- package/dist/DataGridInfinite/useInfiniteScroll.d.ts +0 -92
- package/dist/DataGridInfinite/useInfiniteScroll.js +0 -136
- package/dist/common/utilities.d.ts +0 -15
- package/dist/common/utilities.js +0 -48
package/README.md
CHANGED
|
@@ -36,10 +36,7 @@ import { DataGridCell } from "@versini/ui-datagrid/cell";
|
|
|
36
36
|
import { DataGridCellSort } from "@versini/ui-datagrid/cell-sort";
|
|
37
37
|
|
|
38
38
|
// Infinite scroll (progressive loading)
|
|
39
|
-
import {
|
|
40
|
-
useInfiniteScroll,
|
|
41
|
-
InfiniteScrollMarker
|
|
42
|
-
} from "@versini/ui-datagrid/infinite";
|
|
39
|
+
import { DataGridInfiniteBody } from "@versini/ui-datagrid/infinite";
|
|
43
40
|
|
|
44
41
|
// Animated height wrapper
|
|
45
42
|
import {
|
|
@@ -138,52 +135,122 @@ Supported column values include:
|
|
|
138
135
|
|
|
139
136
|
## Infinite Scroll
|
|
140
137
|
|
|
141
|
-
For datasets with hundreds to thousands of rows, use progressive loading:
|
|
138
|
+
For datasets with hundreds to thousands of rows, use `DataGridInfiniteBody` for progressive loading:
|
|
142
139
|
|
|
143
140
|
```tsx
|
|
141
|
+
import { useState } from "react";
|
|
144
142
|
import { DataGrid } from "@versini/ui-datagrid/datagrid";
|
|
145
143
|
import { DataGridHeader } from "@versini/ui-datagrid/header";
|
|
146
|
-
import { DataGridBody } from "@versini/ui-datagrid/body";
|
|
147
144
|
import { DataGridRow } from "@versini/ui-datagrid/row";
|
|
148
145
|
import { DataGridCell } from "@versini/ui-datagrid/cell";
|
|
149
|
-
import {
|
|
150
|
-
useInfiniteScroll,
|
|
151
|
-
InfiniteScrollMarker
|
|
152
|
-
} from "@versini/ui-datagrid/infinite";
|
|
146
|
+
import { DataGridInfiniteBody } from "@versini/ui-datagrid/infinite";
|
|
153
147
|
import { AnimatedWrapper } from "@versini/ui-datagrid/animated";
|
|
154
148
|
|
|
155
149
|
function MyInfiniteTable({ data }) {
|
|
156
|
-
const
|
|
157
|
-
totalItems: data.length,
|
|
158
|
-
batchSize: 25
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
const visibleData = data.slice(0, visibleCount);
|
|
150
|
+
const [visibleCount, setVisibleCount] = useState(0);
|
|
162
151
|
|
|
163
152
|
return (
|
|
164
153
|
<AnimatedWrapper dependency={visibleCount}>
|
|
165
154
|
<DataGrid maxHeight="500px" stickyHeader>
|
|
166
|
-
<DataGridHeader>
|
|
155
|
+
<DataGridHeader caption={`Showing ${visibleCount} of ${data.length}`}>
|
|
167
156
|
<DataGridRow>
|
|
168
157
|
<DataGridCell>Name</DataGridCell>
|
|
169
158
|
<DataGridCell>Date</DataGridCell>
|
|
170
159
|
</DataGridRow>
|
|
171
160
|
</DataGridHeader>
|
|
172
|
-
|
|
173
|
-
|
|
161
|
+
|
|
162
|
+
<DataGridInfiniteBody
|
|
163
|
+
data={data}
|
|
164
|
+
batchSize={25}
|
|
165
|
+
threshold={5}
|
|
166
|
+
onVisibleCountChange={(count) => setVisibleCount(count)}
|
|
167
|
+
>
|
|
168
|
+
{(item) => (
|
|
174
169
|
<DataGridRow key={item.id}>
|
|
175
170
|
<DataGridCell>{item.name}</DataGridCell>
|
|
176
171
|
<DataGridCell>{item.date}</DataGridCell>
|
|
177
172
|
</DataGridRow>
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
</DataGridBody>
|
|
173
|
+
)}
|
|
174
|
+
</DataGridInfiniteBody>
|
|
181
175
|
</DataGrid>
|
|
182
176
|
</AnimatedWrapper>
|
|
183
177
|
);
|
|
184
178
|
}
|
|
185
179
|
```
|
|
186
180
|
|
|
181
|
+
The `DataGridInfiniteBody` component handles all the complexity internally:
|
|
182
|
+
- Progressive loading with IntersectionObserver
|
|
183
|
+
- Correct marker placement for seamless scrolling (marker is placed `threshold` items before the end)
|
|
184
|
+
- Automatic data slicing and memoization
|
|
185
|
+
|
|
186
|
+
### Jump to Row
|
|
187
|
+
|
|
188
|
+
Use the `scrollToIndex` imperative method to programmatically scroll to any row, even if it's not yet loaded:
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
import { useRef, useState } from "react";
|
|
192
|
+
import { DataGrid } from "@versini/ui-datagrid/datagrid";
|
|
193
|
+
import { DataGridHeader } from "@versini/ui-datagrid/header";
|
|
194
|
+
import { DataGridRow } from "@versini/ui-datagrid/row";
|
|
195
|
+
import { DataGridCell } from "@versini/ui-datagrid/cell";
|
|
196
|
+
import {
|
|
197
|
+
DataGridInfiniteBody,
|
|
198
|
+
type DataGridInfiniteBodyRef
|
|
199
|
+
} from "@versini/ui-datagrid/infinite";
|
|
200
|
+
|
|
201
|
+
function MyInfiniteTableWithJump({ data }) {
|
|
202
|
+
const [activeRowId, setActiveRowId] = useState(null);
|
|
203
|
+
const infiniteBodyRef = useRef<DataGridInfiniteBodyRef>(null);
|
|
204
|
+
|
|
205
|
+
const handleJumpToRow = (targetId) => {
|
|
206
|
+
// Find the index of the row with the target id
|
|
207
|
+
const targetIndex = data.findIndex((row) => row.id === targetId);
|
|
208
|
+
if (targetIndex !== -1) {
|
|
209
|
+
setActiveRowId(targetId);
|
|
210
|
+
infiniteBodyRef.current?.scrollToIndex(targetIndex);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
return (
|
|
215
|
+
<div>
|
|
216
|
+
<button onClick={() => handleJumpToRow(1341)}>
|
|
217
|
+
Jump to row with id=1341
|
|
218
|
+
</button>
|
|
219
|
+
|
|
220
|
+
<DataGrid maxHeight="400px" stickyHeader>
|
|
221
|
+
<DataGridHeader>
|
|
222
|
+
<DataGridRow>
|
|
223
|
+
<DataGridCell>ID</DataGridCell>
|
|
224
|
+
<DataGridCell>Name</DataGridCell>
|
|
225
|
+
</DataGridRow>
|
|
226
|
+
</DataGridHeader>
|
|
227
|
+
|
|
228
|
+
<DataGridInfiniteBody
|
|
229
|
+
ref={infiniteBodyRef}
|
|
230
|
+
data={data}
|
|
231
|
+
batchSize={25}
|
|
232
|
+
>
|
|
233
|
+
{(item) => (
|
|
234
|
+
<DataGridRow
|
|
235
|
+
key={item.id}
|
|
236
|
+
active={activeRowId === item.id}
|
|
237
|
+
onClick={() => setActiveRowId(item.id)}
|
|
238
|
+
>
|
|
239
|
+
<DataGridCell>{item.id}</DataGridCell>
|
|
240
|
+
<DataGridCell>{item.name}</DataGridCell>
|
|
241
|
+
</DataGridRow>
|
|
242
|
+
)}
|
|
243
|
+
</DataGridInfiniteBody>
|
|
244
|
+
</DataGrid>
|
|
245
|
+
</div>
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
The `scrollToIndex` method:
|
|
251
|
+
- If the row is already visible → smooth scrolls to it immediately
|
|
252
|
+
- If the row is not yet loaded → expands visible count first, then scrolls after render
|
|
253
|
+
|
|
187
254
|
## Sorting
|
|
188
255
|
|
|
189
256
|
```tsx
|
|
@@ -328,14 +395,24 @@ function MySortableTable({ data }) {
|
|
|
328
395
|
| `className` | `string` | - | CSS class for the cell |
|
|
329
396
|
| `buttonClassName` | `string` | - | CSS class for the sort button |
|
|
330
397
|
|
|
331
|
-
###
|
|
398
|
+
### DataGridInfiniteBody
|
|
399
|
+
|
|
400
|
+
| Prop | Type | Default | Description |
|
|
401
|
+
| ---------------------- | ------------------------------------------- | ------- | ------------------------------------------------- |
|
|
402
|
+
| `data` | `T[]` | **required** | The full dataset to render progressively |
|
|
403
|
+
| `children` | `(item: T, index: number) => ReactNode` | **required** | Render function for each row |
|
|
404
|
+
| `batchSize` | `number` | `20` | Items to show initially and add per scroll |
|
|
405
|
+
| `threshold` | `number` | `5` | Items before marker to allow seamless scrolling |
|
|
406
|
+
| `rootMargin` | `string` | `'20px'`| IntersectionObserver margin |
|
|
407
|
+
| `onVisibleCountChange` | `(visibleCount: number, total: number) => void` | - | Callback when visible count changes |
|
|
408
|
+
| `className` | `string` | - | CSS class for the body element |
|
|
409
|
+
| `ref` | `React.Ref<DataGridInfiniteBodyRef>` | - | Ref to access imperative methods |
|
|
410
|
+
|
|
411
|
+
### DataGridInfiniteBodyRef (Imperative Handle)
|
|
332
412
|
|
|
333
|
-
|
|
|
334
|
-
|
|
|
335
|
-
| `
|
|
336
|
-
| `batchSize` | `number` | `20` | Items to load per batch |
|
|
337
|
-
| `threshold` | `number` | `5` | Items before end to trigger load |
|
|
338
|
-
| `rootMargin` | `string` | `'20px'` | IntersectionObserver margin |
|
|
413
|
+
| Method | Signature | Description |
|
|
414
|
+
| --------------- | ---------------------------- | -------------------------------------------------------- |
|
|
415
|
+
| `scrollToIndex` | `(index: number) => void` | Scroll to a row by index. Expands visible count if needed, then smooth scrolls. |
|
|
339
416
|
|
|
340
417
|
## License
|
|
341
418
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
@versini/ui-datagrid v0.
|
|
2
|
+
@versini/ui-datagrid v0.4.0
|
|
3
3
|
© 2026 gizmette.com
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { useCallback, useMemo, useState } from "react";
|
|
8
8
|
import { BlurEffects } from "../DataGridConstants/DataGridConstants.js";
|
|
9
|
+
import { getDataGridClasses } from "../utilities/classes.js";
|
|
9
10
|
import { DataGridContext } from "./DataGridContext.js";
|
|
10
|
-
import { getDataGridClasses } from "./utilities.js";
|
|
11
11
|
|
|
12
12
|
;// CONCATENATED MODULE: external "react/jsx-runtime"
|
|
13
13
|
|
|
@@ -15,9 +15,9 @@ import { getDataGridClasses } from "./utilities.js";
|
|
|
15
15
|
|
|
16
16
|
;// CONCATENATED MODULE: external "../DataGridConstants/DataGridConstants.js"
|
|
17
17
|
|
|
18
|
-
;// CONCATENATED MODULE: external "
|
|
18
|
+
;// CONCATENATED MODULE: external "../utilities/classes.js"
|
|
19
19
|
|
|
20
|
-
;// CONCATENATED MODULE: external "./
|
|
20
|
+
;// CONCATENATED MODULE: external "./DataGridContext.js"
|
|
21
21
|
|
|
22
22
|
;// CONCATENATED MODULE: ./src/DataGrid/DataGrid.tsx
|
|
23
23
|
|
|
@@ -42,9 +42,9 @@ import { getDataGridClasses } from "./utilities.js";
|
|
|
42
42
|
*/ const [headerHeight, setHeaderHeight] = useState(0);
|
|
43
43
|
const [footerHeight, setFooterHeight] = useState(0);
|
|
44
44
|
/**
|
|
45
|
-
* Track measured column widths from the body. Used by sticky header/footer
|
|
46
|
-
*
|
|
47
|
-
*
|
|
45
|
+
* Track measured column widths from the body. Used by sticky header/footer to
|
|
46
|
+
* sync column widths since absolutely positioned elements can't use CSS
|
|
47
|
+
* subgrid.
|
|
48
48
|
*/ const [measuredColumnWidths, setMeasuredColumnWidths] = useState([]);
|
|
49
49
|
/**
|
|
50
50
|
* Registration callbacks with stable references. Called by
|
package/dist/DataGrid/index.js
CHANGED
|
@@ -28,16 +28,20 @@ export type AnimatedWrapperProps = {
|
|
|
28
28
|
*
|
|
29
29
|
* @example
|
|
30
30
|
* ```tsx
|
|
31
|
-
* const
|
|
31
|
+
* const [visibleCount, setVisibleCount] = useState(0);
|
|
32
32
|
*
|
|
33
33
|
* return (
|
|
34
34
|
* <AnimatedWrapper dependency={visibleCount}>
|
|
35
|
-
* <DataGrid>
|
|
36
|
-
* <
|
|
37
|
-
* {
|
|
38
|
-
*
|
|
39
|
-
* ))}
|
|
40
|
-
*
|
|
35
|
+
* <DataGrid maxHeight="400px" stickyHeader>
|
|
36
|
+
* <DataGridInfiniteBody
|
|
37
|
+
* data={largeData}
|
|
38
|
+
* batchSize={25}
|
|
39
|
+
* onVisibleCountChange={(count) => setVisibleCount(count)}
|
|
40
|
+
* >
|
|
41
|
+
* {(row) => (
|
|
42
|
+
* <DataGridRow key={row.id}>...</DataGridRow>
|
|
43
|
+
* )}
|
|
44
|
+
* </DataGridInfiniteBody>
|
|
41
45
|
* </DataGrid>
|
|
42
46
|
* </AnimatedWrapper>
|
|
43
47
|
* );
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
@versini/ui-datagrid v0.
|
|
2
|
+
@versini/ui-datagrid v0.4.0
|
|
3
3
|
© 2026 gizmette.com
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -18,16 +18,20 @@ import { useAnimatedHeight } from "./useAnimatedHeight.js";
|
|
|
18
18
|
*
|
|
19
19
|
* @example
|
|
20
20
|
* ```tsx
|
|
21
|
-
* const
|
|
21
|
+
* const [visibleCount, setVisibleCount] = useState(0);
|
|
22
22
|
*
|
|
23
23
|
* return (
|
|
24
24
|
* <AnimatedWrapper dependency={visibleCount}>
|
|
25
|
-
* <DataGrid>
|
|
26
|
-
* <
|
|
27
|
-
* {
|
|
28
|
-
*
|
|
29
|
-
* ))}
|
|
30
|
-
*
|
|
25
|
+
* <DataGrid maxHeight="400px" stickyHeader>
|
|
26
|
+
* <DataGridInfiniteBody
|
|
27
|
+
* data={largeData}
|
|
28
|
+
* batchSize={25}
|
|
29
|
+
* onVisibleCountChange={(count) => setVisibleCount(count)}
|
|
30
|
+
* >
|
|
31
|
+
* {(row) => (
|
|
32
|
+
* <DataGridRow key={row.id}>...</DataGridRow>
|
|
33
|
+
* )}
|
|
34
|
+
* </DataGridInfiniteBody>
|
|
31
35
|
* </DataGrid>
|
|
32
36
|
* </AnimatedWrapper>
|
|
33
37
|
* );
|
|
@@ -1,84 +1,42 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
@versini/ui-datagrid v0.
|
|
2
|
+
@versini/ui-datagrid v0.4.0
|
|
3
3
|
© 2026 gizmette.com
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { jsx } from "react/jsx-runtime";
|
|
7
|
-
import
|
|
8
|
-
import { useContext, useLayoutEffect, useRef } from "react";
|
|
7
|
+
import { useContext, useRef } from "react";
|
|
9
8
|
import { DataGridContext } from "../DataGrid/DataGridContext.js";
|
|
10
9
|
import { CellWrapper } from "../DataGridConstants/index.js";
|
|
10
|
+
import { getBodyClass } from "./getBodyClass.js";
|
|
11
|
+
import { useColumnMeasurement } from "./useColumnMeasurement.js";
|
|
11
12
|
|
|
12
13
|
;// CONCATENATED MODULE: external "react/jsx-runtime"
|
|
13
14
|
|
|
14
|
-
;// CONCATENATED MODULE: external "clsx"
|
|
15
|
-
|
|
16
15
|
;// CONCATENATED MODULE: external "react"
|
|
17
16
|
|
|
18
17
|
;// CONCATENATED MODULE: external "../DataGrid/DataGridContext.js"
|
|
19
18
|
|
|
20
19
|
;// CONCATENATED MODULE: external "../DataGridConstants/index.js"
|
|
21
20
|
|
|
21
|
+
;// CONCATENATED MODULE: external "./getBodyClass.js"
|
|
22
|
+
|
|
23
|
+
;// CONCATENATED MODULE: external "./useColumnMeasurement.js"
|
|
24
|
+
|
|
22
25
|
;// CONCATENATED MODULE: ./src/DataGridBody/DataGridBody.tsx
|
|
23
26
|
|
|
24
27
|
|
|
25
28
|
|
|
26
29
|
|
|
27
30
|
|
|
31
|
+
|
|
28
32
|
/* =============================================================================
|
|
29
33
|
* DataGridBody
|
|
30
34
|
* ========================================================================== */ const DataGridBody = ({ className, children, ...rest })=>{
|
|
31
35
|
const ctx = useContext(DataGridContext);
|
|
32
36
|
const bodyRef = useRef(null);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
* CSS subgrid. We measure the body cells (which ARE in the grid flow)
|
|
37
|
-
* and report the widths so header/footer can use the same pixel values.
|
|
38
|
-
*/ useLayoutEffect(()=>{
|
|
39
|
-
const element = bodyRef.current;
|
|
40
|
-
const needsMeasurement = ctx.columns && (ctx.stickyHeader || ctx.stickyFooter);
|
|
41
|
-
if (!element || !needsMeasurement || !ctx.setMeasuredColumnWidths) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
// Find the first row and its cells once, reuse for both measurement and observation
|
|
45
|
-
const firstRow = element.querySelector('[role="row"]');
|
|
46
|
-
if (!firstRow) {
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
const cells = firstRow.querySelectorAll('[role="cell"], [role="columnheader"], [role="gridcell"]');
|
|
50
|
-
if (cells.length === 0) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
const measureColumns = ()=>{
|
|
54
|
-
// Measure each cell's width
|
|
55
|
-
const widths = Array.from(cells).map((cell)=>cell.getBoundingClientRect().width);
|
|
56
|
-
ctx.setMeasuredColumnWidths?.(widths);
|
|
57
|
-
};
|
|
58
|
-
// Initial measurement
|
|
59
|
-
measureColumns();
|
|
60
|
-
// Set up ResizeObserver to re-measure when cells resize
|
|
61
|
-
const observer = new ResizeObserver(()=>{
|
|
62
|
-
measureColumns();
|
|
63
|
-
});
|
|
64
|
-
// Observe the body element for any size changes
|
|
65
|
-
observer.observe(element);
|
|
66
|
-
// Also observe the first row's cells directly for more accurate updates
|
|
67
|
-
for (const cell of cells){
|
|
68
|
-
observer.observe(cell);
|
|
69
|
-
}
|
|
70
|
-
return ()=>observer.disconnect();
|
|
71
|
-
}, [
|
|
72
|
-
ctx.columns,
|
|
73
|
-
ctx.stickyHeader,
|
|
74
|
-
ctx.stickyFooter,
|
|
75
|
-
ctx.setMeasuredColumnWidths,
|
|
76
|
-
children
|
|
77
|
-
]);
|
|
78
|
-
/**
|
|
79
|
-
* When columns are provided, use display:contents so the body doesn't
|
|
80
|
-
* interfere with the grid flow. Rows will use subgrid.
|
|
81
|
-
*/ const bodyClass = ctx.columns ? clsx("contents", className) : clsx("flex flex-col", className);
|
|
37
|
+
// Measure column widths for sticky header/footer sync.
|
|
38
|
+
useColumnMeasurement(bodyRef, children);
|
|
39
|
+
const bodyClass = getBodyClass(Boolean(ctx.columns), className);
|
|
82
40
|
return /*#__PURE__*/ jsx(DataGridContext.Provider, {
|
|
83
41
|
value: {
|
|
84
42
|
...ctx,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the CSS class for a DataGrid body element.
|
|
3
|
+
* When columns are provided, use display:contents so the body doesn't
|
|
4
|
+
* interfere with the grid flow. Rows will use subgrid.
|
|
5
|
+
*
|
|
6
|
+
* @param hasColumns - Whether the DataGrid has columns defined
|
|
7
|
+
* @param className - Additional class name to merge
|
|
8
|
+
*/
|
|
9
|
+
export declare function getBodyClass(hasColumns: boolean | undefined, className?: string): string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
@versini/ui-datagrid v0.4.0
|
|
3
|
+
© 2026 gizmette.com
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import clsx from "clsx";
|
|
7
|
+
|
|
8
|
+
;// CONCATENATED MODULE: external "clsx"
|
|
9
|
+
|
|
10
|
+
;// CONCATENATED MODULE: ./src/DataGridBody/getBodyClass.ts
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get the CSS class for a DataGrid body element.
|
|
14
|
+
* When columns are provided, use display:contents so the body doesn't
|
|
15
|
+
* interfere with the grid flow. Rows will use subgrid.
|
|
16
|
+
*
|
|
17
|
+
* @param hasColumns - Whether the DataGrid has columns defined
|
|
18
|
+
* @param className - Additional class name to merge
|
|
19
|
+
*/ function getBodyClass(hasColumns, className) {
|
|
20
|
+
return hasColumns ? clsx("contents", className) : clsx("flex flex-col", className);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { getBodyClass };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook to measure column widths from the first body row's cells.
|
|
3
|
+
* This is needed because sticky header/footer are absolutely positioned
|
|
4
|
+
* and can't use CSS subgrid. We measure the body cells (which ARE in the
|
|
5
|
+
* grid flow) and report the widths so header/footer can use the same pixel values.
|
|
6
|
+
*
|
|
7
|
+
* @param bodyRef - Ref to the body element containing the rows
|
|
8
|
+
* @param contentDependency - Dependency that changes when content changes (children or renderedContent)
|
|
9
|
+
*/
|
|
10
|
+
export declare function useColumnMeasurement(bodyRef: React.RefObject<HTMLDivElement | null>, contentDependency: unknown): void;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
@versini/ui-datagrid v0.4.0
|
|
3
|
+
© 2026 gizmette.com
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useContext, useLayoutEffect } from "react";
|
|
7
|
+
import { DataGridContext } from "../DataGrid/DataGridContext.js";
|
|
8
|
+
|
|
9
|
+
;// CONCATENATED MODULE: external "react"
|
|
10
|
+
|
|
11
|
+
;// CONCATENATED MODULE: external "../DataGrid/DataGridContext.js"
|
|
12
|
+
|
|
13
|
+
;// CONCATENATED MODULE: ./src/DataGridBody/useColumnMeasurement.ts
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Hook to measure column widths from the first body row's cells.
|
|
18
|
+
* This is needed because sticky header/footer are absolutely positioned
|
|
19
|
+
* and can't use CSS subgrid. We measure the body cells (which ARE in the
|
|
20
|
+
* grid flow) and report the widths so header/footer can use the same pixel values.
|
|
21
|
+
*
|
|
22
|
+
* @param bodyRef - Ref to the body element containing the rows
|
|
23
|
+
* @param contentDependency - Dependency that changes when content changes (children or renderedContent)
|
|
24
|
+
*/ function useColumnMeasurement(bodyRef, contentDependency) {
|
|
25
|
+
const ctx = useContext(DataGridContext);
|
|
26
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: contentDependency triggers remeasurement when rows change
|
|
27
|
+
useLayoutEffect(()=>{
|
|
28
|
+
const element = bodyRef.current;
|
|
29
|
+
const needsMeasurement = ctx.columns && (ctx.stickyHeader || ctx.stickyFooter);
|
|
30
|
+
if (!element || !needsMeasurement || !ctx.setMeasuredColumnWidths) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const firstRow = element.querySelector('[role="row"]');
|
|
34
|
+
if (!firstRow) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const cells = firstRow.querySelectorAll('[role="cell"], [role="columnheader"], [role="gridcell"]');
|
|
38
|
+
if (cells.length === 0) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const measureColumns = ()=>{
|
|
42
|
+
const widths = Array.from(cells).map((cell)=>cell.getBoundingClientRect().width);
|
|
43
|
+
ctx.setMeasuredColumnWidths?.(widths);
|
|
44
|
+
};
|
|
45
|
+
// Initial measurement.
|
|
46
|
+
measureColumns();
|
|
47
|
+
// Set up ResizeObserver to re-measure when cells resize.
|
|
48
|
+
const observer = new ResizeObserver(()=>{
|
|
49
|
+
measureColumns();
|
|
50
|
+
});
|
|
51
|
+
// Observe the body element for any size changes.
|
|
52
|
+
observer.observe(element);
|
|
53
|
+
// Also observe the first row's cells directly for more accurate updates.
|
|
54
|
+
for (const cell of cells){
|
|
55
|
+
observer.observe(cell);
|
|
56
|
+
}
|
|
57
|
+
return ()=>observer.disconnect();
|
|
58
|
+
}, [
|
|
59
|
+
ctx.columns,
|
|
60
|
+
ctx.stickyHeader,
|
|
61
|
+
ctx.stickyFooter,
|
|
62
|
+
ctx.setMeasuredColumnWidths,
|
|
63
|
+
contentDependency
|
|
64
|
+
]);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { useColumnMeasurement };
|
|
@@ -1,12 +1,2 @@
|
|
|
1
1
|
import type { DataGridCellProps } from "../DataGrid/DataGridTypes";
|
|
2
|
-
import { type CellWrapperType, type ThemeMode } from "../DataGridConstants";
|
|
3
|
-
export declare const getCellClasses: ({ cellWrapper, className, compact, align, mode, borderLeft, borderRight, }: {
|
|
4
|
-
cellWrapper?: CellWrapperType;
|
|
5
|
-
className?: string;
|
|
6
|
-
mode?: ThemeMode;
|
|
7
|
-
compact?: boolean;
|
|
8
|
-
align?: "left" | "center" | "right";
|
|
9
|
-
borderLeft?: boolean;
|
|
10
|
-
borderRight?: boolean;
|
|
11
|
-
}) => string;
|
|
12
2
|
export declare const DataGridCell: ({ className, children, align, borderLeft, borderRight, colSpan, style, ...rest }: DataGridCellProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,76 +1,22 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
@versini/ui-datagrid v0.
|
|
2
|
+
@versini/ui-datagrid v0.4.0
|
|
3
3
|
© 2026 gizmette.com
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { jsx } from "react/jsx-runtime";
|
|
7
|
-
import { clsx } from "clsx";
|
|
8
7
|
import { DataGridContext } from "../DataGrid/DataGridContext.js";
|
|
9
|
-
import {
|
|
8
|
+
import { getCellClasses, getCellRole } from "../utilities/classes.js";
|
|
10
9
|
|
|
11
10
|
;// CONCATENATED MODULE: external "react/jsx-runtime"
|
|
12
11
|
|
|
13
|
-
;// CONCATENATED MODULE: external "clsx"
|
|
14
|
-
|
|
15
12
|
;// CONCATENATED MODULE: external "../DataGrid/DataGridContext.js"
|
|
16
13
|
|
|
17
|
-
;// CONCATENATED MODULE: external "../
|
|
14
|
+
;// CONCATENATED MODULE: external "../utilities/classes.js"
|
|
18
15
|
|
|
19
16
|
;// CONCATENATED MODULE: ./src/DataGridCell/DataGridCell.tsx
|
|
20
17
|
|
|
21
18
|
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
const getCellClasses = ({ cellWrapper, className, compact, align, mode, borderLeft, borderRight })=>{
|
|
25
|
-
const isHeader = cellWrapper === CellWrapper.HEADER;
|
|
26
|
-
const mainClasses = clsx(// Base padding.
|
|
27
|
-
{
|
|
28
|
-
"px-2 py-1": compact,
|
|
29
|
-
"px-4 py-3": !compact
|
|
30
|
-
}, // Text alignment.
|
|
31
|
-
{
|
|
32
|
-
"text-left justify-start": align === "left" || !align,
|
|
33
|
-
"text-center justify-center": align === "center",
|
|
34
|
-
"text-right justify-end": align === "right"
|
|
35
|
-
}, // Header/footer specific styles.
|
|
36
|
-
{
|
|
37
|
-
"font-semibold": isHeader
|
|
38
|
-
}, /**
|
|
39
|
-
* Active row indicator (left border on first cell only).
|
|
40
|
-
* Uses CSS group-data-[active] to detect when parent row has data-active attribute.
|
|
41
|
-
* The `first:` variant ensures only the first cell in the row shows the indicator.
|
|
42
|
-
*/ "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", // Active indicator color based on theme mode.
|
|
43
|
-
{
|
|
44
|
-
"first:group-data-[active]:before:bg-table-active-dark": mode === "dark",
|
|
45
|
-
"first:group-data-[active]:before:bg-table-active-light": mode === "light",
|
|
46
|
-
"first:group-data-[active]:before:bg-table-active-dark dark:first:group-data-[active]:before:bg-table-active-light": mode === "system",
|
|
47
|
-
"first:group-data-[active]:before:bg-table-active-light dark:first:group-data-[active]:before:bg-table-active-dark": mode === "alt-system"
|
|
48
|
-
}, /**
|
|
49
|
-
* Vertical borders. self-stretch ensures border spans full row height in
|
|
50
|
-
* grid layout. When stretching, use flex + items-center to maintain
|
|
51
|
-
* vertical centering of content.
|
|
52
|
-
*/ {
|
|
53
|
-
"self-stretch flex items-center": borderLeft || borderRight,
|
|
54
|
-
"border-l border-l-table-dark": borderLeft && mode === "dark",
|
|
55
|
-
"border-l border-l-table-light": borderLeft && mode === "light",
|
|
56
|
-
"border-l border-l-table-dark dark:border-l-table-light": borderLeft && mode === "system",
|
|
57
|
-
"border-l border-l-table-light dark:border-l-table-dark": borderLeft && mode === "alt-system",
|
|
58
|
-
"border-r border-r-table-dark": borderRight && mode === "dark",
|
|
59
|
-
"border-r border-r-table-light": borderRight && mode === "light",
|
|
60
|
-
"border-r border-r-table-dark dark:border-r-table-light": borderRight && mode === "system",
|
|
61
|
-
"border-r border-r-table-light dark:border-r-table-dark": borderRight && mode === "alt-system"
|
|
62
|
-
}, className);
|
|
63
|
-
return mainClasses;
|
|
64
|
-
};
|
|
65
|
-
/**
|
|
66
|
-
* Returns the appropriate ARIA role for the cell based on the cell wrapper
|
|
67
|
-
* type.
|
|
68
|
-
*/ const getCellRole = (cellWrapper)=>{
|
|
69
|
-
if (cellWrapper === CellWrapper.HEADER) {
|
|
70
|
-
return "columnheader";
|
|
71
|
-
}
|
|
72
|
-
return "gridcell";
|
|
73
|
-
};
|
|
74
20
|
/* =============================================================================
|
|
75
21
|
* DataGridCell
|
|
76
22
|
* ========================================================================== */ const DataGridCell = ({ className, children, align, borderLeft, borderRight, colSpan, style, ...rest })=>{
|
|
@@ -102,4 +48,4 @@ const getCellClasses = ({ cellWrapper, className, compact, align, mode, borderLe
|
|
|
102
48
|
});
|
|
103
49
|
};
|
|
104
50
|
|
|
105
|
-
export { DataGridCell
|
|
51
|
+
export { DataGridCell };
|