@infinite-table/infinite-react 0.2.0 → 0.2.1

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.
Files changed (4) hide show
  1. package/index.d.ts +110 -101
  2. package/index.esm.js +190 -59
  3. package/index.js +171 -40
  4. package/package.json +2 -2
package/index.d.ts CHANGED
@@ -199,6 +199,103 @@ declare module "components/types/SubscriptionCallback" {
199
199
  onChange: (fn: SubscriptionCallbackOnChangeFn<T>) => VoidFn;
200
200
  }
201
201
  }
202
+ declare module "components/types/Size" {
203
+ export interface Size {
204
+ width: number;
205
+ height: number;
206
+ }
207
+ export type OnResizeFn = (size: {
208
+ width: number;
209
+ height: number;
210
+ }) => void;
211
+ }
212
+ declare module "components/VirtualBrain/index" {
213
+ import { Logger } from "utils/debug";
214
+ import { Size } from "components/types/Size";
215
+ import type { OnScrollFn, ScrollPosition } from "components/types/ScrollPosition";
216
+ type MainAxisOptions = 'vertical' | 'horizontal';
217
+ export type VirtualBrainOptions = {
218
+ mainAxis: MainAxisOptions;
219
+ itemSize: MainAxisSize;
220
+ itemSpan?: ({ itemIndex }: {
221
+ itemIndex: number;
222
+ }) => number;
223
+ count: number;
224
+ name?: string;
225
+ };
226
+ export type MainAxisSize = number | ((itemIndex: number) => number);
227
+ type OnRenderCountChangeFn = (renderCount: number) => void;
228
+ type OnSizeChangeFn = (size: number) => void;
229
+ type OnDestroyFn = () => void;
230
+ export type GetItemPositionFn = (itemIndex: number) => number;
231
+ export type RenderRange = {
232
+ renderStartIndex: number;
233
+ renderEndIndex: number;
234
+ };
235
+ export type OnRenderRangeChangeFn = (renderRange: RenderRange) => void;
236
+ export class VirtualBrain extends Logger {
237
+ private scrollPosition;
238
+ private scrollPositionOnMainAxis;
239
+ private itemSpanParent;
240
+ private itemSpanValue;
241
+ private itemSizeCache;
242
+ private itemOffsetCache;
243
+ private options;
244
+ private destroyed;
245
+ private totalSize;
246
+ private onScrollFns;
247
+ private onTotalSizeChangeFns;
248
+ private onRenderCountChangeFns;
249
+ private onRenderRangeChangeFns;
250
+ private onDestroyFns;
251
+ name: string | undefined;
252
+ private renderCount;
253
+ private renderRange;
254
+ private availableSize;
255
+ constructor(options: VirtualBrainOptions);
256
+ setAvailableSize: (size: Size) => void;
257
+ getOptions(): VirtualBrainOptions;
258
+ private updateRenderCount;
259
+ private computeRenderCount;
260
+ private setRenderCount;
261
+ private updateRenderRange;
262
+ getRenderStartIndex(): number;
263
+ getRenderCount: () => number;
264
+ setScrollPosition: (scrollPosition: ScrollPosition) => void;
265
+ private notifyScrollChange;
266
+ private notifyDestroy;
267
+ private notifyTotalSizeChange;
268
+ private notifyRenderCountChange;
269
+ private notifyRenderRangeChange;
270
+ getRenderRange: () => RenderRange;
271
+ private setRenderRange;
272
+ private computeRenderRange;
273
+ getScrollPosition: () => ScrollPosition;
274
+ onScroll: (fn: OnScrollFn) => () => void;
275
+ onTotalSizeChange: (fn: OnSizeChangeFn) => () => void;
276
+ onRenderCountChange: (fn: OnRenderCountChangeFn) => () => void;
277
+ onRenderRangeChange: (fn: OnRenderRangeChangeFn) => () => void;
278
+ onDestroy: (fn: OnDestroyFn) => () => void;
279
+ update: (newCount: VirtualBrainOptions['count'], newItemSize: VirtualBrainOptions['itemSize'], newItemSpan?: VirtualBrainOptions['itemSpan']) => void;
280
+ private reset;
281
+ private computeItemSpanUpTo;
282
+ private computeCacheFor;
283
+ private getItemSizeCacheFor;
284
+ getItemSpan: (itemIndex: number) => number;
285
+ getItemSpanParent: (itemIndex: number) => number;
286
+ getItemSizeWithSpan: (itemIndex: number, itemSpan: number) => number;
287
+ /**
288
+ * For now, this doesn't take into account the itemspan, and it's okay not to
289
+ * @param itemIndex
290
+ * @returns the size of the specified item
291
+ */
292
+ getItemSize: (itemIndex: number) => number;
293
+ getTotalSize: () => number;
294
+ getItemAt: (scrollPos: number) => number;
295
+ getItemOffsetFor: (itemIndex: number) => number;
296
+ destroy: () => void;
297
+ }
298
+ }
202
299
  declare module "components/DataSource/types" {
203
300
  import * as React from 'react';
204
301
  import { MultisortInfo } from "utils/multisort/index";
@@ -211,6 +308,7 @@ declare module "components/DataSource/types" {
211
308
  import { NonUndefined } from "components/types/NonUndefined";
212
309
  import { SubscriptionCallback } from "components/types/SubscriptionCallback";
213
310
  import { ScrollStopInfo } from "components/InfiniteTable/types/InfiniteTableProps";
311
+ import { RenderRange } from "components/VirtualBrain/index";
214
312
  export interface DataSourceDataParams<T> {
215
313
  originalDataArray: T[];
216
314
  sortInfo?: DataSourceSortInfo<T>;
@@ -290,8 +388,11 @@ declare module "components/DataSource/types" {
290
388
  showSeparatePivotColumnForSingleAggregation: boolean;
291
389
  dataParams?: DataSourceDataParams<T>;
292
390
  originalLazyGroupData: LazyGroupDataDeepMap<T>;
391
+ originalLazyGroupDataChangeDetect: number | string;
392
+ scrollStopDelayUpdatedByTable: number;
293
393
  notifyScrollbarsChange: SubscriptionCallback<Scrollbars>;
294
394
  notifyScrollStop: SubscriptionCallback<ScrollStopInfo>;
395
+ notifyRenderRangeChange: SubscriptionCallback<RenderRange>;
295
396
  originalDataArray: T[];
296
397
  lastSortDataArray?: T[];
297
398
  lastGroupDataArray?: InfiniteTableRowInfo<T>[];
@@ -383,16 +484,24 @@ declare module "components/DataSource/publicHooks/useDataSource" {
383
484
  export function useDataSource<T>(): DataSourceState<T>;
384
485
  export function useDataSourceContextValue<T>(): DataSourceContextValue<T>;
385
486
  }
487
+ declare module "utils/getGlobal" {
488
+ export function getGlobal<T extends Window>(): T;
489
+ }
386
490
  declare module "components/hooks/useEffectWithChanges" {
387
491
  import { EffectCallback } from 'react';
388
492
  export function useEffectWithChanges(fn: (changes: Record<string, any>, prevValues: Record<string, any>) => void | (() => void), deps: Record<string, any>): void;
389
493
  export function useEffectWithObject(fn: EffectCallback, deps: Record<string, any>): void;
390
494
  }
495
+ declare module "components/utils/debounce" {
496
+ export function debounce(fn: Function, { wait }: {
497
+ wait: number;
498
+ }): (...args: any[]) => void;
499
+ }
391
500
  declare module "components/DataSource/privateHooks/useLoadData" {
392
501
  import { ComponentStateGeneratedActions } from "components/hooks/useComponentState";
393
502
  import { DataSourceDataParams, DataSourceData, DataSourceState } from "components/DataSource/types";
394
503
  export function buildDataSourceDataParams<T>(componentState: DataSourceState<T>, overrides?: Partial<DataSourceDataParams<T>>): DataSourceDataParams<T>;
395
- export function loadData<T>(data: DataSourceData<T>, componentState: DataSourceState<T>, actions: ComponentStateGeneratedActions<DataSourceState<T>>, overrides?: Partial<DataSourceDataParams<T>>): void;
504
+ export function loadData<T>(data: DataSourceData<T>, componentState: DataSourceState<T>, actions: ComponentStateGeneratedActions<DataSourceState<T>>, overrides?: Partial<DataSourceDataParams<T>>): Promise<void>;
396
505
  export function useLoadData<T>(): void;
397
506
  }
398
507
  declare module "components/DataSource/state/normalizeSortInfo" {
@@ -1179,16 +1288,6 @@ declare module "components/InfiniteTable/types/InfiniteTableProps" {
1179
1288
  horizontal: boolean;
1180
1289
  };
1181
1290
  }
1182
- declare module "components/types/Size" {
1183
- export interface Size {
1184
- width: number;
1185
- height: number;
1186
- }
1187
- export type OnResizeFn = (size: {
1188
- width: number;
1189
- height: number;
1190
- }) => void;
1191
- }
1192
1291
  declare module "components/VirtualBrain/ScrollListener" {
1193
1292
  import { OnScrollFn, ScrollPosition } from "components/types/ScrollPosition";
1194
1293
  export class ScrollListener {
@@ -1535,9 +1634,6 @@ declare module "components/InfiniteTable/utils/adjustColumnOrderForPinning" {
1535
1634
  import { InfiniteTablePropColumnOrderNormalized, InfiniteTablePropColumnPinningMap } from "components/InfiniteTable/types/InfiniteTableProps";
1536
1635
  export const adjustColumnOrderForPinning: (columnOrder: InfiniteTablePropColumnOrderNormalized, columnPinning: InfiniteTablePropColumnPinningMap) => InfiniteTablePropColumnOrderNormalized;
1537
1636
  }
1538
- declare module "utils/getGlobal" {
1539
- export function getGlobal<T extends Window>(): T;
1540
- }
1541
1637
  declare module "components/utils/getScrollbarWidth" {
1542
1638
  export function getScrollbarWidth(): number;
1543
1639
  }
@@ -1797,93 +1893,6 @@ declare module "components/InfiniteTable/components/InfiniteTableBody/InfiniteTa
1797
1893
  declare module "components/InfiniteTable/components/InfiniteTableBody/index" {
1798
1894
  export * from "components/InfiniteTable/components/InfiniteTableBody/InfiniteTableBody";
1799
1895
  }
1800
- declare module "components/VirtualBrain/index" {
1801
- import { Logger } from "utils/debug";
1802
- import { Size } from "components/types/Size";
1803
- import type { OnScrollFn, ScrollPosition } from "components/types/ScrollPosition";
1804
- type MainAxisOptions = 'vertical' | 'horizontal';
1805
- export type VirtualBrainOptions = {
1806
- mainAxis: MainAxisOptions;
1807
- itemSize: MainAxisSize;
1808
- itemSpan?: ({ itemIndex }: {
1809
- itemIndex: number;
1810
- }) => number;
1811
- count: number;
1812
- name?: string;
1813
- };
1814
- export type MainAxisSize = number | ((itemIndex: number) => number);
1815
- type OnRenderCountChangeFn = (renderCount: number) => void;
1816
- type OnSizeChangeFn = (size: number) => void;
1817
- type OnDestroyFn = () => void;
1818
- export type GetItemPositionFn = (itemIndex: number) => number;
1819
- export type RenderRange = {
1820
- renderStartIndex: number;
1821
- renderEndIndex: number;
1822
- };
1823
- export type OnRenderRangeChangeFn = (renderRange: RenderRange) => void;
1824
- export class VirtualBrain extends Logger {
1825
- private scrollPosition;
1826
- private scrollPositionOnMainAxis;
1827
- private itemSpanParent;
1828
- private itemSpanValue;
1829
- private itemSizeCache;
1830
- private itemOffsetCache;
1831
- private options;
1832
- private destroyed;
1833
- private totalSize;
1834
- private onScrollFns;
1835
- private onTotalSizeChangeFns;
1836
- private onRenderCountChangeFns;
1837
- private onRenderRangeChangeFns;
1838
- private onDestroyFns;
1839
- name: string | undefined;
1840
- private renderCount;
1841
- private renderRange;
1842
- private availableSize;
1843
- constructor(options: VirtualBrainOptions);
1844
- setAvailableSize: (size: Size) => void;
1845
- getOptions(): VirtualBrainOptions;
1846
- private updateRenderCount;
1847
- private computeRenderCount;
1848
- private setRenderCount;
1849
- private updateRenderRange;
1850
- getRenderStartIndex(): number;
1851
- getRenderCount: () => number;
1852
- setScrollPosition: (scrollPosition: ScrollPosition) => void;
1853
- private notifyScrollChange;
1854
- private notifyDestroy;
1855
- private notifyTotalSizeChange;
1856
- private notifyRenderCountChange;
1857
- private notifyRenderRangeChange;
1858
- getRenderRange: () => RenderRange;
1859
- private setRenderRange;
1860
- private computeRenderRange;
1861
- getScrollPosition: () => ScrollPosition;
1862
- onScroll: (fn: OnScrollFn) => () => void;
1863
- onTotalSizeChange: (fn: OnSizeChangeFn) => () => void;
1864
- onRenderCountChange: (fn: OnRenderCountChangeFn) => () => void;
1865
- onRenderRangeChange: (fn: OnRenderRangeChangeFn) => () => void;
1866
- onDestroy: (fn: OnDestroyFn) => () => void;
1867
- update: (newCount: VirtualBrainOptions['count'], newItemSize: VirtualBrainOptions['itemSize'], newItemSpan?: VirtualBrainOptions['itemSpan']) => void;
1868
- private reset;
1869
- private computeItemSpanUpTo;
1870
- private computeCacheFor;
1871
- private getItemSizeCacheFor;
1872
- getItemSpan: (itemIndex: number) => number;
1873
- getItemSpanParent: (itemIndex: number) => number;
1874
- getItemSizeWithSpan: (itemIndex: number, itemSpan: number) => number;
1875
- /**
1876
- * For now, this doesn't take into account the itemspan, and it's okay not to
1877
- * @param itemIndex
1878
- * @returns the size of the specified item
1879
- */
1880
- getItemSize: (itemIndex: number) => number;
1881
- getTotalSize: () => number;
1882
- getItemAt: (scrollPos: number) => number;
1883
- getItemOffsetFor: (itemIndex: number) => number;
1884
- destroy: () => void;
1885
- }
1886
- }
1887
1896
  declare module "components/InfiniteTable/components/ScrollbarPlaceholder" {
1888
1897
  import * as React from 'react';
1889
1898
  function HorizontalScrollbarPlaceholderFn(props: {
package/index.esm.js CHANGED
@@ -2048,7 +2048,7 @@ function useColumnGroups() {
2048
2048
  }
2049
2049
 
2050
2050
  // src/components/InfiniteTable/hooks/useColumnsWhen.ts
2051
- import { useEffect as useEffect9, useMemo as useMemo4 } from "react";
2051
+ import { useEffect as useEffect9, useMemo as useMemo5 } from "react";
2052
2052
 
2053
2053
  // src/components/InfiniteTable/state/getComputedPivotColumnsFromDataSourcePivotColumns.tsx
2054
2054
  import {
@@ -2393,7 +2393,7 @@ var getMultisortFunction = (sortInfo, get) => {
2393
2393
  };
2394
2394
 
2395
2395
  // src/components/DataSource/privateHooks/useLoadData.ts
2396
- import { useEffect as useEffect8, useRef as useRef6, useState as useState4 } from "react";
2396
+ import { useEffect as useEffect8, useMemo as useMemo4, useRef as useRef6, useState as useState4 } from "react";
2397
2397
 
2398
2398
  // src/utils/DeepMap/once.ts
2399
2399
  function once(fn) {
@@ -3029,11 +3029,33 @@ function useEffectWithChanges(fn, deps) {
3029
3029
  }, useEffectDeps);
3030
3030
  }
3031
3031
 
3032
+ // src/components/utils/debounce.ts
3033
+ function debounce(fn, { wait }) {
3034
+ let timeout = null;
3035
+ return function(...args) {
3036
+ const context = this;
3037
+ if (timeout !== null) {
3038
+ clearTimeout(timeout);
3039
+ }
3040
+ timeout = setTimeout(() => {
3041
+ fn.apply(context, args);
3042
+ }, wait);
3043
+ };
3044
+ }
3045
+
3032
3046
  // src/components/DataSource/privateHooks/useLoadData.ts
3047
+ var getRafPromise = () => new Promise((resolve) => {
3048
+ requestAnimationFrame(resolve);
3049
+ });
3033
3050
  var SKIP_DATA_CHANGES_KEYS = {
3034
3051
  originalDataArray: true,
3035
3052
  changes: true
3036
3053
  };
3054
+ function getChangeDetect() {
3055
+ var _a;
3056
+ const perfNow = (_a = getGlobal().performance) == null ? void 0 : _a.now();
3057
+ return `${Date.now()}:${perfNow}`;
3058
+ }
3037
3059
  function buildDataSourceDataParams(componentState, overrides) {
3038
3060
  var _a, _b;
3039
3061
  const sortInfo = componentState.multiSort ? componentState.sortInfo : (_b = (_a = componentState.sortInfo) == null ? void 0 : _a[0]) != null ? _b : null;
@@ -3077,10 +3099,10 @@ function loadData(data, componentState, actions, overrides) {
3077
3099
  data = data(dataParams);
3078
3100
  }
3079
3101
  const dataIsPromise = typeof data === "object" && typeof data.then === "function";
3080
- if (dataIsPromise) {
3102
+ if (dataIsPromise && !componentState.fullLazyLoad) {
3081
3103
  actions.loading = true;
3082
3104
  }
3083
- Promise.resolve(data).then((dataParam) => {
3105
+ return Promise.resolve(data).then((dataParam) => {
3084
3106
  var _a, _b, _c;
3085
3107
  let dataArray = [];
3086
3108
  let skipAssign = false;
@@ -3094,7 +3116,7 @@ function loadData(data, componentState, actions, overrides) {
3094
3116
  actions.pivotMappings = remoteData.mappings;
3095
3117
  }
3096
3118
  if (componentState.fullLazyLoad && dataParams.groupKeys) {
3097
- const lazyGroupData = DeepMap.clone(componentState.originalLazyGroupData);
3119
+ const lazyGroupData = componentState.originalLazyGroupData;
3098
3120
  const key = [LAZY_ROOT_KEY_FOR_GROUPS, ...dataParams.groupKeys];
3099
3121
  const newGroupRowInfo = {
3100
3122
  totalCount: (_a = remoteData.totalCount) != null ? _a : dataArray.length,
@@ -3108,7 +3130,7 @@ function loadData(data, componentState, actions, overrides) {
3108
3130
  };
3109
3131
  currentGroupRowInfo.items.length = currentGroupRowInfo.totalCount;
3110
3132
  const start = (_b = dataParams.lazyLoadStartIndex) != null ? _b : 0;
3111
- const end = start + ((_c = dataParams.lazyLoadBatchSize) != null ? _c : dataArray.length);
3133
+ const end = Math.min((_c = remoteData.totalCount) != null ? _c : start + dataParams.lazyLoadBatchSize);
3112
3134
  for (let i = start; i < end; i++) {
3113
3135
  currentGroupRowInfo.items[i] = newGroupRowInfo.items[i - start];
3114
3136
  }
@@ -3119,7 +3141,7 @@ function loadData(data, componentState, actions, overrides) {
3119
3141
  lazyGroupData.set(key, newGroupRowInfo);
3120
3142
  }
3121
3143
  skipAssign = true;
3122
- actions.originalLazyGroupData = lazyGroupData;
3144
+ actions.originalLazyGroupDataChangeDetect = getChangeDetect();
3123
3145
  }
3124
3146
  } else {
3125
3147
  dataArray = dataParam;
@@ -3127,7 +3149,7 @@ function loadData(data, componentState, actions, overrides) {
3127
3149
  if (!skipAssign) {
3128
3150
  actions.originalDataArray = dataArray;
3129
3151
  }
3130
- if (dataIsPromise) {
3152
+ if (dataIsPromise && !componentState.fullLazyLoad) {
3131
3153
  actions.loading = false;
3132
3154
  }
3133
3155
  });
@@ -3146,9 +3168,7 @@ function useLoadData() {
3146
3168
  pivotBy,
3147
3169
  livePagination,
3148
3170
  livePaginationCursor,
3149
- cursorId: stateCursorId,
3150
- lazyLoadBatchSize,
3151
- notifyScrollStop
3171
+ cursorId: stateCursorId
3152
3172
  } = componentState;
3153
3173
  const [scrollbars, setScrollbars] = useState4({
3154
3174
  vertical: false,
@@ -3189,33 +3209,7 @@ function useLoadData() {
3189
3209
  cursorId: livePagination ? stateCursorId : null
3190
3210
  };
3191
3211
  const initialRef = useRef6(true);
3192
- useEffect8(() => {
3193
- if (lazyLoadBatchSize && lazyLoadBatchSize > 0) {
3194
- return notifyScrollStop.onChange((param) => {
3195
- console.log("scroll stop", param);
3196
- if (!param) {
3197
- return;
3198
- }
3199
- const componentState2 = getComponentState();
3200
- const {
3201
- firstVisibleRowIndex: startIndex,
3202
- lastVisibleRowIndex: endIndex
3203
- } = param;
3204
- const firstBatchStart = Math.floor(startIndex / lazyLoadBatchSize) * lazyLoadBatchSize;
3205
- const lastBatchStart = Math.floor(endIndex / lazyLoadBatchSize) * lazyLoadBatchSize;
3206
- const isRowLoaded = (index) => componentState2.dataArray[index].data != null;
3207
- for (let batchStartIndex = firstBatchStart; batchStartIndex <= lastBatchStart; batchStartIndex = batchStartIndex + lazyLoadBatchSize) {
3208
- if (!isRowLoaded(batchStartIndex)) {
3209
- loadData(componentState2.data, componentState2, actions, {
3210
- lazyLoadBatchSize,
3211
- lazyLoadStartIndex: batchStartIndex
3212
- });
3213
- }
3214
- }
3215
- });
3216
- }
3217
- return;
3218
- }, [lazyLoadBatchSize]);
3212
+ useLazyLoadRange();
3219
3213
  useEffectWithChanges(() => {
3220
3214
  const componentState2 = getComponentState();
3221
3215
  if (typeof componentState2.data !== "function") {
@@ -3236,6 +3230,126 @@ function useLoadData() {
3236
3230
  }
3237
3231
  }, depsObject);
3238
3232
  }
3233
+ function useLazyLoadRange() {
3234
+ const {
3235
+ getComponentState,
3236
+ componentActions: actions,
3237
+ componentState
3238
+ } = useComponentState();
3239
+ const loadingCache = useMemo4(() => {
3240
+ return new Map();
3241
+ }, [componentState.data]);
3242
+ globalThis.loadingCache = loadingCache;
3243
+ const {
3244
+ lazyLoadBatchSize,
3245
+ notifyRenderRangeChange,
3246
+ dataArray,
3247
+ scrollStopDelayUpdatedByTable
3248
+ } = componentState;
3249
+ const latestRenderRangeRef = useRef6(null);
3250
+ const loadRange = (param, cache = loadingCache) => {
3251
+ const componentState2 = getComponentState();
3252
+ param = param || latestRenderRangeRef.current;
3253
+ if (!param) {
3254
+ return;
3255
+ }
3256
+ const { renderStartIndex: startIndex, renderEndIndex: endIndex } = param;
3257
+ if (!lazyLoadBatchSize || lazyLoadBatchSize <= 0) {
3258
+ return;
3259
+ }
3260
+ lazyLoadRange({
3261
+ startIndex,
3262
+ endIndex,
3263
+ lazyLoadBatchSize,
3264
+ componentState: componentState2,
3265
+ componentActions: actions
3266
+ }, cache);
3267
+ };
3268
+ const debouncedLoadRange = useMemo4(() => debounce(loadRange, { wait: scrollStopDelayUpdatedByTable }), [scrollStopDelayUpdatedByTable]);
3269
+ useEffect8(() => {
3270
+ if (lazyLoadBatchSize && lazyLoadBatchSize > 0) {
3271
+ return notifyRenderRangeChange.onChange((param) => {
3272
+ latestRenderRangeRef.current = param;
3273
+ loadRange(param, loadingCache);
3274
+ });
3275
+ }
3276
+ return;
3277
+ }, [lazyLoadBatchSize, loadingCache]);
3278
+ useEffect8(() => {
3279
+ if (lazyLoadBatchSize && lazyLoadBatchSize > 0) {
3280
+ debouncedLoadRange();
3281
+ }
3282
+ return;
3283
+ }, [dataArray]);
3284
+ }
3285
+ function lazyLoadRange(options, cache) {
3286
+ const {
3287
+ startIndex,
3288
+ endIndex,
3289
+ lazyLoadBatchSize,
3290
+ componentState,
3291
+ componentActions
3292
+ } = options;
3293
+ const { dataArray } = componentState;
3294
+ const isRowLoaded = (index) => dataArray[index].data != null;
3295
+ const perGroupFnCalls = new DeepMap();
3296
+ const rootGroupKeys = ["_______xxx______"];
3297
+ for (let i = startIndex; i <= endIndex; i++) {
3298
+ const rowInfo = dataArray[i];
3299
+ if (!rowInfo) {
3300
+ continue;
3301
+ }
3302
+ const rowLoaded = rowInfo.data != null;
3303
+ const parentGroupKeys = rowInfo.groupKeys ? [...rowInfo.groupKeys] : [];
3304
+ if (rowInfo.groupKeys) {
3305
+ parentGroupKeys.pop();
3306
+ }
3307
+ const cacheKeys = parentGroupKeys.length ? parentGroupKeys : rootGroupKeys;
3308
+ const indexInGroup = rowInfo.indexInGroup;
3309
+ if (!rowLoaded) {
3310
+ let fnCalls = perGroupFnCalls.get(cacheKeys);
3311
+ const batchStartIndexInGroup = Math.floor(indexInGroup / lazyLoadBatchSize) * lazyLoadBatchSize;
3312
+ const offset = rowInfo.indexInGroup - batchStartIndexInGroup;
3313
+ const absoluteIndexOfBatchStart = dataArray[rowInfo.indexInAll - offset].indexInAll;
3314
+ const batchStartRowLoaded = isRowLoaded(absoluteIndexOfBatchStart);
3315
+ const batchStartRowId = dataArray[absoluteIndexOfBatchStart].id;
3316
+ if (batchStartRowLoaded || (fnCalls == null ? void 0 : fnCalls[batchStartRowId])) {
3317
+ continue;
3318
+ }
3319
+ const shouldSetFnCalls = !fnCalls;
3320
+ fnCalls = fnCalls || {};
3321
+ const currentFnCall = {
3322
+ lazyLoadStartIndex: batchStartIndexInGroup,
3323
+ lazyLoadBatchSize,
3324
+ groupKeys: parentGroupKeys
3325
+ };
3326
+ if (!fnCalls[batchStartRowId]) {
3327
+ fnCalls[batchStartRowId] = currentFnCall;
3328
+ }
3329
+ if (shouldSetFnCalls) {
3330
+ perGroupFnCalls.set(cacheKeys, fnCalls);
3331
+ }
3332
+ }
3333
+ }
3334
+ let initialPromise = Promise.resolve(true);
3335
+ const allFnCalls = [];
3336
+ perGroupFnCalls.topDownValues().forEach((record) => {
3337
+ allFnCalls.push(...Object.values(record));
3338
+ });
3339
+ allFnCalls.reduce((promise, fnCall) => {
3340
+ const cacheKey = `${fnCall.groupKeys.join("-")}:${fnCall.lazyLoadStartIndex}`;
3341
+ if (cache && cache.has(cacheKey)) {
3342
+ return promise;
3343
+ }
3344
+ cache == null ? void 0 : cache.set(cacheKey, true);
3345
+ const args = [componentState.data, componentState, componentActions, fnCall];
3346
+ return promise.then(() => getRafPromise()).then(() => loadData(...args)).then(() => {
3347
+ requestAnimationFrame(() => {
3348
+ cache == null ? void 0 : cache.delete(cacheKey);
3349
+ });
3350
+ });
3351
+ }, initialPromise);
3352
+ }
3239
3353
 
3240
3354
  // src/components/DataSource/state/normalizeSortInfo.ts
3241
3355
  var EMPTY_ARRAY = Object.freeze([]);
@@ -3435,8 +3549,11 @@ function initSetupState2() {
3435
3549
  dataParams: void 0,
3436
3550
  notifyScrollbarsChange: buildSubscriptionCallback(),
3437
3551
  notifyScrollStop: buildSubscriptionCallback(),
3552
+ notifyRenderRangeChange: buildSubscriptionCallback(),
3438
3553
  pivotTotalColumnPosition: "end",
3554
+ scrollStopDelayUpdatedByTable: 100,
3439
3555
  originalLazyGroupData,
3556
+ originalLazyGroupDataChangeDetect: 0,
3440
3557
  originalDataArray,
3441
3558
  cursorId: defaultCursorId,
3442
3559
  showSeparatePivotColumnForSingleAggregation: false,
@@ -3631,7 +3748,7 @@ function getPivotColumnsAndColumnGroups({
3631
3748
  var _a;
3632
3749
  const { rowInfo } = params;
3633
3750
  if (!rowInfo.data) {
3634
- return "Loading ... TODO replace this with a loading indicator";
3751
+ return "Loading ...";
3635
3752
  }
3636
3753
  return rowInfo.groupKeys ? rowInfo.groupKeys[((_a = rowInfo.groupKeys) == null ? void 0 : _a.length) - 1] : null;
3637
3754
  }
@@ -3809,6 +3926,7 @@ function concludeReducer(params) {
3809
3926
  "generateGroupRows",
3810
3927
  "originalDataArray",
3811
3928
  "originalLazyGroupData",
3929
+ "originalLazyGroupDataChangeDetect",
3812
3930
  "groupBy",
3813
3931
  "groupRowsState",
3814
3932
  "pivotBy",
@@ -3953,7 +4071,7 @@ function useToggleGroupRow() {
3953
4071
 
3954
4072
  // src/components/InfiniteTable/hooks/useColumnsWhen.ts
3955
4073
  function useGroupByMap(groupBy) {
3956
- const groupByMap = useMemo4(() => {
4074
+ const groupByMap = useMemo5(() => {
3957
4075
  return groupBy.reduce((acc, groupBy2, index) => {
3958
4076
  acc.set(groupBy2.field, {
3959
4077
  groupBy: groupBy2,
@@ -4505,7 +4623,7 @@ var InfiniteTableCell = memo3(InfiniteTableCellFn);
4505
4623
  var import_binary_search = __toModule(require_binary_search());
4506
4624
  import {
4507
4625
  useCallback as useCallback6,
4508
- useMemo as useMemo5,
4626
+ useMemo as useMemo6,
4509
4627
  useRef as useRef7,
4510
4628
  useState as useState6
4511
4629
  } from "react";
@@ -4552,8 +4670,8 @@ var useColumnPointerEvents = ({
4552
4670
  const dragging = draggingDiff != null;
4553
4671
  const { componentActions, computed } = useInfiniteTable();
4554
4672
  const { computedColumnOrder } = computed;
4555
- const column = useMemo5(() => columns.get(columnId), [columnId, columns]);
4556
- const { columnIndex, columnWidth, columnsArray } = useMemo5(() => {
4673
+ const column = useMemo6(() => columns.get(columnId), [columnId, columns]);
4674
+ const { columnIndex, columnWidth, columnsArray } = useMemo6(() => {
4557
4675
  const columnsArray2 = Array.from(columns.values());
4558
4676
  return {
4559
4677
  columnsArray: columnsArray2,
@@ -4890,7 +5008,7 @@ function AvoidReactDiffFn(props) {
4890
5008
  var AvoidReactDiff = memo4(AvoidReactDiffFn);
4891
5009
 
4892
5010
  // src/components/RawList/useVirtualRenderer.ts
4893
- import { useMemo as useMemo6 } from "react";
5011
+ import { useMemo as useMemo7 } from "react";
4894
5012
 
4895
5013
  // src/components/RawList/ReactVirtualRenderer.tsx
4896
5014
  import {
@@ -5206,7 +5324,7 @@ function createRenderer(brain) {
5206
5324
  function useVirtualRenderer({
5207
5325
  brain
5208
5326
  }) {
5209
- return useMemo6(() => {
5327
+ return useMemo7(() => {
5210
5328
  return createRenderer(brain);
5211
5329
  }, [brain]);
5212
5330
  }
@@ -5298,7 +5416,7 @@ var InfiniteTableHeader = memo6(InfiniteTableHeaderFn);
5298
5416
  import {
5299
5417
  createElement as createElement18,
5300
5418
  memo as memo7,
5301
- useMemo as useMemo7,
5419
+ useMemo as useMemo8,
5302
5420
  useRef as useRef10
5303
5421
  } from "react";
5304
5422
 
@@ -5512,7 +5630,7 @@ function InfiniteTableHeaderUnvirtualizedFn(props) {
5512
5630
  } = useInfiniteTable();
5513
5631
  const domRef = useRef10(null);
5514
5632
  const hasColumnGroups = computedColumnGroups.size > 0;
5515
- const columnHeaderGroups = useMemo7(() => {
5633
+ const columnHeaderGroups = useMemo8(() => {
5516
5634
  return hasColumnGroups ? renderColumnHeaderGroups({
5517
5635
  columnGroups: computedColumnGroups,
5518
5636
  columnGroupsDepthsMap,
@@ -5749,7 +5867,7 @@ function SpacePlaceholderFn(props) {
5749
5867
  var SpacePlaceholder = memo8(SpacePlaceholderFn);
5750
5868
 
5751
5869
  // src/components/InfiniteTable/hooks/useListRendering.tsx
5752
- import { useCallback as useCallback17, useEffect as useEffect22, useMemo as useMemo10, useRef as useRef17 } from "react";
5870
+ import { useCallback as useCallback17, useEffect as useEffect22, useMemo as useMemo11, useRef as useRef17 } from "react";
5753
5871
 
5754
5872
  // src/components/InfiniteTable/hooks/useUnpinnedRendering.tsx
5755
5873
  import {
@@ -6435,7 +6553,7 @@ var useOnContainerScroll = ({
6435
6553
  // src/components/InfiniteTable/hooks/usePinnedRendering.tsx
6436
6554
  import {
6437
6555
  createElement as createElement29,
6438
- useMemo as useMemo8
6556
+ useMemo as useMemo9
6439
6557
  } from "react";
6440
6558
  import { useCallback as useCallback15 } from "react";
6441
6559
 
@@ -6541,7 +6659,7 @@ function PinnedRowsContainer(props) {
6541
6659
  brain,
6542
6660
  renderRow
6543
6661
  } = props;
6544
- const style = useMemo8(() => {
6662
+ const style = useMemo9(() => {
6545
6663
  const result = pinning === "end" ? {
6546
6664
  width: rowWidth,
6547
6665
  height,
@@ -6715,9 +6833,9 @@ function usePinnedStartRendering(params) {
6715
6833
  import { useEffect as useEffect21 } from "react";
6716
6834
 
6717
6835
  // src/components/hooks/useOnce.ts
6718
- import { useMemo as useMemo9 } from "react";
6836
+ import { useMemo as useMemo10 } from "react";
6719
6837
  function useOnce(fn) {
6720
- const onceFn = useMemo9(() => {
6838
+ const onceFn = useMemo10(() => {
6721
6839
  return once(fn);
6722
6840
  }, []);
6723
6841
  return onceFn();
@@ -7257,7 +7375,7 @@ function useListRendering(param) {
7257
7375
  repaintIdRef.current++;
7258
7376
  }
7259
7377
  const repaintId = repaintIdRef.current;
7260
- const rowSpan = useMemo10(() => {
7378
+ const rowSpan = useMemo11(() => {
7261
7379
  const colsWithRowspan = computedVisibleColumns.filter((col) => typeof col.rowspan === "function");
7262
7380
  return colsWithRowspan.length ? ({ itemIndex }) => {
7263
7381
  let maxSpan = 1;
@@ -7295,7 +7413,7 @@ function useListRendering(param) {
7295
7413
  const hasHorizontalScrollbar = computedUnpinnedColumnsWidth > bodySize.width - computedPinnedStartColumnsWidth - computedPinnedEndColumnsWidth;
7296
7414
  const reservedContentHeight = verticalVirtualBrain.getTotalSize() + (hasHorizontalScrollbar ? getScrollbarWidth() : 0);
7297
7415
  const hasVerticalScrollbar = bodySize.height > 0 && bodySize.height < reservedContentHeight;
7298
- const scrollbars = useMemo10(() => ({
7416
+ const scrollbars = useMemo11(() => ({
7299
7417
  vertical: hasVerticalScrollbar,
7300
7418
  horizontal: hasHorizontalScrollbar
7301
7419
  }), [hasVerticalScrollbar, hasHorizontalScrollbar]);
@@ -7465,7 +7583,7 @@ var InfiniteTableLicenseFooter = forwardRef3(function InfiniteTableLicenseFooter
7465
7583
  });
7466
7584
 
7467
7585
  // src/components/InfiniteTable/hooks/useLicense/useLicense.ts
7468
- import { useMemo as useMemo11 } from "react";
7586
+ import { useMemo as useMemo12 } from "react";
7469
7587
 
7470
7588
  // src/components/InfiniteTable/hooks/useLicense/crc32.ts
7471
7589
  var DEFAULT_ReversedPolynomial = 3988292384;
@@ -7615,10 +7733,10 @@ var origin = typeof window !== "undefined" ? window.location.origin : "";
7615
7733
  var [_fullUrl, protocol, sandboxUrl] = Array.from(SANDBOX_REGEX.exec(origin) || []);
7616
7734
  var isInsideSandbox = protocol === "https" && sandboxUrl === "sandpack.codesandbox.io";
7617
7735
  var useLicense = (licenseKey = "") => {
7618
- const valid = useMemo11(() => {
7736
+ const valid = useMemo12(() => {
7619
7737
  let valid2 = isValidLicense(licenseKey, {
7620
7738
  publishedAt: 1624970570587,
7621
- version: "0.2.0"
7739
+ version: "0.2.1"
7622
7740
  });
7623
7741
  if (!licenseKey && !valid2 && isInsideSandbox) {
7624
7742
  return true;
@@ -7820,7 +7938,8 @@ var InfiniteTableComponent = memo13(function InfiniteTableComponent2() {
7820
7938
  } = useInfiniteTable();
7821
7939
  const {
7822
7940
  componentState: { dataArray, loading },
7823
- getState: getDataSourceState
7941
+ getState: getDataSourceState,
7942
+ componentActions: dataSourceActions
7824
7943
  } = useDataSourceContextValue();
7825
7944
  const {
7826
7945
  domRef,
@@ -7833,7 +7952,8 @@ var InfiniteTableComponent = memo13(function InfiniteTableComponent2() {
7833
7952
  onHeaderHeightCSSVarChange,
7834
7953
  rowHeightCSSVar,
7835
7954
  headerHeightCSSVar,
7836
- components
7955
+ components,
7956
+ scrollStopDelay
7837
7957
  } = componentState;
7838
7958
  const { columnShifts, bodySize } = componentState;
7839
7959
  const {
@@ -7846,6 +7966,7 @@ var InfiniteTableComponent = memo13(function InfiniteTableComponent2() {
7846
7966
  scrollbars,
7847
7967
  applyScrollVertical,
7848
7968
  horizontalVirtualBrain,
7969
+ verticalVirtualBrain,
7849
7970
  reservedContentHeight
7850
7971
  } = useListRendering({
7851
7972
  getComputed,
@@ -7854,6 +7975,13 @@ var InfiniteTableComponent = memo13(function InfiniteTableComponent2() {
7854
7975
  bodySize,
7855
7976
  computed
7856
7977
  });
7978
+ useEffect24(() => {
7979
+ const dataSourceState = getDataSourceState();
7980
+ const onChange = debounce((renderRange) => {
7981
+ dataSourceState.notifyRenderRangeChange(renderRange);
7982
+ }, { wait: scrollStopDelay });
7983
+ return verticalVirtualBrain.onRenderRangeChange(onChange);
7984
+ }, [verticalVirtualBrain, scrollStopDelay]);
7857
7985
  const licenseValid = useLicense(licenseKey);
7858
7986
  const domProps = useDOMProps(componentState.domProps);
7859
7987
  const LoadMaskCmp = (_a = components == null ? void 0 : components.LoadMask) != null ? _a : LoadMask;
@@ -7866,6 +7994,9 @@ var InfiniteTableComponent = memo13(function InfiniteTableComponent2() {
7866
7994
  }
7867
7995
  notifyScrollbarsChange(scrollbars);
7868
7996
  }, [scrollbars]);
7997
+ useEffect24(() => {
7998
+ dataSourceActions.scrollStopDelayUpdatedByTable = scrollStopDelay;
7999
+ }, [scrollStopDelay]);
7869
8000
  return /* @__PURE__ */ createElement33("div", __spreadValues({
7870
8001
  ref: domRef
7871
8002
  }, domProps), header ? /* @__PURE__ */ createElement33(TableHeaderWrapper, {
package/index.js CHANGED
@@ -3013,11 +3013,33 @@ function useEffectWithChanges(fn, deps) {
3013
3013
  }, useEffectDeps);
3014
3014
  }
3015
3015
 
3016
+ // src/components/utils/debounce.ts
3017
+ function debounce(fn, { wait }) {
3018
+ let timeout = null;
3019
+ return function(...args) {
3020
+ const context = this;
3021
+ if (timeout !== null) {
3022
+ clearTimeout(timeout);
3023
+ }
3024
+ timeout = setTimeout(() => {
3025
+ fn.apply(context, args);
3026
+ }, wait);
3027
+ };
3028
+ }
3029
+
3016
3030
  // src/components/DataSource/privateHooks/useLoadData.ts
3031
+ var getRafPromise = () => new Promise((resolve) => {
3032
+ requestAnimationFrame(resolve);
3033
+ });
3017
3034
  var SKIP_DATA_CHANGES_KEYS = {
3018
3035
  originalDataArray: true,
3019
3036
  changes: true
3020
3037
  };
3038
+ function getChangeDetect() {
3039
+ var _a;
3040
+ const perfNow = (_a = getGlobal().performance) == null ? void 0 : _a.now();
3041
+ return `${Date.now()}:${perfNow}`;
3042
+ }
3021
3043
  function buildDataSourceDataParams(componentState, overrides) {
3022
3044
  var _a, _b;
3023
3045
  const sortInfo = componentState.multiSort ? componentState.sortInfo : (_b = (_a = componentState.sortInfo) == null ? void 0 : _a[0]) != null ? _b : null;
@@ -3061,10 +3083,10 @@ function loadData(data, componentState, actions, overrides) {
3061
3083
  data = data(dataParams);
3062
3084
  }
3063
3085
  const dataIsPromise = typeof data === "object" && typeof data.then === "function";
3064
- if (dataIsPromise) {
3086
+ if (dataIsPromise && !componentState.fullLazyLoad) {
3065
3087
  actions.loading = true;
3066
3088
  }
3067
- Promise.resolve(data).then((dataParam) => {
3089
+ return Promise.resolve(data).then((dataParam) => {
3068
3090
  var _a, _b, _c;
3069
3091
  let dataArray = [];
3070
3092
  let skipAssign = false;
@@ -3078,7 +3100,7 @@ function loadData(data, componentState, actions, overrides) {
3078
3100
  actions.pivotMappings = remoteData.mappings;
3079
3101
  }
3080
3102
  if (componentState.fullLazyLoad && dataParams.groupKeys) {
3081
- const lazyGroupData = DeepMap.clone(componentState.originalLazyGroupData);
3103
+ const lazyGroupData = componentState.originalLazyGroupData;
3082
3104
  const key = [LAZY_ROOT_KEY_FOR_GROUPS, ...dataParams.groupKeys];
3083
3105
  const newGroupRowInfo = {
3084
3106
  totalCount: (_a = remoteData.totalCount) != null ? _a : dataArray.length,
@@ -3092,7 +3114,7 @@ function loadData(data, componentState, actions, overrides) {
3092
3114
  };
3093
3115
  currentGroupRowInfo.items.length = currentGroupRowInfo.totalCount;
3094
3116
  const start = (_b = dataParams.lazyLoadStartIndex) != null ? _b : 0;
3095
- const end = start + ((_c = dataParams.lazyLoadBatchSize) != null ? _c : dataArray.length);
3117
+ const end = Math.min((_c = remoteData.totalCount) != null ? _c : start + dataParams.lazyLoadBatchSize);
3096
3118
  for (let i = start; i < end; i++) {
3097
3119
  currentGroupRowInfo.items[i] = newGroupRowInfo.items[i - start];
3098
3120
  }
@@ -3103,7 +3125,7 @@ function loadData(data, componentState, actions, overrides) {
3103
3125
  lazyGroupData.set(key, newGroupRowInfo);
3104
3126
  }
3105
3127
  skipAssign = true;
3106
- actions.originalLazyGroupData = lazyGroupData;
3128
+ actions.originalLazyGroupDataChangeDetect = getChangeDetect();
3107
3129
  }
3108
3130
  } else {
3109
3131
  dataArray = dataParam;
@@ -3111,7 +3133,7 @@ function loadData(data, componentState, actions, overrides) {
3111
3133
  if (!skipAssign) {
3112
3134
  actions.originalDataArray = dataArray;
3113
3135
  }
3114
- if (dataIsPromise) {
3136
+ if (dataIsPromise && !componentState.fullLazyLoad) {
3115
3137
  actions.loading = false;
3116
3138
  }
3117
3139
  });
@@ -3130,9 +3152,7 @@ function useLoadData() {
3130
3152
  pivotBy,
3131
3153
  livePagination,
3132
3154
  livePaginationCursor,
3133
- cursorId: stateCursorId,
3134
- lazyLoadBatchSize,
3135
- notifyScrollStop
3155
+ cursorId: stateCursorId
3136
3156
  } = componentState;
3137
3157
  const [scrollbars, setScrollbars] = (0, import_react15.useState)({
3138
3158
  vertical: false,
@@ -3173,33 +3193,7 @@ function useLoadData() {
3173
3193
  cursorId: livePagination ? stateCursorId : null
3174
3194
  };
3175
3195
  const initialRef = (0, import_react15.useRef)(true);
3176
- (0, import_react15.useEffect)(() => {
3177
- if (lazyLoadBatchSize && lazyLoadBatchSize > 0) {
3178
- return notifyScrollStop.onChange((param) => {
3179
- console.log("scroll stop", param);
3180
- if (!param) {
3181
- return;
3182
- }
3183
- const componentState2 = getComponentState();
3184
- const {
3185
- firstVisibleRowIndex: startIndex,
3186
- lastVisibleRowIndex: endIndex
3187
- } = param;
3188
- const firstBatchStart = Math.floor(startIndex / lazyLoadBatchSize) * lazyLoadBatchSize;
3189
- const lastBatchStart = Math.floor(endIndex / lazyLoadBatchSize) * lazyLoadBatchSize;
3190
- const isRowLoaded = (index) => componentState2.dataArray[index].data != null;
3191
- for (let batchStartIndex = firstBatchStart; batchStartIndex <= lastBatchStart; batchStartIndex = batchStartIndex + lazyLoadBatchSize) {
3192
- if (!isRowLoaded(batchStartIndex)) {
3193
- loadData(componentState2.data, componentState2, actions, {
3194
- lazyLoadBatchSize,
3195
- lazyLoadStartIndex: batchStartIndex
3196
- });
3197
- }
3198
- }
3199
- });
3200
- }
3201
- return;
3202
- }, [lazyLoadBatchSize]);
3196
+ useLazyLoadRange();
3203
3197
  useEffectWithChanges(() => {
3204
3198
  const componentState2 = getComponentState();
3205
3199
  if (typeof componentState2.data !== "function") {
@@ -3220,6 +3214,126 @@ function useLoadData() {
3220
3214
  }
3221
3215
  }, depsObject);
3222
3216
  }
3217
+ function useLazyLoadRange() {
3218
+ const {
3219
+ getComponentState,
3220
+ componentActions: actions,
3221
+ componentState
3222
+ } = useComponentState();
3223
+ const loadingCache = (0, import_react15.useMemo)(() => {
3224
+ return new Map();
3225
+ }, [componentState.data]);
3226
+ globalThis.loadingCache = loadingCache;
3227
+ const {
3228
+ lazyLoadBatchSize,
3229
+ notifyRenderRangeChange,
3230
+ dataArray,
3231
+ scrollStopDelayUpdatedByTable
3232
+ } = componentState;
3233
+ const latestRenderRangeRef = (0, import_react15.useRef)(null);
3234
+ const loadRange = (param, cache = loadingCache) => {
3235
+ const componentState2 = getComponentState();
3236
+ param = param || latestRenderRangeRef.current;
3237
+ if (!param) {
3238
+ return;
3239
+ }
3240
+ const { renderStartIndex: startIndex, renderEndIndex: endIndex } = param;
3241
+ if (!lazyLoadBatchSize || lazyLoadBatchSize <= 0) {
3242
+ return;
3243
+ }
3244
+ lazyLoadRange({
3245
+ startIndex,
3246
+ endIndex,
3247
+ lazyLoadBatchSize,
3248
+ componentState: componentState2,
3249
+ componentActions: actions
3250
+ }, cache);
3251
+ };
3252
+ const debouncedLoadRange = (0, import_react15.useMemo)(() => debounce(loadRange, { wait: scrollStopDelayUpdatedByTable }), [scrollStopDelayUpdatedByTable]);
3253
+ (0, import_react15.useEffect)(() => {
3254
+ if (lazyLoadBatchSize && lazyLoadBatchSize > 0) {
3255
+ return notifyRenderRangeChange.onChange((param) => {
3256
+ latestRenderRangeRef.current = param;
3257
+ loadRange(param, loadingCache);
3258
+ });
3259
+ }
3260
+ return;
3261
+ }, [lazyLoadBatchSize, loadingCache]);
3262
+ (0, import_react15.useEffect)(() => {
3263
+ if (lazyLoadBatchSize && lazyLoadBatchSize > 0) {
3264
+ debouncedLoadRange();
3265
+ }
3266
+ return;
3267
+ }, [dataArray]);
3268
+ }
3269
+ function lazyLoadRange(options, cache) {
3270
+ const {
3271
+ startIndex,
3272
+ endIndex,
3273
+ lazyLoadBatchSize,
3274
+ componentState,
3275
+ componentActions
3276
+ } = options;
3277
+ const { dataArray } = componentState;
3278
+ const isRowLoaded = (index) => dataArray[index].data != null;
3279
+ const perGroupFnCalls = new DeepMap();
3280
+ const rootGroupKeys = ["_______xxx______"];
3281
+ for (let i = startIndex; i <= endIndex; i++) {
3282
+ const rowInfo = dataArray[i];
3283
+ if (!rowInfo) {
3284
+ continue;
3285
+ }
3286
+ const rowLoaded = rowInfo.data != null;
3287
+ const parentGroupKeys = rowInfo.groupKeys ? [...rowInfo.groupKeys] : [];
3288
+ if (rowInfo.groupKeys) {
3289
+ parentGroupKeys.pop();
3290
+ }
3291
+ const cacheKeys = parentGroupKeys.length ? parentGroupKeys : rootGroupKeys;
3292
+ const indexInGroup = rowInfo.indexInGroup;
3293
+ if (!rowLoaded) {
3294
+ let fnCalls = perGroupFnCalls.get(cacheKeys);
3295
+ const batchStartIndexInGroup = Math.floor(indexInGroup / lazyLoadBatchSize) * lazyLoadBatchSize;
3296
+ const offset = rowInfo.indexInGroup - batchStartIndexInGroup;
3297
+ const absoluteIndexOfBatchStart = dataArray[rowInfo.indexInAll - offset].indexInAll;
3298
+ const batchStartRowLoaded = isRowLoaded(absoluteIndexOfBatchStart);
3299
+ const batchStartRowId = dataArray[absoluteIndexOfBatchStart].id;
3300
+ if (batchStartRowLoaded || (fnCalls == null ? void 0 : fnCalls[batchStartRowId])) {
3301
+ continue;
3302
+ }
3303
+ const shouldSetFnCalls = !fnCalls;
3304
+ fnCalls = fnCalls || {};
3305
+ const currentFnCall = {
3306
+ lazyLoadStartIndex: batchStartIndexInGroup,
3307
+ lazyLoadBatchSize,
3308
+ groupKeys: parentGroupKeys
3309
+ };
3310
+ if (!fnCalls[batchStartRowId]) {
3311
+ fnCalls[batchStartRowId] = currentFnCall;
3312
+ }
3313
+ if (shouldSetFnCalls) {
3314
+ perGroupFnCalls.set(cacheKeys, fnCalls);
3315
+ }
3316
+ }
3317
+ }
3318
+ let initialPromise = Promise.resolve(true);
3319
+ const allFnCalls = [];
3320
+ perGroupFnCalls.topDownValues().forEach((record) => {
3321
+ allFnCalls.push(...Object.values(record));
3322
+ });
3323
+ allFnCalls.reduce((promise, fnCall) => {
3324
+ const cacheKey = `${fnCall.groupKeys.join("-")}:${fnCall.lazyLoadStartIndex}`;
3325
+ if (cache && cache.has(cacheKey)) {
3326
+ return promise;
3327
+ }
3328
+ cache == null ? void 0 : cache.set(cacheKey, true);
3329
+ const args = [componentState.data, componentState, componentActions, fnCall];
3330
+ return promise.then(() => getRafPromise()).then(() => loadData(...args)).then(() => {
3331
+ requestAnimationFrame(() => {
3332
+ cache == null ? void 0 : cache.delete(cacheKey);
3333
+ });
3334
+ });
3335
+ }, initialPromise);
3336
+ }
3223
3337
 
3224
3338
  // src/components/DataSource/state/normalizeSortInfo.ts
3225
3339
  var EMPTY_ARRAY = Object.freeze([]);
@@ -3419,8 +3533,11 @@ function initSetupState2() {
3419
3533
  dataParams: void 0,
3420
3534
  notifyScrollbarsChange: buildSubscriptionCallback(),
3421
3535
  notifyScrollStop: buildSubscriptionCallback(),
3536
+ notifyRenderRangeChange: buildSubscriptionCallback(),
3422
3537
  pivotTotalColumnPosition: "end",
3538
+ scrollStopDelayUpdatedByTable: 100,
3423
3539
  originalLazyGroupData,
3540
+ originalLazyGroupDataChangeDetect: 0,
3424
3541
  originalDataArray,
3425
3542
  cursorId: defaultCursorId,
3426
3543
  showSeparatePivotColumnForSingleAggregation: false,
@@ -3615,7 +3732,7 @@ function getPivotColumnsAndColumnGroups({
3615
3732
  var _a;
3616
3733
  const { rowInfo } = params;
3617
3734
  if (!rowInfo.data) {
3618
- return "Loading ... TODO replace this with a loading indicator";
3735
+ return "Loading ...";
3619
3736
  }
3620
3737
  return rowInfo.groupKeys ? rowInfo.groupKeys[((_a = rowInfo.groupKeys) == null ? void 0 : _a.length) - 1] : null;
3621
3738
  }
@@ -3793,6 +3910,7 @@ function concludeReducer(params) {
3793
3910
  "generateGroupRows",
3794
3911
  "originalDataArray",
3795
3912
  "originalLazyGroupData",
3913
+ "originalLazyGroupDataChangeDetect",
3796
3914
  "groupBy",
3797
3915
  "groupRowsState",
3798
3916
  "pivotBy",
@@ -7523,7 +7641,7 @@ var useLicense = (licenseKey = "") => {
7523
7641
  const valid = (0, import_react43.useMemo)(() => {
7524
7642
  let valid2 = isValidLicense(licenseKey, {
7525
7643
  publishedAt: 1624970570587,
7526
- version: "0.2.0"
7644
+ version: "0.2.1"
7527
7645
  });
7528
7646
  if (!licenseKey && !valid2 && isInsideSandbox) {
7529
7647
  return true;
@@ -7718,7 +7836,8 @@ var InfiniteTableComponent = React35.memo(function InfiniteTableComponent2() {
7718
7836
  } = useInfiniteTable();
7719
7837
  const {
7720
7838
  componentState: { dataArray, loading },
7721
- getState: getDataSourceState
7839
+ getState: getDataSourceState,
7840
+ componentActions: dataSourceActions
7722
7841
  } = useDataSourceContextValue();
7723
7842
  const {
7724
7843
  domRef,
@@ -7731,7 +7850,8 @@ var InfiniteTableComponent = React35.memo(function InfiniteTableComponent2() {
7731
7850
  onHeaderHeightCSSVarChange,
7732
7851
  rowHeightCSSVar,
7733
7852
  headerHeightCSSVar,
7734
- components
7853
+ components,
7854
+ scrollStopDelay
7735
7855
  } = componentState;
7736
7856
  const { columnShifts, bodySize } = componentState;
7737
7857
  const {
@@ -7744,6 +7864,7 @@ var InfiniteTableComponent = React35.memo(function InfiniteTableComponent2() {
7744
7864
  scrollbars,
7745
7865
  applyScrollVertical,
7746
7866
  horizontalVirtualBrain,
7867
+ verticalVirtualBrain,
7747
7868
  reservedContentHeight
7748
7869
  } = useListRendering({
7749
7870
  getComputed,
@@ -7752,6 +7873,13 @@ var InfiniteTableComponent = React35.memo(function InfiniteTableComponent2() {
7752
7873
  bodySize,
7753
7874
  computed
7754
7875
  });
7876
+ React35.useEffect(() => {
7877
+ const dataSourceState = getDataSourceState();
7878
+ const onChange = debounce((renderRange) => {
7879
+ dataSourceState.notifyRenderRangeChange(renderRange);
7880
+ }, { wait: scrollStopDelay });
7881
+ return verticalVirtualBrain.onRenderRangeChange(onChange);
7882
+ }, [verticalVirtualBrain, scrollStopDelay]);
7755
7883
  const licenseValid = useLicense(licenseKey);
7756
7884
  const domProps = useDOMProps(componentState.domProps);
7757
7885
  const LoadMaskCmp = (_a = components == null ? void 0 : components.LoadMask) != null ? _a : LoadMask;
@@ -7764,6 +7892,9 @@ var InfiniteTableComponent = React35.memo(function InfiniteTableComponent2() {
7764
7892
  }
7765
7893
  notifyScrollbarsChange(scrollbars);
7766
7894
  }, [scrollbars]);
7895
+ React35.useEffect(() => {
7896
+ dataSourceActions.scrollStopDelayUpdatedByTable = scrollStopDelay;
7897
+ }, [scrollStopDelay]);
7767
7898
  return /* @__PURE__ */ React35.createElement("div", __spreadValues({
7768
7899
  ref: domRef
7769
7900
  }, domProps), header ? /* @__PURE__ */ React35.createElement(TableHeaderWrapper, {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@infinite-table/infinite-react",
3
3
  "description": "Infinite Table for React",
4
- "version": "0.2.0",
4
+ "version": "0.2.1",
5
5
  "main": "index.js",
6
6
  "module": "index.esm.js",
7
7
  "typings": "index.d.ts",
@@ -14,5 +14,5 @@
14
14
  },
15
15
  "license": "Commercial & Open Source",
16
16
  "//": "will be updated at build time",
17
- "publishedAt": 1643662501272
17
+ "publishedAt": 1643825063998
18
18
  }