@mui/x-virtualizer 0.1.0 → 0.1.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.
- package/CHANGELOG.md +217 -6
- package/esm/features/colspan.d.ts +4 -0
- package/esm/features/colspan.js +4 -3
- package/esm/features/dimensions.d.ts +10 -0
- package/esm/features/dimensions.js +30 -49
- package/esm/features/virtualization.d.ts +17 -10
- package/esm/features/virtualization.js +55 -74
- package/esm/index.js +1 -1
- package/esm/models/core.d.ts +10 -4
- package/esm/models/core.js +20 -8
- package/esm/models/dimensions.d.ts +2 -33
- package/esm/useVirtualizer.d.ts +34 -53
- package/features/colspan.d.ts +4 -0
- package/features/colspan.js +4 -3
- package/features/dimensions.d.ts +10 -0
- package/features/dimensions.js +28 -48
- package/features/virtualization.d.ts +17 -10
- package/features/virtualization.js +55 -74
- package/index.js +1 -1
- package/models/core.d.ts +10 -4
- package/models/core.js +21 -9
- package/models/dimensions.d.ts +2 -33
- package/package.json +26 -15
- package/useVirtualizer.d.ts +34 -53
package/features/dimensions.d.ts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { Store } from '@mui/x-internals/store';
|
|
2
2
|
import { ColumnWithWidth, DimensionsState, RowId, RowsMetaState, Size } from "../models/index.js";
|
|
3
3
|
import type { BaseState, VirtualizerParams } from "../useVirtualizer.js";
|
|
4
|
+
export type DimensionsParams = {
|
|
5
|
+
rowHeight: number;
|
|
6
|
+
columnsTotalWidth: number;
|
|
7
|
+
leftPinnedWidth: number;
|
|
8
|
+
rightPinnedWidth: number;
|
|
9
|
+
topPinnedHeight: number;
|
|
10
|
+
bottomPinnedHeight: number;
|
|
11
|
+
scrollbarSize?: number;
|
|
12
|
+
};
|
|
4
13
|
export declare const Dimensions: {
|
|
5
14
|
initialize: typeof initializeState;
|
|
6
15
|
use: typeof useDimensions;
|
|
@@ -8,6 +17,7 @@ export declare const Dimensions: {
|
|
|
8
17
|
rootSize: (state: BaseState) => Size;
|
|
9
18
|
dimensions: (state: BaseState) => DimensionsState;
|
|
10
19
|
rowHeight: (state: BaseState) => number;
|
|
20
|
+
contentHeight: (state: BaseState) => number;
|
|
11
21
|
rowsMeta: (state: BaseState) => RowsMetaState;
|
|
12
22
|
columnPositions: (_: any, columns: ColumnWithWidth[]) => number[];
|
|
13
23
|
needsHorizontalScrollbar: (state: BaseState) => boolean;
|
package/features/dimensions.js
CHANGED
|
@@ -31,15 +31,11 @@ const EMPTY_DIMENSIONS = {
|
|
|
31
31
|
hasScrollX: false,
|
|
32
32
|
hasScrollY: false,
|
|
33
33
|
scrollbarSize: 0,
|
|
34
|
-
headerHeight: 0,
|
|
35
|
-
groupHeaderHeight: 0,
|
|
36
|
-
headerFilterHeight: 0,
|
|
37
34
|
rowWidth: 0,
|
|
38
35
|
rowHeight: 0,
|
|
39
36
|
columnsTotalWidth: 0,
|
|
40
37
|
leftPinnedWidth: 0,
|
|
41
38
|
rightPinnedWidth: 0,
|
|
42
|
-
headersTotalHeight: 0,
|
|
43
39
|
topContainerHeight: 0,
|
|
44
40
|
bottomContainerHeight: 0
|
|
45
41
|
};
|
|
@@ -47,6 +43,7 @@ const selectors = {
|
|
|
47
43
|
rootSize: state => state.rootSize,
|
|
48
44
|
dimensions: state => state.dimensions,
|
|
49
45
|
rowHeight: state => state.dimensions.rowHeight,
|
|
46
|
+
contentHeight: state => state.dimensions.contentSize.height,
|
|
50
47
|
rowsMeta: state => state.rowsMeta,
|
|
51
48
|
columnPositions: (0, _store.createSelectorMemoized)((_, columns) => {
|
|
52
49
|
const positions = [];
|
|
@@ -94,15 +91,15 @@ function useDimensions(store, params, _api) {
|
|
|
94
91
|
refs,
|
|
95
92
|
dimensions: {
|
|
96
93
|
rowHeight,
|
|
97
|
-
headerHeight,
|
|
98
94
|
columnsTotalWidth,
|
|
99
|
-
groupHeaderHeight,
|
|
100
|
-
headerFilterHeight,
|
|
101
|
-
headersTotalHeight,
|
|
102
95
|
leftPinnedWidth,
|
|
103
|
-
rightPinnedWidth
|
|
104
|
-
|
|
96
|
+
rightPinnedWidth,
|
|
97
|
+
topPinnedHeight,
|
|
98
|
+
bottomPinnedHeight
|
|
99
|
+
},
|
|
100
|
+
onResize
|
|
105
101
|
} = params;
|
|
102
|
+
const containerNode = refs.container.current;
|
|
106
103
|
const updateDimensions = React.useCallback(() => {
|
|
107
104
|
if (isFirstSizing.current) {
|
|
108
105
|
return;
|
|
@@ -113,9 +110,9 @@ function useDimensions(store, params, _api) {
|
|
|
113
110
|
// All the floating point dimensions should be rounded to .1 decimal places to avoid subpixel rendering issues
|
|
114
111
|
// https://github.com/mui/mui-x/issues/9550#issuecomment-1619020477
|
|
115
112
|
// https://github.com/mui/mui-x/issues/15721
|
|
116
|
-
const scrollbarSize = measureScrollbarSize(
|
|
117
|
-
const topContainerHeight =
|
|
118
|
-
const bottomContainerHeight = rowsMeta.pinnedBottomRowsTotalHeight;
|
|
113
|
+
const scrollbarSize = measureScrollbarSize(containerNode, params.dimensions.scrollbarSize);
|
|
114
|
+
const topContainerHeight = topPinnedHeight + rowsMeta.pinnedTopRowsTotalHeight;
|
|
115
|
+
const bottomContainerHeight = bottomPinnedHeight + rowsMeta.pinnedBottomRowsTotalHeight;
|
|
119
116
|
const contentSize = {
|
|
120
117
|
width: columnsTotalWidth,
|
|
121
118
|
height: (0, _math.roundToDecimalPlaces)(rowsMeta.currentPageTotalHeight, 1)
|
|
@@ -179,15 +176,11 @@ function useDimensions(store, params, _api) {
|
|
|
179
176
|
hasScrollX,
|
|
180
177
|
hasScrollY,
|
|
181
178
|
scrollbarSize,
|
|
182
|
-
headerHeight,
|
|
183
|
-
groupHeaderHeight,
|
|
184
|
-
headerFilterHeight,
|
|
185
179
|
rowWidth,
|
|
186
180
|
rowHeight,
|
|
187
181
|
columnsTotalWidth,
|
|
188
182
|
leftPinnedWidth,
|
|
189
183
|
rightPinnedWidth,
|
|
190
|
-
headersTotalHeight,
|
|
191
184
|
topContainerHeight,
|
|
192
185
|
bottomContainerHeight
|
|
193
186
|
};
|
|
@@ -198,21 +191,16 @@ function useDimensions(store, params, _api) {
|
|
|
198
191
|
store.update({
|
|
199
192
|
dimensions: newDimensions
|
|
200
193
|
});
|
|
201
|
-
|
|
194
|
+
onResize?.(newDimensions.root);
|
|
195
|
+
}, [store, containerNode, params.dimensions.scrollbarSize, params.autoHeight, onResize, rowHeight, columnsTotalWidth, leftPinnedWidth, rightPinnedWidth, topPinnedHeight, bottomPinnedHeight]);
|
|
202
196
|
const {
|
|
203
|
-
resizeThrottleMs
|
|
204
|
-
onResize
|
|
197
|
+
resizeThrottleMs
|
|
205
198
|
} = params;
|
|
206
199
|
const updateDimensionCallback = (0, _useEventCallback.default)(updateDimensions);
|
|
207
|
-
const debouncedUpdateDimensions = React.useMemo(() => resizeThrottleMs > 0 ? (0, _throttle.throttle)(
|
|
208
|
-
updateDimensionCallback();
|
|
209
|
-
onResize?.(store.state.rootSize);
|
|
210
|
-
}, resizeThrottleMs) : undefined, [resizeThrottleMs, onResize, store, updateDimensionCallback]);
|
|
200
|
+
const debouncedUpdateDimensions = React.useMemo(() => resizeThrottleMs > 0 ? (0, _throttle.throttle)(updateDimensionCallback, resizeThrottleMs) : undefined, [resizeThrottleMs, updateDimensionCallback]);
|
|
211
201
|
React.useEffect(() => debouncedUpdateDimensions?.clear, [debouncedUpdateDimensions]);
|
|
212
|
-
(0,
|
|
213
|
-
|
|
214
|
-
(0, _store.useStoreEffect)(store, selectors.rootSize, (_, size) => {
|
|
215
|
-
params.onResize?.(size);
|
|
202
|
+
const setRootSize = (0, _useEventCallback.default)(rootSize => {
|
|
203
|
+
store.state.rootSize = rootSize;
|
|
216
204
|
if (isFirstSizing.current || !debouncedUpdateDimensions) {
|
|
217
205
|
// We want to initialize the grid dimensions as soon as possible to avoid flickering
|
|
218
206
|
isFirstSizing.current = false;
|
|
@@ -221,6 +209,8 @@ function useDimensions(store, params, _api) {
|
|
|
221
209
|
debouncedUpdateDimensions();
|
|
222
210
|
}
|
|
223
211
|
});
|
|
212
|
+
(0, _useEnhancedEffect.default)(() => observeRootNode(containerNode, store, setRootSize), [containerNode, store, setRootSize]);
|
|
213
|
+
(0, _useEnhancedEffect.default)(updateDimensions, [updateDimensions]);
|
|
224
214
|
const rowsMeta = useRowsMeta(store, params, updateDimensions);
|
|
225
215
|
return {
|
|
226
216
|
updateDimensions,
|
|
@@ -257,7 +247,6 @@ function useRowsMeta(store, params, updateDimensions) {
|
|
|
257
247
|
return entry;
|
|
258
248
|
});
|
|
259
249
|
const {
|
|
260
|
-
rowIdToIndexMap,
|
|
261
250
|
applyRowHeight
|
|
262
251
|
} = params;
|
|
263
252
|
const processHeightEntry = React.useCallback(row => {
|
|
@@ -289,12 +278,7 @@ function useRowsMeta(store, params, updateDimensions) {
|
|
|
289
278
|
}
|
|
290
279
|
}
|
|
291
280
|
if (getRowSpacing) {
|
|
292
|
-
const
|
|
293
|
-
const spacing = getRowSpacing(row, {
|
|
294
|
-
isFirstVisible: indexRelativeToCurrentPage === 0,
|
|
295
|
-
isLastVisible: indexRelativeToCurrentPage === rows.length - 1,
|
|
296
|
-
indexRelativeToCurrentPage
|
|
297
|
-
});
|
|
281
|
+
const spacing = getRowSpacing(row);
|
|
298
282
|
entry.spacingTop = spacing.top ?? 0;
|
|
299
283
|
entry.spacingBottom = spacing.bottom ?? 0;
|
|
300
284
|
} else {
|
|
@@ -303,17 +287,17 @@ function useRowsMeta(store, params, updateDimensions) {
|
|
|
303
287
|
}
|
|
304
288
|
applyRowHeight?.(entry, row);
|
|
305
289
|
return entry;
|
|
306
|
-
}, [store,
|
|
290
|
+
}, [store, getRowHeightProp, getRowHeightEntry, getEstimatedRowHeight, rowHeight, getRowSpacing, applyRowHeight]);
|
|
307
291
|
const hydrateRowsMeta = React.useCallback(() => {
|
|
308
292
|
hasRowWithAutoHeight.current = false;
|
|
309
|
-
const pinnedTopRowsTotalHeight = pinnedRows
|
|
293
|
+
const pinnedTopRowsTotalHeight = pinnedRows?.top.reduce((acc, row) => {
|
|
310
294
|
const entry = processHeightEntry(row);
|
|
311
295
|
return acc + entry.content + entry.spacingTop + entry.spacingBottom + entry.detail;
|
|
312
|
-
}, 0);
|
|
313
|
-
const pinnedBottomRowsTotalHeight = pinnedRows
|
|
296
|
+
}, 0) ?? 0;
|
|
297
|
+
const pinnedBottomRowsTotalHeight = pinnedRows?.bottom.reduce((acc, row) => {
|
|
314
298
|
const entry = processHeightEntry(row);
|
|
315
299
|
return acc + entry.content + entry.spacingTop + entry.spacingBottom + entry.detail;
|
|
316
|
-
}, 0);
|
|
300
|
+
}, 0) ?? 0;
|
|
317
301
|
const positions = [];
|
|
318
302
|
const currentPageTotalHeight = rows.reduce((acc, row) => {
|
|
319
303
|
positions.push(acc);
|
|
@@ -369,7 +353,7 @@ function useRowsMeta(store, params, updateDimensions) {
|
|
|
369
353
|
const entry = entries[i];
|
|
370
354
|
const height = entry.borderBoxSize && entry.borderBoxSize.length > 0 ? entry.borderBoxSize[0].blockSize : entry.contentRect.height;
|
|
371
355
|
const rowId = entry.target.__mui_id;
|
|
372
|
-
const focusedVirtualRowId = params.focusedVirtualCell()?.id;
|
|
356
|
+
const focusedVirtualRowId = params.focusedVirtualCell?.()?.id;
|
|
373
357
|
if (focusedVirtualRowId === rowId && height === 0) {
|
|
374
358
|
// Focused virtual row has 0 height.
|
|
375
359
|
// We don't want to store it to avoid scroll jumping.
|
|
@@ -408,7 +392,7 @@ function useRowsMeta(store, params, updateDimensions) {
|
|
|
408
392
|
resetRowHeights
|
|
409
393
|
};
|
|
410
394
|
}
|
|
411
|
-
function observeRootNode(node, store) {
|
|
395
|
+
function observeRootNode(node, store, setRootSize) {
|
|
412
396
|
if (!node) {
|
|
413
397
|
return undefined;
|
|
414
398
|
}
|
|
@@ -418,9 +402,7 @@ function observeRootNode(node, store) {
|
|
|
418
402
|
height: (0, _math.roundToDecimalPlaces)(bounds.height, 1)
|
|
419
403
|
};
|
|
420
404
|
if (store.state.rootSize === _models.Size.EMPTY || !_models.Size.equals(initialSize, store.state.rootSize)) {
|
|
421
|
-
|
|
422
|
-
rootSize: initialSize
|
|
423
|
-
});
|
|
405
|
+
setRootSize(initialSize);
|
|
424
406
|
}
|
|
425
407
|
if (typeof ResizeObserver === 'undefined') {
|
|
426
408
|
return undefined;
|
|
@@ -434,9 +416,7 @@ function observeRootNode(node, store) {
|
|
|
434
416
|
height: (0, _math.roundToDecimalPlaces)(entry.contentRect.height, 1)
|
|
435
417
|
};
|
|
436
418
|
if (!_models.Size.equals(rootSize, store.state.rootSize)) {
|
|
437
|
-
|
|
438
|
-
rootSize
|
|
439
|
-
});
|
|
419
|
+
setRootSize(rootSize);
|
|
440
420
|
}
|
|
441
421
|
});
|
|
442
422
|
observer.observe(node);
|
|
@@ -5,6 +5,16 @@ import type { CellColSpanInfo } from "../models/colspan.js";
|
|
|
5
5
|
import { Dimensions } from "./dimensions.js";
|
|
6
6
|
import type { BaseState, VirtualizerParams } from "../useVirtualizer.js";
|
|
7
7
|
import { PinnedRowPosition, RenderContext, ColumnsRenderContext, ColumnWithWidth, RowId, RowEntry } from "../models/index.js";
|
|
8
|
+
export type VirtualizationParams = {
|
|
9
|
+
/** @default false */
|
|
10
|
+
isRtl?: boolean;
|
|
11
|
+
/** The row buffer in pixels to render before and after the viewport.
|
|
12
|
+
* @default 150 */
|
|
13
|
+
rowBufferPx?: number;
|
|
14
|
+
/** The column buffer in pixels to render before and after the viewport.
|
|
15
|
+
* @default 150 */
|
|
16
|
+
columnBufferPx?: number;
|
|
17
|
+
};
|
|
8
18
|
export type VirtualizationState = {
|
|
9
19
|
enabled: boolean;
|
|
10
20
|
enabledForRows: boolean;
|
|
@@ -44,16 +54,16 @@ type RequiredAPI = Dimensions.API & AbstractAPI;
|
|
|
44
54
|
declare function useVirtualization(store: Store<BaseState>, params: VirtualizerParams, api: RequiredAPI): {
|
|
45
55
|
getters: {
|
|
46
56
|
setPanels: React.Dispatch<React.SetStateAction<Readonly<Map<any, React.ReactNode>>>>;
|
|
47
|
-
getRows: (rowParams
|
|
57
|
+
getRows: (rowParams?: {
|
|
48
58
|
rows?: RowEntry[];
|
|
49
59
|
position?: PinnedRowPosition;
|
|
50
60
|
renderContext?: RenderContext;
|
|
51
|
-
}
|
|
61
|
+
}, unstable_rowTree?: Record<RowId, any>) => React.ReactNode[];
|
|
52
62
|
getContainerProps: () => {
|
|
53
|
-
ref:
|
|
63
|
+
ref: (node: HTMLDivElement | null) => void;
|
|
54
64
|
};
|
|
55
65
|
getScrollerProps: () => {
|
|
56
|
-
ref:
|
|
66
|
+
ref: (node: HTMLDivElement | null) => void;
|
|
57
67
|
onScroll: () => void;
|
|
58
68
|
onWheel: ((event: React.WheelEvent) => void) | undefined;
|
|
59
69
|
onTouchMove: ((event: React.TouchEvent) => void) | undefined;
|
|
@@ -62,22 +72,19 @@ declare function useVirtualization(store: Store<BaseState>, params: VirtualizerP
|
|
|
62
72
|
tabIndex: number | undefined;
|
|
63
73
|
};
|
|
64
74
|
getContentProps: () => {
|
|
65
|
-
style: React.CSSProperties;
|
|
66
|
-
role: string;
|
|
67
75
|
ref: (node: HTMLDivElement | null) => void;
|
|
68
|
-
|
|
69
|
-
getRenderZoneProps: () => {
|
|
76
|
+
style: React.CSSProperties;
|
|
70
77
|
role: string;
|
|
71
78
|
};
|
|
72
79
|
getScrollbarVerticalProps: () => {
|
|
73
|
-
ref:
|
|
80
|
+
ref: (node: HTMLDivElement | null) => void;
|
|
74
81
|
scrollPosition: React.RefObject<{
|
|
75
82
|
top: number;
|
|
76
83
|
left: number;
|
|
77
84
|
}>;
|
|
78
85
|
};
|
|
79
86
|
getScrollbarHorizontalProps: () => {
|
|
80
|
-
ref:
|
|
87
|
+
ref: (node: HTMLDivElement | null) => void;
|
|
81
88
|
scrollPosition: React.RefObject<{
|
|
82
89
|
top: number;
|
|
83
90
|
left: number;
|
|
@@ -21,6 +21,7 @@ var platform = _interopRequireWildcard(require("@mui/x-internals/platform"));
|
|
|
21
21
|
var _useRunOnce = require("@mui/x-internals/useRunOnce");
|
|
22
22
|
var _useFirstRender = require("@mui/x-internals/useFirstRender");
|
|
23
23
|
var _store = require("@mui/x-internals/store");
|
|
24
|
+
var _core = require("../models/core");
|
|
24
25
|
var _dimensions = require("./dimensions");
|
|
25
26
|
var _models = require("../models");
|
|
26
27
|
/* eslint-disable import/export, @typescript-eslint/no-redeclare */
|
|
@@ -66,28 +67,29 @@ function initializeState(params) {
|
|
|
66
67
|
|
|
67
68
|
function useVirtualization(store, params, api) {
|
|
68
69
|
const {
|
|
69
|
-
initialState,
|
|
70
|
-
isRtl,
|
|
71
|
-
rows,
|
|
72
|
-
range,
|
|
73
|
-
columns,
|
|
74
|
-
pinnedRows,
|
|
75
|
-
pinnedColumns,
|
|
76
70
|
refs,
|
|
77
|
-
hasColSpan,
|
|
78
71
|
dimensions: {
|
|
79
72
|
rowHeight,
|
|
80
73
|
columnsTotalWidth
|
|
81
74
|
},
|
|
82
|
-
|
|
75
|
+
virtualization: {
|
|
76
|
+
isRtl = false,
|
|
77
|
+
rowBufferPx = 150,
|
|
78
|
+
columnBufferPx = 150
|
|
79
|
+
},
|
|
80
|
+
colspan,
|
|
81
|
+
initialState,
|
|
82
|
+
rows,
|
|
83
|
+
range,
|
|
84
|
+
columns,
|
|
85
|
+
pinnedRows = _core.PinnedRows.EMPTY,
|
|
86
|
+
pinnedColumns = _core.PinnedColumns.EMPTY,
|
|
83
87
|
minimalContentHeight,
|
|
84
88
|
autoHeight,
|
|
85
89
|
onWheel,
|
|
86
90
|
onTouchMove,
|
|
87
91
|
onRenderContextChange,
|
|
88
92
|
onScrollChange,
|
|
89
|
-
rowBufferPx,
|
|
90
|
-
columnBufferPx,
|
|
91
93
|
scrollReset,
|
|
92
94
|
renderRow,
|
|
93
95
|
renderInfiniteLoadingTrigger
|
|
@@ -95,10 +97,12 @@ function useVirtualization(store, params, api) {
|
|
|
95
97
|
const needsHorizontalScrollbar = (0, _store.useStore)(store, _dimensions.Dimensions.selectors.needsHorizontalScrollbar);
|
|
96
98
|
const hasBottomPinnedRows = pinnedRows.bottom.length > 0;
|
|
97
99
|
const [panels, setPanels] = React.useState(EMPTY_DETAIL_PANELS);
|
|
100
|
+
const [, setRefTick] = React.useState(0);
|
|
98
101
|
const isRenderContextReady = React.useRef(false);
|
|
99
102
|
const renderContext = (0, _store.useStore)(store, selectors.renderContext);
|
|
100
103
|
const enabledForRows = (0, _store.useStore)(store, selectors.enabledForRows);
|
|
101
104
|
const enabledForColumns = (0, _store.useStore)(store, selectors.enabledForColumns);
|
|
105
|
+
const contentHeight = (0, _store.useStore)(store, _dimensions.Dimensions.selectors.contentHeight);
|
|
102
106
|
|
|
103
107
|
/*
|
|
104
108
|
* Scroll context logic
|
|
@@ -123,18 +127,17 @@ function useVirtualization(store, params, api) {
|
|
|
123
127
|
const frozenContext = React.useRef(undefined);
|
|
124
128
|
const scrollCache = (0, _useLazyRef.default)(() => createScrollCache(isRtl, rowBufferPx, columnBufferPx, rowHeight * 15, MINIMUM_COLUMN_WIDTH * 6)).current;
|
|
125
129
|
const updateRenderContext = React.useCallback(nextRenderContext => {
|
|
126
|
-
if (areRenderContextsEqual(nextRenderContext, store.state.virtualization.renderContext)) {
|
|
127
|
-
|
|
130
|
+
if (!areRenderContextsEqual(nextRenderContext, store.state.virtualization.renderContext)) {
|
|
131
|
+
store.set('virtualization', (0, _extends2.default)({}, store.state.virtualization, {
|
|
132
|
+
renderContext: nextRenderContext
|
|
133
|
+
}));
|
|
128
134
|
}
|
|
129
|
-
const didRowsIntervalChange = nextRenderContext.firstRowIndex !== previousRowContext.current.firstRowIndex || nextRenderContext.lastRowIndex !== previousRowContext.current.lastRowIndex;
|
|
130
|
-
store.set('virtualization', (0, _extends2.default)({}, store.state.virtualization, {
|
|
131
|
-
renderContext: nextRenderContext
|
|
132
|
-
}));
|
|
133
135
|
|
|
134
136
|
// The lazy-loading hook is listening to `renderedRowsIntervalChange`,
|
|
135
137
|
// but only does something if we already have a render context, because
|
|
136
138
|
// otherwise we would call an update directly on mount
|
|
137
139
|
const isReady = _dimensions.Dimensions.selectors.dimensions(store.state).isReady;
|
|
140
|
+
const didRowsIntervalChange = nextRenderContext.firstRowIndex !== previousRowContext.current.firstRowIndex || nextRenderContext.lastRowIndex !== previousRowContext.current.lastRowIndex;
|
|
138
141
|
if (isReady && didRowsIntervalChange) {
|
|
139
142
|
previousRowContext.current = nextRenderContext;
|
|
140
143
|
onRenderContextChange?.(nextRenderContext);
|
|
@@ -159,7 +162,7 @@ function useVirtualization(store, params, api) {
|
|
|
159
162
|
const dy = newScroll.top - scrollPosition.current.top;
|
|
160
163
|
const isScrolling = dx !== 0 || dy !== 0;
|
|
161
164
|
scrollPosition.current = newScroll;
|
|
162
|
-
const direction = isScrolling ?
|
|
165
|
+
const direction = isScrolling ? _models.ScrollDirection.forDelta(dx, dy) : _models.ScrollDirection.NONE;
|
|
163
166
|
|
|
164
167
|
// Since previous render, we have scrolled...
|
|
165
168
|
const rowScroll = Math.abs(scrollPosition.current.top - previousContextScrollPosition.current.top);
|
|
@@ -227,9 +230,7 @@ function useVirtualization(store, params, api) {
|
|
|
227
230
|
* section of code to the DataGrid's rowTree model. The `unstable_rowTree` param is a temporary
|
|
228
231
|
* solution to decouple the code.
|
|
229
232
|
*/
|
|
230
|
-
const getRows = (
|
|
231
|
-
// eslint-disable-next-line @typescript-eslint/default-param-last
|
|
232
|
-
rowParams = {}, unstable_rowTree) => {
|
|
233
|
+
const getRows = (rowParams = {}, unstable_rowTree) => {
|
|
233
234
|
if (!rowParams.rows && !range) {
|
|
234
235
|
return [];
|
|
235
236
|
}
|
|
@@ -259,7 +260,7 @@ function useVirtualization(store, params, api) {
|
|
|
259
260
|
const lastRowToRender = Math.min(baseRenderContext.lastRowIndex, rowModels.length);
|
|
260
261
|
const rowIndexes = rowParams.rows ? createRange(0, rowParams.rows.length) : createRange(firstRowToRender, lastRowToRender);
|
|
261
262
|
let virtualRowIndex = -1;
|
|
262
|
-
const focusedVirtualCell = params.focusedVirtualCell();
|
|
263
|
+
const focusedVirtualCell = params.focusedVirtualCell?.();
|
|
263
264
|
if (!isPinnedSection && focusedVirtualCell) {
|
|
264
265
|
if (focusedVirtualCell.rowIndex < firstRowToRender) {
|
|
265
266
|
rowIndexes.unshift(focusedVirtualCell.rowIndex);
|
|
@@ -284,13 +285,13 @@ function useVirtualization(store, params, api) {
|
|
|
284
285
|
// See:
|
|
285
286
|
// - https://github.com/mui/mui-x/issues/16638
|
|
286
287
|
// - https://github.com/mui/mui-x/issues/17022
|
|
287
|
-
if (!unstable_rowTree[id]) {
|
|
288
|
+
if (unstable_rowTree && !unstable_rowTree[id]) {
|
|
288
289
|
return;
|
|
289
290
|
}
|
|
290
291
|
const rowIndex = (range?.firstRowIndex || 0) + rowIndexOffset + rowIndexInPage;
|
|
291
292
|
|
|
292
293
|
// NOTE: This is an expensive feature, the colSpan code could be optimized.
|
|
293
|
-
if (
|
|
294
|
+
if (colspan?.enabled) {
|
|
294
295
|
const minFirstColumn = pinnedColumns.left.length;
|
|
295
296
|
const maxLastColumn = columns.length - pinnedColumns.right.length;
|
|
296
297
|
api.calculateColSpan(id, minFirstColumn, maxLastColumn, columns);
|
|
@@ -336,7 +337,6 @@ function useVirtualization(store, params, api) {
|
|
|
336
337
|
offsetLeft,
|
|
337
338
|
columnsTotalWidth,
|
|
338
339
|
baseRowHeight,
|
|
339
|
-
columns,
|
|
340
340
|
firstColumnIndex,
|
|
341
341
|
lastColumnIndex,
|
|
342
342
|
focusedColumnIndex: isVirtualFocusColumn ? focusedVirtualCell.columnIndex : undefined,
|
|
@@ -373,12 +373,12 @@ function useVirtualization(store, params, api) {
|
|
|
373
373
|
}
|
|
374
374
|
return size;
|
|
375
375
|
}, [columnsTotalWidth, contentHeight, needsHorizontalScrollbar, minimalContentHeight]);
|
|
376
|
-
const
|
|
377
|
-
const
|
|
376
|
+
const scrollRestoreCallback = React.useRef(null);
|
|
377
|
+
const contentNodeRef = React.useCallback(node => {
|
|
378
378
|
if (!node) {
|
|
379
379
|
return;
|
|
380
380
|
}
|
|
381
|
-
|
|
381
|
+
scrollRestoreCallback.current?.(columnsTotalWidth, contentHeight);
|
|
382
382
|
}, [columnsTotalWidth, contentHeight]);
|
|
383
383
|
(0, _useEnhancedEffect.default)(() => {
|
|
384
384
|
if (!isRenderContextReady.current) {
|
|
@@ -400,54 +400,58 @@ function useVirtualization(store, params, api) {
|
|
|
400
400
|
top,
|
|
401
401
|
left
|
|
402
402
|
} = initialState.scroll;
|
|
403
|
-
|
|
404
|
-
// On initial mount, if we have columns available, we can restore the horizontal scroll immediately, but we need to skip the resulting scroll event, otherwise we would recalculate the render context at position top=0, left=restoredValue, but the initial render context is already calculated based on the initial value of scrollPosition ref.
|
|
405
403
|
const isScrollRestored = {
|
|
406
404
|
top: !(top > 0),
|
|
407
405
|
left: !(left > 0)
|
|
408
406
|
};
|
|
409
407
|
if (!isScrollRestored.left && columnsTotalWidth) {
|
|
410
408
|
scroller.scrollLeft = left;
|
|
411
|
-
ignoreNextScrollEvent.current = true;
|
|
412
409
|
isScrollRestored.left = true;
|
|
410
|
+
ignoreNextScrollEvent.current = true;
|
|
413
411
|
}
|
|
414
412
|
|
|
415
|
-
//
|
|
413
|
+
// To restore the vertical scroll, we need to wait until the rows are available in the DOM (otherwise
|
|
414
|
+
// there's nowhere to scroll). We still set the scrollTop to the initial value at this point in case
|
|
415
|
+
// there already are rows rendered in the DOM, but we only confirm `isScrollRestored.top = true` in the
|
|
416
|
+
// asynchronous callback below.
|
|
416
417
|
if (!isScrollRestored.top && contentHeight) {
|
|
417
418
|
scroller.scrollTop = top;
|
|
418
419
|
ignoreNextScrollEvent.current = true;
|
|
419
|
-
isScrollRestored.top = true;
|
|
420
420
|
}
|
|
421
|
-
|
|
422
|
-
// To restore the vertical scroll, we need to wait until the rows are available in the DOM (otherwise there's nowhere to scroll), but before paint to avoid reflows
|
|
423
421
|
if (!isScrollRestored.top || !isScrollRestored.left) {
|
|
424
|
-
|
|
422
|
+
scrollRestoreCallback.current = (columnsTotalWidthCurrent, contentHeightCurrent) => {
|
|
425
423
|
if (!isScrollRestored.left && columnsTotalWidthCurrent) {
|
|
426
424
|
scroller.scrollLeft = left;
|
|
427
|
-
ignoreNextScrollEvent.current = true;
|
|
428
425
|
isScrollRestored.left = true;
|
|
426
|
+
ignoreNextScrollEvent.current = true;
|
|
429
427
|
}
|
|
430
428
|
if (!isScrollRestored.top && contentHeightCurrent) {
|
|
431
429
|
scroller.scrollTop = top;
|
|
432
|
-
ignoreNextScrollEvent.current = true;
|
|
433
430
|
isScrollRestored.top = true;
|
|
431
|
+
ignoreNextScrollEvent.current = true;
|
|
434
432
|
}
|
|
435
433
|
if (isScrollRestored.left && isScrollRestored.top) {
|
|
436
|
-
|
|
434
|
+
scrollRestoreCallback.current = null;
|
|
437
435
|
}
|
|
438
436
|
};
|
|
439
437
|
}
|
|
440
438
|
}
|
|
441
439
|
});
|
|
442
440
|
(0, _store.useStoreEffect)(store, _dimensions.Dimensions.selectors.dimensions, forceUpdateRenderContext);
|
|
441
|
+
const refSetter = name => node => {
|
|
442
|
+
if (node && refs[name].current !== node) {
|
|
443
|
+
refs[name].current = node;
|
|
444
|
+
setRefTick(tick => tick + 1);
|
|
445
|
+
}
|
|
446
|
+
};
|
|
443
447
|
const getters = {
|
|
444
448
|
setPanels,
|
|
445
449
|
getRows,
|
|
446
450
|
getContainerProps: () => ({
|
|
447
|
-
ref:
|
|
451
|
+
ref: refSetter('container')
|
|
448
452
|
}),
|
|
449
453
|
getScrollerProps: () => ({
|
|
450
|
-
ref:
|
|
454
|
+
ref: refSetter('scroller'),
|
|
451
455
|
onScroll: handleScroll,
|
|
452
456
|
onWheel,
|
|
453
457
|
onTouchMove,
|
|
@@ -458,19 +462,16 @@ function useVirtualization(store, params, api) {
|
|
|
458
462
|
tabIndex: platform.isFirefox ? -1 : undefined
|
|
459
463
|
}),
|
|
460
464
|
getContentProps: () => ({
|
|
465
|
+
ref: contentNodeRef,
|
|
461
466
|
style: contentSize,
|
|
462
|
-
role: 'presentation'
|
|
463
|
-
ref: onContentSizeApplied
|
|
464
|
-
}),
|
|
465
|
-
getRenderZoneProps: () => ({
|
|
466
|
-
role: 'rowgroup'
|
|
467
|
+
role: 'presentation'
|
|
467
468
|
}),
|
|
468
469
|
getScrollbarVerticalProps: () => ({
|
|
469
|
-
ref:
|
|
470
|
+
ref: refSetter('scrollbarVertical'),
|
|
470
471
|
scrollPosition
|
|
471
472
|
}),
|
|
472
473
|
getScrollbarHorizontalProps: () => ({
|
|
473
|
-
ref:
|
|
474
|
+
ref: refSetter('scrollbarHorizontal'),
|
|
474
475
|
scrollPosition
|
|
475
476
|
}),
|
|
476
477
|
getScrollAreaProps: () => ({
|
|
@@ -483,9 +484,9 @@ function useVirtualization(store, params, api) {
|
|
|
483
484
|
});
|
|
484
485
|
});
|
|
485
486
|
React.useEffect(() => {
|
|
486
|
-
store.update(
|
|
487
|
+
store.update({
|
|
487
488
|
getters
|
|
488
|
-
})
|
|
489
|
+
});
|
|
489
490
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
490
491
|
}, Object.values(getters));
|
|
491
492
|
|
|
@@ -523,8 +524,8 @@ function inputsSelector(store, params, api, enabledForRows, enabledForColumns) {
|
|
|
523
524
|
enabledForRows,
|
|
524
525
|
enabledForColumns,
|
|
525
526
|
autoHeight: params.autoHeight,
|
|
526
|
-
rowBufferPx: params.rowBufferPx,
|
|
527
|
-
columnBufferPx: params.columnBufferPx,
|
|
527
|
+
rowBufferPx: params.virtualization.rowBufferPx,
|
|
528
|
+
columnBufferPx: params.virtualization.columnBufferPx,
|
|
528
529
|
leftPinnedWidth: dimensions.leftPinnedWidth,
|
|
529
530
|
columnsTotalWidth: dimensions.columnsTotalWidth,
|
|
530
531
|
viewportInnerWidth: dimensions.viewportInnerSize.width,
|
|
@@ -654,8 +655,8 @@ function deriveRenderContext(inputs, nextRenderContext, scrollCache) {
|
|
|
654
655
|
const [initialFirstColumnToRender, lastColumnToRender] = getIndexesToRender({
|
|
655
656
|
firstIndex: nextRenderContext.firstColumnIndex,
|
|
656
657
|
lastIndex: nextRenderContext.lastColumnIndex,
|
|
657
|
-
minFirstIndex: inputs.pinnedColumns
|
|
658
|
-
maxLastIndex: inputs.columns.length - inputs.pinnedColumns
|
|
658
|
+
minFirstIndex: inputs.pinnedColumns?.left.length ?? 0,
|
|
659
|
+
maxLastIndex: inputs.columns.length - (inputs.pinnedColumns?.right.length ?? 0),
|
|
659
660
|
bufferBefore: scrollCache.buffer.columnBefore,
|
|
660
661
|
bufferAfter: scrollCache.buffer.columnAfter,
|
|
661
662
|
positions: inputs.columnPositions,
|
|
@@ -736,26 +737,6 @@ function computeOffsetLeft(columnPositions, renderContext, pinnedLeftLength) {
|
|
|
736
737
|
const left = (columnPositions[renderContext.firstColumnIndex] ?? 0) - (columnPositions[pinnedLeftLength] ?? 0);
|
|
737
738
|
return Math.abs(left);
|
|
738
739
|
}
|
|
739
|
-
function directionForDelta(dx, dy) {
|
|
740
|
-
if (dx === 0 && dy === 0) {
|
|
741
|
-
return _models.ScrollDirection.NONE;
|
|
742
|
-
}
|
|
743
|
-
/* eslint-disable */
|
|
744
|
-
if (Math.abs(dy) >= Math.abs(dx)) {
|
|
745
|
-
if (dy > 0) {
|
|
746
|
-
return _models.ScrollDirection.DOWN;
|
|
747
|
-
} else {
|
|
748
|
-
return _models.ScrollDirection.UP;
|
|
749
|
-
}
|
|
750
|
-
} else {
|
|
751
|
-
if (dx > 0) {
|
|
752
|
-
return _models.ScrollDirection.RIGHT;
|
|
753
|
-
} else {
|
|
754
|
-
return _models.ScrollDirection.LEFT;
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
/* eslint-enable */
|
|
758
|
-
}
|
|
759
740
|
function bufferForDirection(isRtl, direction, rowBufferPx, columnBufferPx, verticalBuffer, horizontalBuffer) {
|
|
760
741
|
if (isRtl) {
|
|
761
742
|
switch (direction) {
|
package/index.js
CHANGED
package/models/core.d.ts
CHANGED
|
@@ -2,13 +2,13 @@ export type Size = {
|
|
|
2
2
|
width: number;
|
|
3
3
|
height: number;
|
|
4
4
|
};
|
|
5
|
-
export declare
|
|
6
|
-
|
|
5
|
+
export declare const Size: {
|
|
6
|
+
EMPTY: {
|
|
7
7
|
width: number;
|
|
8
8
|
height: number;
|
|
9
9
|
};
|
|
10
|
-
|
|
11
|
-
}
|
|
10
|
+
equals: (a: Size, b: Size) => boolean;
|
|
11
|
+
};
|
|
12
12
|
export type Row = {
|
|
13
13
|
[key: string | symbol]: any;
|
|
14
14
|
};
|
|
@@ -27,10 +27,16 @@ export type PinnedRows = {
|
|
|
27
27
|
top: RowEntry[];
|
|
28
28
|
bottom: RowEntry[];
|
|
29
29
|
};
|
|
30
|
+
export declare const PinnedRows: {
|
|
31
|
+
EMPTY: PinnedRows;
|
|
32
|
+
};
|
|
30
33
|
export type PinnedColumns = {
|
|
31
34
|
left: Column[];
|
|
32
35
|
right: Column[];
|
|
33
36
|
};
|
|
37
|
+
export declare const PinnedColumns: {
|
|
38
|
+
EMPTY: PinnedColumns;
|
|
39
|
+
};
|
|
34
40
|
export type FocusedCell = {
|
|
35
41
|
rowIndex: number;
|
|
36
42
|
columnIndex: number;
|
package/models/core.js
CHANGED
|
@@ -3,19 +3,31 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.Size = exports.ScrollDirection = void 0;
|
|
6
|
+
exports.Size = exports.ScrollDirection = exports.PinnedRows = exports.PinnedColumns = void 0;
|
|
7
7
|
/* eslint-disable @typescript-eslint/no-redeclare */
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
|
|
9
|
+
const Size = exports.Size = {
|
|
10
|
+
EMPTY: {
|
|
11
11
|
width: 0,
|
|
12
12
|
height: 0
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
},
|
|
14
|
+
equals: (a, b) => a.width === b.width && a.height === b.height
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// TODO
|
|
18
|
+
|
|
19
|
+
const PinnedRows = exports.PinnedRows = {
|
|
20
|
+
EMPTY: {
|
|
21
|
+
top: [],
|
|
22
|
+
bottom: []
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const PinnedColumns = exports.PinnedColumns = {
|
|
26
|
+
EMPTY: {
|
|
27
|
+
left: [],
|
|
28
|
+
right: []
|
|
16
29
|
}
|
|
17
|
-
|
|
18
|
-
})(Size || (exports.Size = Size = {})); // TODO
|
|
30
|
+
};
|
|
19
31
|
let ScrollDirection = exports.ScrollDirection = /*#__PURE__*/function (ScrollDirection) {
|
|
20
32
|
ScrollDirection[ScrollDirection["NONE"] = 0] = "NONE";
|
|
21
33
|
ScrollDirection[ScrollDirection["UP"] = 1] = "UP";
|