@versini/ui-datagrid 4.0.1 → 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 +175 -65
- 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.0
|
|
2
|
+
@versini/ui-datagrid v4.1.0
|
|
3
3
|
© 2026 gizmette.com
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -23,17 +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
|
-
|
|
28
|
-
|
|
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;
|
|
29
37
|
/**
|
|
30
38
|
* A virtualized DataGrid body for large datasets.
|
|
31
39
|
*
|
|
32
40
|
* Only the rows near the viewport stay mounted (windowing via
|
|
33
41
|
* `@tanstack/react-virtual`), so the DOM stays small and scrolling stays smooth
|
|
34
42
|
* at any row count. Rows are rendered in normal grid flow between two full-span
|
|
35
|
-
* spacer elements, so `columns`/subgrid (including intrinsic `auto` tracks)
|
|
36
|
-
* 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
|
|
37
45
|
* DataGrid's own scroll container (created by `maxHeight` or a sticky
|
|
38
46
|
* header/footer), or the page (window scroll) when the grid has neither.
|
|
39
47
|
*
|
|
@@ -64,31 +72,41 @@ const JUMP_SETTLE_FALLBACK_MS = 1000;
|
|
|
64
72
|
*/ function DataGridInfiniteBody({ data = [], children: renderRow, overscan = DEFAULT_OVERSCAN, estimatedRowHeight, onVisibleCountChange, className, noData = false, noDataText, ref }) {
|
|
65
73
|
const ctx = useContext(DataGridContext);
|
|
66
74
|
const bodyRef = useRef(null);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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);
|
|
70
79
|
const totalItems = data.length;
|
|
71
80
|
const headerRows = ctx.headerRows ?? 0;
|
|
72
81
|
const footerRows = ctx.footerRows ?? 0;
|
|
73
82
|
/**
|
|
74
83
|
* Default the row-height estimate from the grid's density when the consumer
|
|
75
84
|
* doesn't set it: `compact` rows are much shorter (~29px vs ~44px), and an
|
|
76
|
-
* estimate far from the real height hurts scrollbar accuracy and
|
|
85
|
+
* estimate far from the real height hurts scrollbar accuracy and
|
|
86
|
+
* scrollToIndex.
|
|
77
87
|
*/ const resolvedEstimatedRowHeight = estimatedRowHeight ?? (ctx.compact ? DEFAULT_COMPACT_ESTIMATED_ROW_HEIGHT : DEFAULT_ESTIMATED_ROW_HEIGHT);
|
|
78
88
|
const [scrollMargin, setScrollMargin] = useState(0);
|
|
79
89
|
/**
|
|
80
|
-
*
|
|
81
|
-
* virtualized window (`jump.overscan`) so every row between the
|
|
82
|
-
* and the target is mounted
|
|
83
|
-
* the real target element
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* is
|
|
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).
|
|
87
104
|
*
|
|
88
105
|
* `seq` makes every request a fresh object: two jumps that happen to compute
|
|
89
106
|
* the same `overscan` still re-run the jump effect (a plain number would be a
|
|
90
|
-
* no-op setState and the second jump would be dropped). `handledSeqRef`
|
|
91
|
-
* the last seq the effect acted on, so it scrolls once per request.
|
|
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
|
+
*
|
|
92
110
|
*/ const [jump, setJump] = useState(null);
|
|
93
111
|
const jumpSeqRef = useRef(0);
|
|
94
112
|
const handledSeqRef = useRef(-1);
|
|
@@ -100,9 +118,10 @@ const JUMP_SETTLE_FALLBACK_MS = 1000;
|
|
|
100
118
|
*/ const useWindow = ctx.pageScroll === true;
|
|
101
119
|
const scrollEl = useWindow ? null : ctx.scrollContainer ?? null;
|
|
102
120
|
const resolved = useWindow || scrollEl !== null;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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(()=>{
|
|
106
125
|
if (!resolved || noData) {
|
|
107
126
|
return;
|
|
108
127
|
}
|
|
@@ -129,29 +148,64 @@ const JUMP_SETTLE_FALLBACK_MS = 1000;
|
|
|
129
148
|
scrollEl,
|
|
130
149
|
noData
|
|
131
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
|
+
]);
|
|
132
172
|
const virtualizer = useVirtualizer({
|
|
133
173
|
count: totalItems,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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,
|
|
138
179
|
estimateSize: ()=>resolvedEstimatedRowHeight,
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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,
|
|
142
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,
|
|
143
195
|
// React 19: let React batch the scroll-driven updates natively.
|
|
144
196
|
useFlushSync: false,
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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,
|
|
148
201
|
observeElementOffset: useWindow ? observeWindowOffset : observeElementOffset,
|
|
149
202
|
scrollToFn: useWindow ? windowScroll : elementScroll,
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
|
155
209
|
});
|
|
156
210
|
const virtualItems = virtualizer.getVirtualItems();
|
|
157
211
|
const totalSize = virtualizer.getTotalSize();
|
|
@@ -163,9 +217,10 @@ const JUMP_SETTLE_FALLBACK_MS = 1000;
|
|
|
163
217
|
* scroller that isn't height-bounded (every row mounts). Runs after paint so
|
|
164
218
|
* the measurements are real; stripped from production builds.
|
|
165
219
|
*/ useEffect(()=>{
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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";
|
|
169
224
|
if (isProduction || noData || totalItems === 0 || typeof window === "undefined") {
|
|
170
225
|
return;
|
|
171
226
|
}
|
|
@@ -189,29 +244,60 @@ const JUMP_SETTLE_FALLBACK_MS = 1000;
|
|
|
189
244
|
totalItems
|
|
190
245
|
]);
|
|
191
246
|
/**
|
|
192
|
-
* Expose scrollToIndex via ref.
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
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
|
+
*
|
|
197
260
|
*/ useImperativeHandle(ref, ()=>({
|
|
198
|
-
scrollToIndex: (index)=>{
|
|
261
|
+
scrollToIndex: (index, options)=>{
|
|
199
262
|
if (index < 0 || index >= totalItems) {
|
|
200
263
|
console.warn(`scrollToIndex: index ${index} is out of bounds${totalItems === 0 ? " (the grid is empty)" : ` (0-${totalItems - 1})`}`);
|
|
201
264
|
return;
|
|
202
265
|
}
|
|
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
|
+
}
|
|
203
285
|
const items = virtualizer.getVirtualItems();
|
|
204
286
|
const first = items.length > 0 ? items[0].index : 0;
|
|
205
287
|
const last = items.length > 0 ? items[items.length - 1].index : 0;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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);
|
|
211
296
|
jumpSeqRef.current += 1;
|
|
212
297
|
setJump({
|
|
213
298
|
index,
|
|
214
299
|
overscan: reach + overscan,
|
|
300
|
+
instant: reach > FAR_JUMP_THRESHOLD,
|
|
215
301
|
seq: jumpSeqRef.current
|
|
216
302
|
});
|
|
217
303
|
}
|
|
@@ -223,10 +309,17 @@ const JUMP_SETTLE_FALLBACK_MS = 1000;
|
|
|
223
309
|
/**
|
|
224
310
|
* Perform a pending jump once the widened window has mounted the target row
|
|
225
311
|
* (react-virtual recomputes the window synchronously, so it is mounted in the
|
|
226
|
-
* commit that follows the `jump` change), then restore the normal window
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
* (
|
|
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
|
+
*
|
|
230
323
|
*/ useLayoutEffect(()=>{
|
|
231
324
|
if (jump === null || handledSeqRef.current === jump.seq) {
|
|
232
325
|
return;
|
|
@@ -234,15 +327,30 @@ const JUMP_SETTLE_FALLBACK_MS = 1000;
|
|
|
234
327
|
handledSeqRef.current = jump.seq;
|
|
235
328
|
const rowEl = bodyRef.current?.querySelector(`[data-index="${jump.index}"]`);
|
|
236
329
|
if (!rowEl) {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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);
|
|
241
335
|
return;
|
|
242
336
|
}
|
|
243
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
|
+
}
|
|
244
352
|
rowEl.scrollIntoView({
|
|
245
|
-
behavior:
|
|
353
|
+
behavior: "smooth",
|
|
246
354
|
block: "center"
|
|
247
355
|
});
|
|
248
356
|
const scroller = useWindow ? typeof window !== "undefined" ? window : null : scrollEl;
|
|
@@ -258,7 +366,8 @@ const JUMP_SETTLE_FALLBACK_MS = 1000;
|
|
|
258
366
|
}, [
|
|
259
367
|
jump,
|
|
260
368
|
useWindow,
|
|
261
|
-
scrollEl
|
|
369
|
+
scrollEl,
|
|
370
|
+
virtualizer
|
|
262
371
|
]);
|
|
263
372
|
/**
|
|
264
373
|
* Report a monotonic high-water mark via onVisibleCountChange: the furthest
|
|
@@ -324,10 +433,11 @@ const JUMP_SETTLE_FALLBACK_MS = 1000;
|
|
|
324
433
|
}), [
|
|
325
434
|
ctx
|
|
326
435
|
]);
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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);
|
|
331
441
|
const bodyClass = getBodyClass(className);
|
|
332
442
|
const paddingTop = virtualItems.length > 0 ? Math.max(0, virtualItems[0].start - scrollMargin) : 0;
|
|
333
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.0
|
|
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
|
}
|