@versini/ui-datagrid 4.0.0 → 4.1.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/dist/197.js +1 -1
- package/dist/430.js +1 -1
- package/dist/799.js +1 -1
- package/dist/91.js +1 -1
- package/dist/DataGrid/index.js +1 -1
- package/dist/DataGridAnimated/index.js +1 -1
- package/dist/DataGridBody/index.js +1 -1
- package/dist/DataGridCell/index.js +1 -1
- package/dist/DataGridCellSort/index.js +1 -1
- package/dist/DataGridConstants/index.js +1 -1
- package/dist/DataGridFooter/index.js +1 -1
- package/dist/DataGridHeader/index.js +1 -1
- package/dist/DataGridInfinite/DataGridInfiniteBody.d.ts +23 -5
- package/dist/DataGridInfinite/index.d.ts +1 -1
- package/dist/DataGridInfinite/index.js +221 -42
- package/dist/DataGridRow/index.js +1 -1
- package/dist/DataGridSorting/index.js +1 -1
- package/package.json +4 -4
package/dist/197.js
CHANGED
package/dist/430.js
CHANGED
package/dist/799.js
CHANGED
package/dist/91.js
CHANGED
package/dist/DataGrid/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type DataGridInfiniteBodyProps<T> = ({
|
|
2
2
|
/**
|
|
3
|
-
* If true, displays an empty state instead of the grid rows. When noData
|
|
4
|
-
*
|
|
3
|
+
* If true, displays an empty state instead of the grid rows. When noData is
|
|
4
|
+
* true, data and children are optional.
|
|
5
5
|
*/
|
|
6
6
|
noData: true;
|
|
7
7
|
/**
|
|
@@ -58,6 +58,22 @@ export type DataGridInfiniteBodyProps<T> = ({
|
|
|
58
58
|
*/
|
|
59
59
|
className?: string;
|
|
60
60
|
};
|
|
61
|
+
/**
|
|
62
|
+
* Options for {@link DataGridInfiniteBodyRef.scrollToIndex}.
|
|
63
|
+
*/
|
|
64
|
+
export type ScrollToIndexOptions = {
|
|
65
|
+
/**
|
|
66
|
+
* How the grid scrolls to the row. `"smooth"` (the default) glides to the
|
|
67
|
+
* target; `"instant"` (or `"auto"`) jumps directly with no animation. A user
|
|
68
|
+
* `prefers-reduced-motion` setting always forces a direct jump, whatever is
|
|
69
|
+
* requested. Spelled out as a literal union rather than the DOM
|
|
70
|
+
* `ScrollBehavior` type, whose definition (notably `"instant"`) varies across
|
|
71
|
+
* TypeScript `lib.dom` versions — so the documented values are always
|
|
72
|
+
* available.
|
|
73
|
+
* @default "smooth"
|
|
74
|
+
*/
|
|
75
|
+
behavior?: "auto" | "instant" | "smooth";
|
|
76
|
+
};
|
|
61
77
|
/**
|
|
62
78
|
* Ref handle for DataGridInfiniteBody, exposing imperative methods.
|
|
63
79
|
*/
|
|
@@ -65,8 +81,10 @@ export type DataGridInfiniteBodyRef = {
|
|
|
65
81
|
/**
|
|
66
82
|
* Scroll a row into view (centered). Works for any index, mounted or not.
|
|
67
83
|
* @param index - The index of the row to scroll to
|
|
84
|
+
* @param options - Scroll options; defaults to a smooth scroll. Pass
|
|
85
|
+
* `{ behavior: "instant" }` for a direct jump.
|
|
68
86
|
*/
|
|
69
|
-
scrollToIndex: (index: number) => void;
|
|
87
|
+
scrollToIndex: (index: number, options?: ScrollToIndexOptions) => void;
|
|
70
88
|
};
|
|
71
89
|
/**
|
|
72
90
|
* A virtualized DataGrid body for large datasets.
|
|
@@ -74,8 +92,8 @@ export type DataGridInfiniteBodyRef = {
|
|
|
74
92
|
* Only the rows near the viewport stay mounted (windowing via
|
|
75
93
|
* `@tanstack/react-virtual`), so the DOM stays small and scrolling stays smooth
|
|
76
94
|
* at any row count. Rows are rendered in normal grid flow between two full-span
|
|
77
|
-
* spacer elements, so `columns`/subgrid (including intrinsic `auto` tracks)
|
|
78
|
-
* working without any consumer change. The virtualizer windows against the
|
|
95
|
+
* spacer elements, so `columns`/subgrid (including intrinsic `auto` tracks)
|
|
96
|
+
* keep working without any consumer change. The virtualizer windows against the
|
|
79
97
|
* DataGrid's own scroll container (created by `maxHeight` or a sticky
|
|
80
98
|
* header/footer), or the page (window scroll) when the grid has neither.
|
|
81
99
|
*
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { DataGridInfiniteBodyProps, DataGridInfiniteBodyRef, } from "./DataGridInfiniteBody";
|
|
1
|
+
export type { DataGridInfiniteBodyProps, DataGridInfiniteBodyRef, ScrollToIndexOptions, } from "./DataGridInfiniteBody";
|
|
2
2
|
export { DataGridInfiniteBody } from "./DataGridInfiniteBody";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
@versini/ui-datagrid v4.
|
|
2
|
+
@versini/ui-datagrid v4.1.0
|
|
3
3
|
© 2026 gizmette.com
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -23,14 +23,25 @@ import { getBodyClass, useColumnMeasurement } from "../197.js";
|
|
|
23
23
|
const DEFAULT_OVERSCAN = 8;
|
|
24
24
|
const DEFAULT_ESTIMATED_ROW_HEIGHT = 44;
|
|
25
25
|
const DEFAULT_COMPACT_ESTIMATED_ROW_HEIGHT = 29;
|
|
26
|
+
/**
|
|
27
|
+
* Fallback used to restore the normal window after a smooth jump when the
|
|
28
|
+
* browser does not fire `scrollend` (e.g. Safari < 17.4) or the target was
|
|
29
|
+
* already centered.
|
|
30
|
+
*/ const JUMP_SETTLE_FALLBACK_MS = 1000;
|
|
31
|
+
/**
|
|
32
|
+
* A smooth jump animates reliably over a short hop, but a long one outruns the
|
|
33
|
+
* animation budget and races react-virtual's mid-flight measurement, landing
|
|
34
|
+
* unpredictably. Past this many rows of reach we jump instantly instead — it
|
|
35
|
+
* lands exactly (no animation to race) at the cost of the glide.
|
|
36
|
+
*/ const FAR_JUMP_THRESHOLD = 30;
|
|
26
37
|
/**
|
|
27
38
|
* A virtualized DataGrid body for large datasets.
|
|
28
39
|
*
|
|
29
40
|
* Only the rows near the viewport stay mounted (windowing via
|
|
30
41
|
* `@tanstack/react-virtual`), so the DOM stays small and scrolling stays smooth
|
|
31
42
|
* at any row count. Rows are rendered in normal grid flow between two full-span
|
|
32
|
-
* spacer elements, so `columns`/subgrid (including intrinsic `auto` tracks)
|
|
33
|
-
* working without any consumer change. The virtualizer windows against the
|
|
43
|
+
* spacer elements, so `columns`/subgrid (including intrinsic `auto` tracks)
|
|
44
|
+
* keep working without any consumer change. The virtualizer windows against the
|
|
34
45
|
* DataGrid's own scroll container (created by `maxHeight` or a sticky
|
|
35
46
|
* header/footer), or the page (window scroll) when the grid has neither.
|
|
36
47
|
*
|
|
@@ -61,18 +72,44 @@ const DEFAULT_COMPACT_ESTIMATED_ROW_HEIGHT = 29;
|
|
|
61
72
|
*/ function DataGridInfiniteBody({ data = [], children: renderRow, overscan = DEFAULT_OVERSCAN, estimatedRowHeight, onVisibleCountChange, className, noData = false, noDataText, ref }) {
|
|
62
73
|
const ctx = useContext(DataGridContext);
|
|
63
74
|
const bodyRef = useRef(null);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
75
|
+
/**
|
|
76
|
+
* A zero-height grid item at the list start. The display:contents body has no
|
|
77
|
+
* box, so we measure this real element to find the list's scroll-margin.
|
|
78
|
+
*/ const sentinelRef = useRef(null);
|
|
67
79
|
const totalItems = data.length;
|
|
68
80
|
const headerRows = ctx.headerRows ?? 0;
|
|
69
81
|
const footerRows = ctx.footerRows ?? 0;
|
|
70
82
|
/**
|
|
71
83
|
* Default the row-height estimate from the grid's density when the consumer
|
|
72
84
|
* doesn't set it: `compact` rows are much shorter (~29px vs ~44px), and an
|
|
73
|
-
* estimate far from the real height hurts scrollbar accuracy and
|
|
85
|
+
* estimate far from the real height hurts scrollbar accuracy and
|
|
86
|
+
* scrollToIndex.
|
|
74
87
|
*/ const resolvedEstimatedRowHeight = estimatedRowHeight ?? (ctx.compact ? DEFAULT_COMPACT_ESTIMATED_ROW_HEIGHT : DEFAULT_ESTIMATED_ROW_HEIGHT);
|
|
75
88
|
const [scrollMargin, setScrollMargin] = useState(0);
|
|
89
|
+
/**
|
|
90
|
+
* Widen-window jump-to-row state. A default (`"smooth"`) `scrollToIndex` first
|
|
91
|
+
* widens the virtualized window (`jump.overscan`) so every row between the
|
|
92
|
+
* current view and the target is mounted with real, laid-out heights, then the
|
|
93
|
+
* layout effect scrolls to the real target element and restores the window
|
|
94
|
+
* once the scroll settles.
|
|
95
|
+
*
|
|
96
|
+
* `instant` is set for a far jump (reach past `FAR_JUMP_THRESHOLD`); the
|
|
97
|
+
* effect then jumps instantly rather than animating (it also forces instant
|
|
98
|
+
* for a `prefers-reduced-motion` user, but that is read live in the effect,
|
|
99
|
+
* not stored here). Scrolling over real rows always lands exactly; the
|
|
100
|
+
* animation is what raced react-virtual's mid-flight measurement over a long
|
|
101
|
+
* distance, so far jumps drop it. A direct jump (`behavior: "instant"`) skips
|
|
102
|
+
* this state entirely and delegates to the virtualizer (see `scrollToIndex`
|
|
103
|
+
* below).
|
|
104
|
+
*
|
|
105
|
+
* `seq` makes every request a fresh object: two jumps that happen to compute
|
|
106
|
+
* the same `overscan` still re-run the jump effect (a plain number would be a
|
|
107
|
+
* no-op setState and the second jump would be dropped). `handledSeqRef`
|
|
108
|
+
* records the last seq the effect acted on, so it scrolls once per request.
|
|
109
|
+
*
|
|
110
|
+
*/ const [jump, setJump] = useState(null);
|
|
111
|
+
const jumpSeqRef = useRef(0);
|
|
112
|
+
const handledSeqRef = useRef(-1);
|
|
76
113
|
/**
|
|
77
114
|
* The scroll target is provided by the parent DataGrid: its internal bounded
|
|
78
115
|
* scroller (sticky / maxHeight), or the page (window scroll) when it owns
|
|
@@ -81,9 +118,10 @@ const DEFAULT_COMPACT_ESTIMATED_ROW_HEIGHT = 29;
|
|
|
81
118
|
*/ const useWindow = ctx.pageScroll === true;
|
|
82
119
|
const scrollEl = useWindow ? null : ctx.scrollContainer ?? null;
|
|
83
120
|
const resolved = useWindow || scrollEl !== null;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Measure the list's offset within the scroller (= sticky-header padding /
|
|
123
|
+
* inline-header height / page offset). Keeps the window origin correct.
|
|
124
|
+
*/ useLayoutEffect(()=>{
|
|
87
125
|
if (!resolved || noData) {
|
|
88
126
|
return;
|
|
89
127
|
}
|
|
@@ -110,27 +148,64 @@ const DEFAULT_COMPACT_ESTIMATED_ROW_HEIGHT = 29;
|
|
|
110
148
|
scrollEl,
|
|
111
149
|
noData
|
|
112
150
|
]);
|
|
151
|
+
/**
|
|
152
|
+
* Keep the element scroller's `scroll-padding-top` equal to the header height
|
|
153
|
+
* (`scrollMargin`). A smooth jump centers via native `scrollIntoView`, which
|
|
154
|
+
* otherwise centers in the full box and lands the row half a header-height too
|
|
155
|
+
* high under a sticky header; the inset shrinks the centering region to the
|
|
156
|
+
* visible area. Declared here (not mutated per-jump) so it is not call-order
|
|
157
|
+
* dependent, and the previous inline value is restored on cleanup so the grid
|
|
158
|
+
* never permanently overrides a consumer's own `scroll-padding-top`.
|
|
159
|
+
*/ useLayoutEffect(()=>{
|
|
160
|
+
if (!scrollEl) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const previous = scrollEl.style.scrollPaddingTop;
|
|
164
|
+
scrollEl.style.scrollPaddingTop = `${scrollMargin}px`;
|
|
165
|
+
return ()=>{
|
|
166
|
+
scrollEl.style.scrollPaddingTop = previous;
|
|
167
|
+
};
|
|
168
|
+
}, [
|
|
169
|
+
scrollEl,
|
|
170
|
+
scrollMargin
|
|
171
|
+
]);
|
|
113
172
|
const virtualizer = useVirtualizer({
|
|
114
173
|
count: totalItems,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
174
|
+
/**
|
|
175
|
+
* In window mode the scroll element IS `window` (the window observers read
|
|
176
|
+
* window.innerHeight / window.scrollY off it); cast bridges the type since
|
|
177
|
+
* useVirtualizer's element constraint excludes Window.
|
|
178
|
+
*/ getScrollElement: ()=>useWindow ? typeof window !== "undefined" ? window : null : scrollEl,
|
|
119
179
|
estimateSize: ()=>resolvedEstimatedRowHeight,
|
|
120
|
-
|
|
180
|
+
/**
|
|
181
|
+
* While a jump is in flight, `jump.overscan` widens the window to mount the
|
|
182
|
+
* rows along the path; otherwise the consumer's (or default) overscan.
|
|
183
|
+
*/ overscan: jump?.overscan ?? overscan,
|
|
121
184
|
scrollMargin,
|
|
185
|
+
/**
|
|
186
|
+
* Direct jumps center via the virtualizer (`scrollToIndex` with align
|
|
187
|
+
* "center"). Like the sticky-header inset on the smooth path, the row must
|
|
188
|
+
* center in the visible area below the header, not the full box.
|
|
189
|
+
* react-virtual shifts the target offset by `scrollPaddingStart` and then
|
|
190
|
+
* centers, so half the header height lands the row at the visible center.
|
|
191
|
+
* (The smooth path uses CSS `scroll-padding-top`, which shrinks the centering
|
|
192
|
+
* box instead, so that one takes the full header height — hence the /2
|
|
193
|
+
* asymmetry.)
|
|
194
|
+
*/ scrollPaddingStart: scrollMargin / 2,
|
|
122
195
|
// React 19: let React batch the scroll-driven updates natively.
|
|
123
196
|
useFlushSync: false,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
197
|
+
/**
|
|
198
|
+
* Window vs element observers are selected to match getScrollElement; the
|
|
199
|
+
* casts bridge the Element|Window union react-virtual can't narrow here.
|
|
200
|
+
*/ observeElementRect: useWindow ? observeWindowRect : observeElementRect,
|
|
127
201
|
observeElementOffset: useWindow ? observeWindowOffset : observeElementOffset,
|
|
128
202
|
scrollToFn: useWindow ? windowScroll : elementScroll,
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
203
|
+
/**
|
|
204
|
+
* Window mode: seed the offset from the live scroll position (mirrors
|
|
205
|
+
* useWindowVirtualizer). Without this, on mount the virtualizer's offset is
|
|
206
|
+
* 0, so it scrolls an already-scrolled page to the top and computes the wrong
|
|
207
|
+
* initial window.
|
|
208
|
+
*/ initialOffset: useWindow ? ()=>typeof window !== "undefined" ? window.scrollY : 0 : undefined
|
|
134
209
|
});
|
|
135
210
|
const virtualItems = virtualizer.getVirtualItems();
|
|
136
211
|
const totalSize = virtualizer.getTotalSize();
|
|
@@ -142,9 +217,10 @@ const DEFAULT_COMPACT_ESTIMATED_ROW_HEIGHT = 29;
|
|
|
142
217
|
* scroller that isn't height-bounded (every row mounts). Runs after paint so
|
|
143
218
|
* the measurements are real; stripped from production builds.
|
|
144
219
|
*/ useEffect(()=>{
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
220
|
+
/**
|
|
221
|
+
* Treat a missing `process` (a non-Node runtime whose bundler didn't replace
|
|
222
|
+
* this expression) as production, so reading NODE_ENV can never throw.
|
|
223
|
+
*/ const isProduction = typeof process === "undefined" || process.env.NODE_ENV === "production";
|
|
148
224
|
if (isProduction || noData || totalItems === 0 || typeof window === "undefined") {
|
|
149
225
|
return;
|
|
150
226
|
}
|
|
@@ -168,27 +244,129 @@ const DEFAULT_COMPACT_ESTIMATED_ROW_HEIGHT = 29;
|
|
|
168
244
|
totalItems
|
|
169
245
|
]);
|
|
170
246
|
/**
|
|
171
|
-
* Expose scrollToIndex via ref
|
|
172
|
-
*
|
|
247
|
+
* Expose scrollToIndex via ref. Works for any index, mounted or not.
|
|
248
|
+
*
|
|
249
|
+
* A direct jump (`behavior: "instant"`/`"auto"`) delegates straight to the
|
|
250
|
+
* virtualizer: it scrolls to the target's estimated offset and mounts only the
|
|
251
|
+
* window around it — lightweight, but the landing is only as accurate as
|
|
252
|
+
* `estimatedRowHeight` (the rows above stay estimated).
|
|
253
|
+
*
|
|
254
|
+
* A default (`"smooth"`) jump instead widens the window to mount every row
|
|
255
|
+
* from the current view to the target with real heights, so it lands exactly.
|
|
256
|
+
* The layout effect then animates a near jump, or — past `FAR_JUMP_THRESHOLD`
|
|
257
|
+
* — jumps instantly (a long animation races the virtualizer's mid-flight
|
|
258
|
+
* measurement and strands the jump short).
|
|
259
|
+
*
|
|
173
260
|
*/ useImperativeHandle(ref, ()=>({
|
|
174
|
-
scrollToIndex: (index)=>{
|
|
261
|
+
scrollToIndex: (index, options)=>{
|
|
175
262
|
if (index < 0 || index >= totalItems) {
|
|
176
263
|
console.warn(`scrollToIndex: index ${index} is out of bounds${totalItems === 0 ? " (the grid is empty)" : ` (0-${totalItems - 1})`}`);
|
|
177
264
|
return;
|
|
178
265
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
266
|
+
const behavior = options?.behavior ?? "smooth";
|
|
267
|
+
if (behavior !== "smooth") {
|
|
268
|
+
/**
|
|
269
|
+
* If a widened smooth jump is still in flight, cancel it so its (possibly
|
|
270
|
+
* large) path window doesn't stay mounted until its own settle. Safe
|
|
271
|
+
* unconditionally: `setJump(null)` no-ops when there is no pending jump.
|
|
272
|
+
*/ setJump(null);
|
|
273
|
+
/**
|
|
274
|
+
* Direct jump: delegate to the virtualizer with an explicit "instant".
|
|
275
|
+
* Forwarding "auto" instead would defer to the scroller's CSS
|
|
276
|
+
* `scroll-behavior` — under a global `scroll-behavior: smooth` that
|
|
277
|
+
* animates through the un-widened window (the very stutter the smooth path
|
|
278
|
+
* exists to avoid), breaking the documented "jumps directly" contract.
|
|
279
|
+
*/ virtualizer.scrollToIndex(index, {
|
|
280
|
+
align: "center",
|
|
281
|
+
behavior: "instant"
|
|
282
|
+
});
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const items = virtualizer.getVirtualItems();
|
|
286
|
+
const first = items.length > 0 ? items[0].index : 0;
|
|
287
|
+
const last = items.length > 0 ? items[items.length - 1].index : 0;
|
|
288
|
+
/**
|
|
289
|
+
* Overscan is symmetric, so widen by the larger gap to either edge of the
|
|
290
|
+
* current window; that guarantees the target (and the rows between) mount.
|
|
291
|
+
* The jump effect scrolls once they render, then restores the normal
|
|
292
|
+
* window. A jump reaching past `FAR_JUMP_THRESHOLD` lands instantly (a long
|
|
293
|
+
* smooth animation races the virtualizer's mid-flight measurement and
|
|
294
|
+
* strands the jump short).
|
|
295
|
+
*/ const reach = Math.max(index - first, last - index, 0);
|
|
296
|
+
jumpSeqRef.current += 1;
|
|
297
|
+
setJump({
|
|
298
|
+
index,
|
|
299
|
+
overscan: reach + overscan,
|
|
300
|
+
instant: reach > FAR_JUMP_THRESHOLD,
|
|
301
|
+
seq: jumpSeqRef.current
|
|
188
302
|
});
|
|
189
303
|
}
|
|
190
304
|
}), [
|
|
191
305
|
totalItems,
|
|
306
|
+
virtualizer,
|
|
307
|
+
overscan
|
|
308
|
+
]);
|
|
309
|
+
/**
|
|
310
|
+
* Perform a pending jump once the widened window has mounted the target row
|
|
311
|
+
* (react-virtual recomputes the window synchronously, so it is mounted in the
|
|
312
|
+
* commit that follows the `jump` change), then restore the normal window.
|
|
313
|
+
*
|
|
314
|
+
* Two flavors:
|
|
315
|
+
* - **Instant** (far jump or reduced motion): scroll instantly, measure the
|
|
316
|
+
* path rows so the window-collapse keeps their real heights (not the
|
|
317
|
+
* estimate, which would shift the target), then collapse — all synchronously.
|
|
318
|
+
* No animation means nothing races the measurement, so it lands exactly.
|
|
319
|
+
* - **Smooth** (near jump): animate, and collapse once the scroll settles
|
|
320
|
+
* (`scrollend`, or the timeout fallback for the no-op / unsupported case). A
|
|
321
|
+
* short hop finishes before measurement can interfere, so it stays exact.
|
|
322
|
+
*
|
|
323
|
+
*/ useLayoutEffect(()=>{
|
|
324
|
+
if (jump === null || handledSeqRef.current === jump.seq) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
handledSeqRef.current = jump.seq;
|
|
328
|
+
const rowEl = bodyRef.current?.querySelector(`[data-index="${jump.index}"]`);
|
|
329
|
+
if (!rowEl) {
|
|
330
|
+
/**
|
|
331
|
+
* The widened window should always mount the target; if it somehow did not,
|
|
332
|
+
* don't stay stuck in widened mode — restore the normal window so later
|
|
333
|
+
* jumps aren't dropped.
|
|
334
|
+
*/ setJump(null);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const reduceMotion = typeof window !== "undefined" && !!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
338
|
+
if (jump.instant || reduceMotion) {
|
|
339
|
+
rowEl.scrollIntoView({
|
|
340
|
+
behavior: "instant",
|
|
341
|
+
block: "center"
|
|
342
|
+
});
|
|
343
|
+
/**
|
|
344
|
+
* Cache the path rows' real heights before the window collapses, so the rows
|
|
345
|
+
* above the target don't fall back to `estimatedRowHeight` and shift it.
|
|
346
|
+
* Safe to do synchronously here: the instant scroll has already landed, so
|
|
347
|
+
* there is no in-flight animation for the measurement to disturb.
|
|
348
|
+
*/ bodyRef.current?.querySelectorAll("[data-index]").forEach((el)=>virtualizer.measureElement(el));
|
|
349
|
+
setJump(null);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
rowEl.scrollIntoView({
|
|
353
|
+
behavior: "smooth",
|
|
354
|
+
block: "center"
|
|
355
|
+
});
|
|
356
|
+
const scroller = useWindow ? typeof window !== "undefined" ? window : null : scrollEl;
|
|
357
|
+
const settle = ()=>setJump(null);
|
|
358
|
+
scroller?.addEventListener("scrollend", settle, {
|
|
359
|
+
once: true
|
|
360
|
+
});
|
|
361
|
+
const timer = setTimeout(settle, JUMP_SETTLE_FALLBACK_MS);
|
|
362
|
+
return ()=>{
|
|
363
|
+
clearTimeout(timer);
|
|
364
|
+
scroller?.removeEventListener("scrollend", settle);
|
|
365
|
+
};
|
|
366
|
+
}, [
|
|
367
|
+
jump,
|
|
368
|
+
useWindow,
|
|
369
|
+
scrollEl,
|
|
192
370
|
virtualizer
|
|
193
371
|
]);
|
|
194
372
|
/**
|
|
@@ -255,10 +433,11 @@ const DEFAULT_COMPACT_ESTIMATED_ROW_HEIGHT = 29;
|
|
|
255
433
|
}), [
|
|
256
434
|
ctx
|
|
257
435
|
]);
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
436
|
+
/**
|
|
437
|
+
* Measure column widths for sticky header/footer sync. Re-key on the first
|
|
438
|
+
* mounted row so the sticky header re-syncs as the window (and the auto-column
|
|
439
|
+
* width) slides.
|
|
440
|
+
*/ useColumnMeasurement(bodyRef, virtualItems[0]?.index, noData);
|
|
262
441
|
const bodyClass = getBodyClass(className);
|
|
263
442
|
const paddingTop = virtualItems.length > 0 ? Math.max(0, virtualItems[0].start - scrollMargin) : 0;
|
|
264
443
|
const paddingBottom = virtualItems.length > 0 ? Math.max(0, totalSize - virtualItems[virtualItems.length - 1].end) : 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@versini/ui-datagrid",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Arno Versini",
|
|
6
6
|
"publishConfig": {
|
|
@@ -87,8 +87,8 @@
|
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
|
89
89
|
"@testing-library/jest-dom": "6.9.1",
|
|
90
|
-
"@versini/ui-button": "15.0
|
|
91
|
-
"@versini/ui-types": "10.
|
|
90
|
+
"@versini/ui-button": "15.1.0",
|
|
91
|
+
"@versini/ui-types": "10.1.0"
|
|
92
92
|
},
|
|
93
93
|
"dependencies": {
|
|
94
94
|
"@tanstack/react-virtual": "3.14.4",
|
|
@@ -99,5 +99,5 @@
|
|
|
99
99
|
"sideEffects": [
|
|
100
100
|
"**/*.css"
|
|
101
101
|
],
|
|
102
|
-
"gitHead": "
|
|
102
|
+
"gitHead": "fbfcead0c4637fdf2cf9c8f6a701fe5da9508d4b"
|
|
103
103
|
}
|