@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.
@@ -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;
@@ -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(params.refs.container.current, params.scrollbarSize);
117
- const topContainerHeight = headersTotalHeight + rowsMeta.pinnedTopRowsTotalHeight;
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
- }, [store, params.refs.container, params.scrollbarSize, params.autoHeight, rowHeight, headerHeight, groupHeaderHeight, headerFilterHeight, columnsTotalWidth, headersTotalHeight, leftPinnedWidth, rightPinnedWidth]);
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, _useEnhancedEffect.default)(() => observeRootNode(refs.container.current, store), [refs, store]);
213
- (0, _useEnhancedEffect.default)(updateDimensions, [updateDimensions]);
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 indexRelativeToCurrentPage = rowIdToIndexMap.get(row.id) ?? -1;
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, rows, getRowHeightProp, getRowHeightEntry, getEstimatedRowHeight, rowHeight, getRowSpacing, rowIdToIndexMap, applyRowHeight]);
290
+ }, [store, getRowHeightProp, getRowHeightEntry, getEstimatedRowHeight, rowHeight, getRowSpacing, applyRowHeight]);
307
291
  const hydrateRowsMeta = React.useCallback(() => {
308
292
  hasRowWithAutoHeight.current = false;
309
- const pinnedTopRowsTotalHeight = pinnedRows.top.reduce((acc, row) => {
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.bottom.reduce((acc, row) => {
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
- store.update({
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
- store.update({
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
- } | undefined, unstable_rowTree: Record<RowId, any>) => React.ReactNode[];
61
+ }, unstable_rowTree?: Record<RowId, any>) => React.ReactNode[];
52
62
  getContainerProps: () => {
53
- ref: React.RefObject<HTMLDivElement | null>;
63
+ ref: (node: HTMLDivElement | null) => void;
54
64
  };
55
65
  getScrollerProps: () => {
56
- ref: React.RefObject<HTMLDivElement | null>;
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: React.RefObject<HTMLDivElement | null>;
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: React.RefObject<HTMLDivElement | null>;
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
- contentHeight,
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
- return;
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 ? directionForDelta(dx, dy) : _models.ScrollDirection.NONE;
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 (hasColSpan) {
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 verticalScrollRestoreCallback = React.useRef(null);
377
- const onContentSizeApplied = React.useCallback(node => {
376
+ const scrollRestoreCallback = React.useRef(null);
377
+ const contentNodeRef = React.useCallback(node => {
378
378
  if (!node) {
379
379
  return;
380
380
  }
381
- verticalScrollRestoreCallback.current?.(columnsTotalWidth, contentHeight);
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
- // For the sake of completeness, but I'm not sure if contentHeight is ever available at this point. Maybe when virtualisation is disabled?
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
- verticalScrollRestoreCallback.current = (columnsTotalWidthCurrent, contentHeightCurrent) => {
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
- verticalScrollRestoreCallback.current = null;
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: params.refs.container
451
+ ref: refSetter('container')
448
452
  }),
449
453
  getScrollerProps: () => ({
450
- ref: refs.scroller,
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: refs.scrollbarVertical,
470
+ ref: refSetter('scrollbarVertical'),
470
471
  scrollPosition
471
472
  }),
472
473
  getScrollbarHorizontalProps: () => ({
473
- ref: refs.scrollbarHorizontal,
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((0, _extends2.default)({}, store.state, {
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.left.length,
658
- maxLastIndex: inputs.columns.length - inputs.pinnedColumns.right.length,
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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-virtualizer v0.1.0
2
+ * @mui/x-virtualizer v0.1.2
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
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 namespace Size {
6
- const EMPTY: {
5
+ export declare const Size: {
6
+ EMPTY: {
7
7
  width: number;
8
8
  height: number;
9
9
  };
10
- function equals(a: Size, b: Size): boolean;
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
- let Size = exports.Size = void 0;
9
- (function (_Size) {
10
- const EMPTY = _Size.EMPTY = {
8
+
9
+ const Size = exports.Size = {
10
+ EMPTY: {
11
11
  width: 0,
12
12
  height: 0
13
- };
14
- function equals(a, b) {
15
- return a.width === b.width && a.height === b.height;
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
- _Size.equals = equals;
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";