@tanstack/virtual-core 3.0.0-beta.0 → 3.0.0-beta.12

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 +1 @@
1
- {"version":3,"file":"index.production.js","sources":["../../../../node_modules/@reach/observe-rect/dist/observe-rect.esm.js","../../src/utils.ts","../../src/index.ts"],"sourcesContent":["var props = [\"bottom\", \"height\", \"left\", \"right\", \"top\", \"width\"];\n\nvar rectChanged = function rectChanged(a, b) {\n if (a === void 0) {\n a = {};\n }\n\n if (b === void 0) {\n b = {};\n }\n\n return props.some(function (prop) {\n return a[prop] !== b[prop];\n });\n};\n\nvar observedNodes = /*#__PURE__*/new Map();\nvar rafId;\n\nvar run = function run() {\n var changedStates = [];\n observedNodes.forEach(function (state, node) {\n var newRect = node.getBoundingClientRect();\n\n if (rectChanged(newRect, state.rect)) {\n state.rect = newRect;\n changedStates.push(state);\n }\n });\n changedStates.forEach(function (state) {\n state.callbacks.forEach(function (cb) {\n return cb(state.rect);\n });\n });\n rafId = window.requestAnimationFrame(run);\n};\n\nfunction observeRect(node, cb) {\n return {\n observe: function observe() {\n var wasEmpty = observedNodes.size === 0;\n\n if (observedNodes.has(node)) {\n observedNodes.get(node).callbacks.push(cb);\n } else {\n observedNodes.set(node, {\n rect: undefined,\n hasRectChanged: false,\n callbacks: [cb]\n });\n }\n\n if (wasEmpty) run();\n },\n unobserve: function unobserve() {\n var state = observedNodes.get(node);\n\n if (state) {\n // Remove the callback\n var index = state.callbacks.indexOf(cb);\n if (index >= 0) state.callbacks.splice(index, 1); // Remove the node reference\n\n if (!state.callbacks.length) observedNodes[\"delete\"](node); // Stop the loop\n\n if (!observedNodes.size) cancelAnimationFrame(rafId);\n }\n }\n };\n}\n\nexport default observeRect;\n//# sourceMappingURL=observe-rect.esm.js.map\n","export type NoInfer<A extends any> = [A][A extends any ? 0 : never]\n\nexport type PartialKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>\n\nexport function memo<TDeps extends readonly any[], TResult>(\n getDeps: () => [...TDeps],\n fn: (...args: NoInfer<[...TDeps]>) => TResult,\n opts: {\n key: any\n debug?: () => any\n onChange?: (result: TResult) => void\n },\n): () => TResult {\n let deps: any[] = []\n let result: TResult | undefined\n\n return () => {\n let depTime: number\n if (opts.key && opts.debug?.()) depTime = Date.now()\n\n const newDeps = getDeps()\n\n const depsChanged =\n newDeps.length !== deps.length ||\n newDeps.some((dep: any, index: number) => deps[index] !== dep)\n\n if (!depsChanged) {\n return result!\n }\n\n deps = newDeps\n\n let resultTime: number\n if (opts.key && opts.debug?.()) resultTime = Date.now()\n\n result = fn(...newDeps)\n opts?.onChange?.(result)\n\n if (opts.key && opts.debug?.()) {\n const depEndTime = Math.round((Date.now() - depTime!) * 100) / 100\n const resultEndTime = Math.round((Date.now() - resultTime!) * 100) / 100\n const resultFpsPercentage = resultEndTime / 16\n\n const pad = (str: number | string, num: number) => {\n str = String(str)\n while (str.length < num) {\n str = ' ' + str\n }\n return str\n }\n\n console.info(\n `%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`,\n `\n font-size: .6rem;\n font-weight: bold;\n color: hsl(${Math.max(\n 0,\n Math.min(120 - 120 * resultFpsPercentage, 120),\n )}deg 100% 31%);`,\n opts?.key,\n )\n }\n\n return result!\n }\n}\n","import observeRect from '@reach/observe-rect'\nimport { check } from 'prettier'\nimport React from 'react'\nimport { memo } from './utils'\n\nexport * from './utils'\n\n//\n\ntype ScrollAlignment = 'start' | 'center' | 'end' | 'auto'\n\ninterface ScrollToOptions {\n align: ScrollAlignment\n}\n\ntype ScrollToOffsetOptions = ScrollToOptions\n\ntype ScrollToIndexOptions = ScrollToOptions\n\nexport interface Range {\n startIndex: number\n endIndex: number\n overscan: number\n count: number\n}\n\ntype Key = number | string\n\ninterface Item {\n key: Key\n index: number\n start: number\n end: number\n size: number\n}\n\ninterface Rect {\n width: number\n height: number\n}\n\nexport interface VirtualItem<TItemElement> extends Item {\n measureElement: (el: TItemElement | null) => void\n}\n\n//\n\nexport const defaultKeyExtractor = (index: number) => index\n\nexport const defaultRangeExtractor = (range: Range) => {\n const start = Math.max(range.startIndex - range.overscan, 0)\n const end = Math.min(range.endIndex + range.overscan, range.count - 1)\n\n const arr = []\n\n for (let i = start; i <= end; i++) {\n arr.push(i)\n }\n\n return arr\n}\n\nexport const observeElementRect = (\n instance: Virtualizer<any, any>,\n cb: (rect: Rect) => void,\n) => {\n const observer = observeRect(instance.scrollElement as Element, (rect) => {\n cb(rect)\n })\n\n if (!instance.scrollElement) {\n return\n }\n\n cb(instance.scrollElement.getBoundingClientRect())\n\n observer.observe()\n\n return () => {\n observer.unobserve()\n }\n}\n\nexport const observeWindowRect = (\n instance: Virtualizer<any, any>,\n cb: (rect: Rect) => void,\n) => {\n const onResize = () => {\n cb({\n width: instance.scrollElement.innerWidth,\n height: instance.scrollElement.innerHeight,\n })\n }\n\n if (!instance.scrollElement) {\n return\n }\n\n onResize()\n\n instance.scrollElement.addEventListener('resize', onResize, {\n capture: false,\n passive: true,\n })\n\n return () => {\n instance.scrollElement.removeEventListener('resize', onResize)\n }\n}\n\nexport const observeElementOffset = (\n instance: Virtualizer<any, any>,\n cb: (offset: number) => void,\n) => {\n const onScroll = () =>\n cb(\n instance.scrollElement[\n instance.options.horizontal ? 'scrollLeft' : 'scrollTop'\n ],\n )\n\n if (!instance.scrollElement) {\n return\n }\n\n onScroll()\n\n instance.scrollElement.addEventListener('scroll', onScroll, {\n capture: false,\n passive: true,\n })\n\n return () => {\n instance.scrollElement.removeEventListener('scroll', onScroll)\n }\n}\n\nexport const observeWindowOffset = (\n instance: Virtualizer<any, any>,\n cb: (offset: number) => void,\n) => {\n const onScroll = () =>\n cb(\n instance.scrollElement[\n instance.options.horizontal ? 'scrollX' : 'scrollY'\n ],\n )\n\n if (!instance.scrollElement) {\n return\n }\n\n onScroll()\n\n instance.scrollElement.addEventListener('scroll', onScroll, {\n capture: false,\n passive: true,\n })\n\n return () => {\n instance.scrollElement.removeEventListener('scroll', onScroll)\n }\n}\n\nexport const measureElement = (\n element: unknown,\n instance: Virtualizer<any, any>,\n) => {\n return (element as Element).getBoundingClientRect()[\n instance.options.horizontal ? 'width' : 'height'\n ]\n}\n\nexport const windowScroll = (\n offset: number,\n canSmooth: boolean,\n instance: Virtualizer<any, any>,\n) => {\n ;(instance.scrollElement as Window)?.scrollTo({\n [instance.options.horizontal ? 'left' : 'top']: offset,\n behavior: canSmooth ? 'smooth' : undefined,\n })\n}\n\nexport const elementScroll = (\n offset: number,\n canSmooth: boolean,\n instance: Virtualizer<any, any>,\n) => {\n ;(instance.scrollElement as Element)?.scrollTo({\n [instance.options.horizontal ? 'left' : 'top']: offset,\n behavior: canSmooth ? 'smooth' : undefined,\n })\n}\n\nexport interface VirtualizerOptions<\n TScrollElement = unknown,\n TItemElement = unknown,\n> {\n // Required from the user\n count: number\n getScrollElement: () => TScrollElement\n estimateSize: (index: number) => number\n\n // Required from the framework adapter (but can be overridden)\n scrollToFn: (\n offset: number,\n canSmooth: boolean,\n instance: Virtualizer<TScrollElement, TItemElement>,\n ) => void\n observeElementRect: (\n instance: Virtualizer<TScrollElement, TItemElement>,\n cb: (rect: Rect) => void,\n ) => void | (() => void)\n observeElementOffset: (\n instance: Virtualizer<TScrollElement, TItemElement>,\n cb: (offset: number) => void,\n ) => void | (() => void)\n\n // Optional\n debug?: any\n initialRect?: Rect\n onChange?: (instance: Virtualizer<TScrollElement, TItemElement>) => void\n measureElement?: (\n el: TItemElement,\n instance: Virtualizer<TScrollElement, TItemElement>,\n ) => number\n overscan?: number\n horizontal?: boolean\n paddingStart?: number\n paddingEnd?: number\n initialOffset?: number\n getItemKey?: (index: number) => Key\n rangeExtractor?: (range: Range) => number[]\n enableSmoothScroll?: boolean\n}\n\nexport class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {\n private unsubs: (void | (() => void))[] = []\n options!: Required<VirtualizerOptions<TScrollElement, TItemElement>>\n scrollElement: TScrollElement | null = null\n private measurementsCache: Item[] = []\n private itemMeasurementsCache: Record<Key, number> = {}\n private pendingMeasuredCacheIndexes: number[] = []\n private scrollRect: Rect\n private scrollOffset: number\n private destinationOffset: undefined | number\n private scrollCheckFrame!: ReturnType<typeof setTimeout>\n\n constructor(opts: VirtualizerOptions<TScrollElement, TItemElement>) {\n this.setOptions(opts)\n this.scrollRect = this.options.initialRect\n this.scrollOffset = this.options.initialOffset\n }\n\n setOptions = (opts: VirtualizerOptions<TScrollElement, TItemElement>) => {\n Object.entries(opts).forEach(([key, value]) => {\n if (typeof value === 'undefined') delete (opts as any)[key]\n })\n\n this.options = {\n debug: false,\n initialOffset: 0,\n overscan: 1,\n paddingStart: 0,\n paddingEnd: 0,\n horizontal: false,\n getItemKey: defaultKeyExtractor,\n rangeExtractor: defaultRangeExtractor,\n enableSmoothScroll: false,\n onChange: () => {},\n measureElement,\n initialRect: { width: 0, height: 0 },\n ...opts,\n }\n }\n\n private notify = () => {\n this.options.onChange?.(this)\n }\n\n private cleanup = () => {\n this.unsubs.filter(Boolean).forEach((d) => d!())\n this.unsubs = []\n }\n\n _didMount = () => {\n return () => {\n this.cleanup()\n }\n }\n\n _willUpdate = () => {\n const scrollElement = this.options.getScrollElement()\n\n if (this.scrollElement !== scrollElement) {\n this.cleanup()\n\n this.scrollElement = scrollElement\n\n this.unsubs.push(\n this.options.observeElementRect(this, (rect) => {\n this.scrollRect = rect\n this.notify()\n }),\n )\n\n this.unsubs.push(\n this.options.observeElementOffset(this, (offset) => {\n this.scrollOffset = offset\n this.notify()\n }),\n )\n }\n }\n\n private getSize = () => {\n return this.scrollRect[this.options.horizontal ? 'width' : 'height']\n }\n\n private getMeasurements = memo(\n () => [\n this.options.count,\n this.options.paddingStart,\n this.options.getItemKey,\n this.itemMeasurementsCache,\n ],\n (count, paddingStart, getItemKey, measurementsCache) => {\n const min =\n this.pendingMeasuredCacheIndexes.length > 0\n ? Math.min(...this.pendingMeasuredCacheIndexes)\n : 0\n this.pendingMeasuredCacheIndexes = []\n\n const measurements = this.measurementsCache.slice(0, min)\n\n for (let i = min; i < count; i++) {\n const key = getItemKey(i)\n const measuredSize = measurementsCache[key]\n const start = measurements[i - 1]\n ? measurements[i - 1]!.end\n : paddingStart\n const size =\n typeof measuredSize === 'number'\n ? measuredSize\n : this.options.estimateSize(i)\n const end = start + size\n measurements[i] = { index: i, start, size, end, key }\n }\n\n this.measurementsCache = measurements\n return measurements\n },\n {\n key: process.env.NODE_ENV === 'development' && 'getMeasurements',\n debug: () => this.options.debug,\n },\n )\n\n private calculateRange = memo(\n () => [this.getMeasurements(), this.getSize(), this.scrollOffset],\n (measurements, outerSize, scrollOffset) => {\n return calculateRange({\n measurements,\n outerSize,\n scrollOffset,\n })\n },\n {\n key: process.env.NODE_ENV === 'development' && 'calculateRange',\n debug: () => this.options.debug,\n },\n )\n\n private getIndexes = memo(\n () => [\n this.options.rangeExtractor,\n this.calculateRange(),\n this.options.overscan,\n this.options.count,\n ],\n (rangeExtractor, range, overscan, count) => {\n return rangeExtractor({\n ...range,\n overscan,\n count: count,\n })\n },\n {\n key: process.env.NODE_ENV === 'development' && 'getIndexes',\n },\n )\n\n getVirtualItems = memo(\n () => [\n this.getIndexes(),\n this.getMeasurements(),\n this.options.measureElement,\n ],\n (indexes, measurements, measureElement) => {\n const virtualItems: VirtualItem<TItemElement>[] = []\n\n for (let k = 0, len = indexes.length; k < len; k++) {\n const i = indexes[k]!\n const measurement = measurements[i]!\n\n const item = {\n ...measurement,\n measureElement: (measurableItem: TItemElement | null) => {\n if (measurableItem) {\n const measuredItemSize = measureElement(measurableItem, this)\n\n if (measuredItemSize !== item.size) {\n if (item.start < this.scrollOffset) {\n if (\n process.env.NODE_ENV === 'development' &&\n this.options.debug\n )\n console.info('correction', measuredItemSize - item.size)\n\n if (!this.destinationOffset) {\n this._scrollToOffset(\n this.scrollOffset + (measuredItemSize - item.size),\n false,\n )\n }\n }\n\n this.pendingMeasuredCacheIndexes.push(i)\n this.itemMeasurementsCache = {\n ...this.itemMeasurementsCache,\n [item.key]: measuredItemSize,\n }\n this.notify()\n }\n }\n },\n }\n\n virtualItems.push(item)\n }\n\n return virtualItems\n },\n {\n key: process.env.NODE_ENV === 'development' && 'getIndexes',\n },\n )\n\n scrollToOffset = (\n toOffset: number,\n { align }: ScrollToOffsetOptions = { align: 'start' },\n ) => {\n const attempt = () => {\n const offset = this.scrollOffset\n const size = this.getSize()\n\n if (align === 'auto') {\n if (toOffset <= offset) {\n align = 'start'\n } else if (toOffset >= offset + size) {\n align = 'end'\n } else {\n align = 'start'\n }\n }\n\n if (align === 'start') {\n this._scrollToOffset(toOffset, true)\n } else if (align === 'end') {\n this._scrollToOffset(toOffset - size, true)\n } else if (align === 'center') {\n this._scrollToOffset(toOffset - size / 2, true)\n }\n }\n\n attempt()\n requestAnimationFrame(() => {\n attempt()\n })\n }\n\n scrollToIndex = (\n index: number,\n { align, ...rest }: ScrollToIndexOptions = { align: 'auto' },\n ) => {\n const measurements = this.getMeasurements()\n const offset = this.scrollOffset\n const size = this.getSize()\n const { count } = this.options\n\n const measurement = measurements[Math.max(0, Math.min(index, count - 1))]\n\n if (!measurement) {\n return\n }\n\n if (align === 'auto') {\n if (measurement.end >= offset + size) {\n align = 'end'\n } else if (measurement.start <= offset) {\n align = 'start'\n } else {\n return\n }\n }\n\n const toOffset =\n align === 'center'\n ? measurement.start + measurement.size / 2\n : align === 'end'\n ? measurement.end\n : measurement.start\n\n this.scrollToOffset(toOffset, { align, ...rest })\n }\n\n getTotalSize = () =>\n (this.getMeasurements()[this.options.count - 1]?.end ||\n this.options.paddingStart) + this.options.paddingEnd\n\n private _scrollToOffset = (offset: number, canSmooth: boolean) => {\n clearTimeout(this.scrollCheckFrame)\n\n this.destinationOffset = offset\n this.options.scrollToFn(offset, canSmooth, this)\n\n let scrollCheckFrame: ReturnType<typeof setTimeout>\n\n const check = () => {\n let lastOffset = this.scrollOffset\n this.scrollCheckFrame = scrollCheckFrame = setTimeout(() => {\n if (this.scrollCheckFrame !== scrollCheckFrame) {\n return\n }\n\n if (this.scrollOffset === lastOffset) {\n this.destinationOffset = undefined\n return\n }\n lastOffset = this.scrollOffset\n check()\n }, 100)\n }\n\n check()\n }\n\n measure = () => {\n this.itemMeasurementsCache = {}\n this.notify()\n }\n}\n\nconst findNearestBinarySearch = (\n low: number,\n high: number,\n getCurrentValue: (i: number) => number,\n value: number,\n) => {\n while (low <= high) {\n const middle = ((low + high) / 2) | 0\n const currentValue = getCurrentValue(middle)\n\n if (currentValue < value) {\n low = middle + 1\n } else if (currentValue > value) {\n high = middle - 1\n } else {\n return middle\n }\n }\n\n if (low > 0) {\n return low - 1\n } else {\n return 0\n }\n}\n\nfunction calculateRange({\n measurements,\n outerSize,\n scrollOffset,\n}: {\n measurements: Item[]\n outerSize: number\n scrollOffset: number\n}) {\n const count = measurements.length - 1\n const getOffset = (index: number) => measurements[index]!.start\n\n const startIndex = findNearestBinarySearch(0, count, getOffset, scrollOffset)\n let endIndex = startIndex\n\n while (\n endIndex < count &&\n measurements[endIndex]!.end < scrollOffset + outerSize\n ) {\n endIndex++\n }\n\n return { startIndex, endIndex }\n}\n"],"names":["rafId","props","observedNodes","Map","run","changedStates","forEach","state","node","a","b","newRect","getBoundingClientRect","rect","some","prop","push","callbacks","cb","window","requestAnimationFrame","memo","getDeps","fn","opts","result","deps","depTime","key","debug","Date","now","newDeps","length","dep","index","resultTime","onChange","depEndTime","Math","round","resultEndTime","resultFpsPercentage","pad","str","num","String","console","info","max","min","defaultKeyExtractor","defaultRangeExtractor","range","start","startIndex","overscan","end","endIndex","count","arr","i","measureElement","element","instance","options","horizontal","constructor","_this","this","unsubs","scrollElement","measurementsCache","itemMeasurementsCache","pendingMeasuredCacheIndexes","setOptions","Object","entries","_ref","value","initialOffset","paddingStart","paddingEnd","getItemKey","rangeExtractor","enableSmoothScroll","initialRect","width","height","notify","_this$options$onChang","_this$options","call","cleanup","filter","Boolean","d","_didMount","_willUpdate","getScrollElement","observeElementRect","scrollRect","observeElementOffset","offset","scrollOffset","getSize","getMeasurements","measurements","slice","measuredSize","size","estimateSize","process","calculateRange","outerSize","_ref2","low","high","getCurrentValue","middle","currentValue","findNearestBinarySearch","getIndexes","getVirtualItems","indexes","virtualItems","k","len","item","measurableItem","measuredItemSize","destinationOffset","_scrollToOffset","scrollToOffset","toOffset","_temp","align","attempt","scrollToIndex","_temp2","rest","measurement","getTotalSize","_this$getMeasurements","canSmooth","scrollCheckFrame","clearTimeout","scrollToFn","check","lastOffset","setTimeout","undefined","measure","_instance$scrollEleme2","scrollTo","behavior","onScroll","addEventListener","capture","passive","removeEventListener","observer","observe","wasEmpty","has","get","set","hasRectChanged","unobserve","indexOf","splice","cancelAnimationFrame","observeRect","onResize","innerWidth","innerHeight","_instance$scrollEleme"],"mappings":"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"}
1
+ {"version":3,"file":"index.production.js","sources":["../../../../node_modules/@reach/observe-rect/dist/observe-rect.esm.js","../../src/utils.ts","../../src/index.ts"],"sourcesContent":["var props = [\"bottom\", \"height\", \"left\", \"right\", \"top\", \"width\"];\n\nvar rectChanged = function rectChanged(a, b) {\n if (a === void 0) {\n a = {};\n }\n\n if (b === void 0) {\n b = {};\n }\n\n return props.some(function (prop) {\n return a[prop] !== b[prop];\n });\n};\n\nvar observedNodes = /*#__PURE__*/new Map();\nvar rafId;\n\nvar run = function run() {\n var changedStates = [];\n observedNodes.forEach(function (state, node) {\n var newRect = node.getBoundingClientRect();\n\n if (rectChanged(newRect, state.rect)) {\n state.rect = newRect;\n changedStates.push(state);\n }\n });\n changedStates.forEach(function (state) {\n state.callbacks.forEach(function (cb) {\n return cb(state.rect);\n });\n });\n rafId = window.requestAnimationFrame(run);\n};\n\nfunction observeRect(node, cb) {\n return {\n observe: function observe() {\n var wasEmpty = observedNodes.size === 0;\n\n if (observedNodes.has(node)) {\n observedNodes.get(node).callbacks.push(cb);\n } else {\n observedNodes.set(node, {\n rect: undefined,\n hasRectChanged: false,\n callbacks: [cb]\n });\n }\n\n if (wasEmpty) run();\n },\n unobserve: function unobserve() {\n var state = observedNodes.get(node);\n\n if (state) {\n // Remove the callback\n var index = state.callbacks.indexOf(cb);\n if (index >= 0) state.callbacks.splice(index, 1); // Remove the node reference\n\n if (!state.callbacks.length) observedNodes[\"delete\"](node); // Stop the loop\n\n if (!observedNodes.size) cancelAnimationFrame(rafId);\n }\n }\n };\n}\n\nexport default observeRect;\n//# sourceMappingURL=observe-rect.esm.js.map\n","export type NoInfer<A extends any> = [A][A extends any ? 0 : never]\n\nexport type PartialKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>\n\nexport function memo<TDeps extends readonly any[], TResult>(\n getDeps: () => [...TDeps],\n fn: (...args: NoInfer<[...TDeps]>) => TResult,\n opts: {\n key: any\n debug?: () => any\n onChange?: (result: TResult) => void\n },\n): () => TResult {\n let deps: any[] = []\n let result: TResult | undefined\n\n return () => {\n let depTime: number\n if (opts.key && opts.debug?.()) depTime = Date.now()\n\n const newDeps = getDeps()\n\n const depsChanged =\n newDeps.length !== deps.length ||\n newDeps.some((dep: any, index: number) => deps[index] !== dep)\n\n if (!depsChanged) {\n return result!\n }\n\n deps = newDeps\n\n let resultTime: number\n if (opts.key && opts.debug?.()) resultTime = Date.now()\n\n result = fn(...newDeps)\n opts?.onChange?.(result)\n\n if (opts.key && opts.debug?.()) {\n const depEndTime = Math.round((Date.now() - depTime!) * 100) / 100\n const resultEndTime = Math.round((Date.now() - resultTime!) * 100) / 100\n const resultFpsPercentage = resultEndTime / 16\n\n const pad = (str: number | string, num: number) => {\n str = String(str)\n while (str.length < num) {\n str = ' ' + str\n }\n return str\n }\n\n console.info(\n `%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`,\n `\n font-size: .6rem;\n font-weight: bold;\n color: hsl(${Math.max(\n 0,\n Math.min(120 - 120 * resultFpsPercentage, 120),\n )}deg 100% 31%);`,\n opts?.key,\n )\n }\n\n return result!\n }\n}\n","import observeRect from '@reach/observe-rect'\nimport { memo } from './utils'\n\nexport * from './utils'\n\n//\n\ntype ScrollAlignment = 'start' | 'center' | 'end' | 'auto'\n\nexport interface ScrollToOptions {\n align: ScrollAlignment\n}\n\ntype ScrollToOffsetOptions = ScrollToOptions\n\ntype ScrollToIndexOptions = ScrollToOptions\n\nexport interface Range {\n startIndex: number\n endIndex: number\n overscan: number\n count: number\n}\n\ntype Key = number | string\n\ninterface Item {\n key: Key\n index: number\n start: number\n end: number\n size: number\n}\n\ninterface Rect {\n width: number\n height: number\n}\n\nexport interface VirtualItem<TItemElement> extends Item {\n measureElement: (el: TItemElement | null) => void\n}\n\n//\n\nexport const defaultKeyExtractor = (index: number) => index\n\nexport const defaultRangeExtractor = (range: Range) => {\n const start = Math.max(range.startIndex - range.overscan, 0)\n const end = Math.min(range.endIndex + range.overscan, range.count - 1)\n\n const arr = []\n\n for (let i = start; i <= end; i++) {\n arr.push(i)\n }\n\n return arr\n}\n\nconst memoRectCallback = (\n instance: Virtualizer<any, any>,\n cb: (rect: Rect) => void,\n) => {\n let prev: Rect = { height: -1, width: -1 }\n\n return (rect: Rect) => {\n if (\n instance.options.horizontal\n ? rect.width !== prev.width\n : rect.height !== prev.height\n ) {\n cb(rect)\n }\n\n prev = rect\n }\n}\n\nexport const observeElementRect = (\n instance: Virtualizer<any, any>,\n cb: (rect: Rect) => void,\n) => {\n const onResize = memoRectCallback(instance, cb)\n\n const observer = observeRect(instance.scrollElement as Element, (rect) => {\n onResize(rect)\n })\n\n if (!instance.scrollElement) {\n return\n }\n\n onResize(instance.scrollElement.getBoundingClientRect())\n\n observer.observe()\n\n return () => {\n observer.unobserve()\n }\n}\n\nexport const observeWindowRect = (\n instance: Virtualizer<any, any>,\n cb: (rect: Rect) => void,\n) => {\n const memoizedCallback = memoRectCallback(instance, cb)\n const onResize = () =>\n memoizedCallback({\n width: instance.scrollElement.innerWidth,\n height: instance.scrollElement.innerHeight,\n })\n\n if (!instance.scrollElement) {\n return\n }\n\n onResize()\n\n instance.scrollElement.addEventListener('resize', onResize, {\n capture: false,\n passive: true,\n })\n\n return () => {\n instance.scrollElement.removeEventListener('resize', onResize)\n }\n}\n\ntype ObserverMode = 'element' | 'window'\n\nconst scrollProps = {\n element: ['scrollLeft', 'scrollTop'],\n window: ['scrollX', 'scrollY'],\n} as const\n\nconst createOffsetObserver = (mode: ObserverMode) => {\n return (instance: Virtualizer<any, any>, cb: (offset: number) => void) => {\n if (!instance.scrollElement) {\n return\n }\n\n const propX = scrollProps[mode][0]\n const propY = scrollProps[mode][1]\n\n let prevX: number = instance.scrollElement[propX]\n let prevY: number = instance.scrollElement[propY]\n\n const scroll = () => {\n cb(instance.scrollElement[instance.options.horizontal ? propX : propY])\n }\n\n scroll()\n\n const onScroll = (e: Event) => {\n const target = e.currentTarget as HTMLElement & Window\n const scrollX = target[propX]\n const scrollY = target[propY]\n\n if (instance.options.horizontal ? prevX - scrollX : prevY - scrollY) {\n scroll()\n }\n\n prevX = scrollX\n prevY = scrollY\n }\n\n instance.scrollElement.addEventListener('scroll', onScroll, {\n capture: false,\n passive: true,\n })\n\n return () => {\n instance.scrollElement.removeEventListener('scroll', onScroll)\n }\n }\n}\n\nexport const observeElementOffset = createOffsetObserver('element')\nexport const observeWindowOffset = createOffsetObserver('window')\n\nexport const measureElement = (\n element: unknown,\n instance: Virtualizer<any, any>,\n) => {\n return (element as Element).getBoundingClientRect()[\n instance.options.horizontal ? 'width' : 'height'\n ]\n}\n\nexport const windowScroll = (\n offset: number,\n canSmooth: boolean,\n instance: Virtualizer<any, any>,\n) => {\n ;(instance.scrollElement as Window)?.scrollTo({\n [instance.options.horizontal ? 'left' : 'top']: offset,\n behavior: canSmooth ? 'smooth' : undefined,\n })\n}\n\nexport const elementScroll = (\n offset: number,\n canSmooth: boolean,\n instance: Virtualizer<any, any>,\n) => {\n ;(instance.scrollElement as Element)?.scrollTo({\n [instance.options.horizontal ? 'left' : 'top']: offset,\n behavior: canSmooth ? 'smooth' : undefined,\n })\n}\n\nexport interface VirtualizerOptions<\n TScrollElement = unknown,\n TItemElement = unknown,\n> {\n // Required from the user\n count: number\n getScrollElement: () => TScrollElement\n estimateSize: (index: number) => number\n\n // Required from the framework adapter (but can be overridden)\n scrollToFn: (\n offset: number,\n canSmooth: boolean,\n instance: Virtualizer<TScrollElement, TItemElement>,\n ) => void\n observeElementRect: (\n instance: Virtualizer<TScrollElement, TItemElement>,\n cb: (rect: Rect) => void,\n ) => void | (() => void)\n observeElementOffset: (\n instance: Virtualizer<TScrollElement, TItemElement>,\n cb: (offset: number) => void,\n ) => void | (() => void)\n\n // Optional\n debug?: any\n initialRect?: Rect\n onChange?: (instance: Virtualizer<TScrollElement, TItemElement>) => void\n measureElement?: (\n el: TItemElement,\n instance: Virtualizer<TScrollElement, TItemElement>,\n ) => number\n overscan?: number\n horizontal?: boolean\n paddingStart?: number\n paddingEnd?: number\n scrollPaddingStart?: number\n scrollPaddingEnd?: number\n initialOffset?: number\n getItemKey?: (index: number) => Key\n rangeExtractor?: (range: Range) => number[]\n enableSmoothScroll?: boolean\n}\n\nexport class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {\n private unsubs: (void | (() => void))[] = []\n options!: Required<VirtualizerOptions<TScrollElement, TItemElement>>\n scrollElement: TScrollElement | null = null\n private measurementsCache: Item[] = []\n private itemMeasurementsCache: Record<Key, number> = {}\n private pendingMeasuredCacheIndexes: number[] = []\n private scrollRect: Rect\n private scrollOffset: number\n private destinationOffset: undefined | number\n private scrollCheckFrame!: ReturnType<typeof setTimeout>\n private measureElementCache: Record<\n number,\n (measurableItem: TItemElement | null) => void\n > = {}\n\n constructor(opts: VirtualizerOptions<TScrollElement, TItemElement>) {\n this.setOptions(opts)\n this.scrollRect = this.options.initialRect\n this.scrollOffset = this.options.initialOffset\n }\n\n setOptions = (opts: VirtualizerOptions<TScrollElement, TItemElement>) => {\n Object.entries(opts).forEach(([key, value]) => {\n if (typeof value === 'undefined') delete (opts as any)[key]\n })\n\n this.options = {\n debug: false,\n initialOffset: 0,\n overscan: 1,\n paddingStart: 0,\n paddingEnd: 0,\n scrollPaddingStart: 0,\n scrollPaddingEnd: 0,\n horizontal: false,\n getItemKey: defaultKeyExtractor,\n rangeExtractor: defaultRangeExtractor,\n enableSmoothScroll: true,\n onChange: () => {},\n measureElement,\n initialRect: { width: 0, height: 0 },\n ...opts,\n }\n }\n\n private notify = () => {\n this.options.onChange?.(this)\n }\n\n private cleanup = () => {\n this.unsubs.filter(Boolean).forEach((d) => d!())\n this.unsubs = []\n this.scrollElement = null\n }\n\n _didMount = () => {\n return () => {\n this.cleanup()\n }\n }\n\n _willUpdate = () => {\n const scrollElement = this.options.getScrollElement()\n\n if (this.scrollElement !== scrollElement) {\n this.cleanup()\n\n this.scrollElement = scrollElement\n\n this.unsubs.push(\n this.options.observeElementRect(this, (rect) => {\n this.scrollRect = rect\n this.notify()\n }),\n )\n\n this.unsubs.push(\n this.options.observeElementOffset(this, (offset) => {\n this.scrollOffset = offset\n this.notify()\n }),\n )\n }\n }\n\n private getSize = () => {\n return this.scrollRect[this.options.horizontal ? 'width' : 'height']\n }\n\n private getMeasurements = memo(\n () => [\n this.options.count,\n this.options.paddingStart,\n this.options.getItemKey,\n this.itemMeasurementsCache,\n ],\n (count, paddingStart, getItemKey, measurementsCache) => {\n const min =\n this.pendingMeasuredCacheIndexes.length > 0\n ? Math.min(...this.pendingMeasuredCacheIndexes)\n : 0\n this.pendingMeasuredCacheIndexes = []\n\n const measurements = this.measurementsCache.slice(0, min)\n\n for (let i = min; i < count; i++) {\n const key = getItemKey(i)\n const measuredSize = measurementsCache[key]\n const start = measurements[i - 1]\n ? measurements[i - 1]!.end\n : paddingStart\n const size =\n typeof measuredSize === 'number'\n ? measuredSize\n : this.options.estimateSize(i)\n const end = start + size\n measurements[i] = { index: i, start, size, end, key }\n }\n\n this.measurementsCache = measurements\n return measurements\n },\n {\n key: process.env.NODE_ENV === 'development' && 'getMeasurements',\n debug: () => this.options.debug,\n },\n )\n\n private calculateRange = memo(\n () => [this.getMeasurements(), this.getSize(), this.scrollOffset],\n (measurements, outerSize, scrollOffset) => {\n return calculateRange({\n measurements,\n outerSize,\n scrollOffset,\n })\n },\n {\n key: process.env.NODE_ENV === 'development' && 'calculateRange',\n debug: () => this.options.debug,\n },\n )\n\n private getIndexes = memo(\n () => [\n this.options.rangeExtractor,\n this.calculateRange(),\n this.options.overscan,\n this.options.count,\n ],\n (rangeExtractor, range, overscan, count) => {\n return rangeExtractor({\n ...range,\n overscan,\n count: count,\n })\n },\n {\n key: process.env.NODE_ENV === 'development' && 'getIndexes',\n },\n )\n\n getVirtualItems = memo(\n () => [\n this.getIndexes(),\n this.getMeasurements(),\n this.options.measureElement,\n ],\n (indexes, measurements, measureElement) => {\n const makeMeasureElement =\n (index: number) => (measurableItem: TItemElement | null) => {\n const item = this.measurementsCache[index]!\n\n if (!measurableItem) {\n return\n }\n\n const measuredItemSize = measureElement(measurableItem, this)\n const itemSize = this.itemMeasurementsCache[item.key] ?? item.size\n\n if (measuredItemSize !== itemSize) {\n if (item.start < this.scrollOffset) {\n if (\n process.env.NODE_ENV === 'development' &&\n this.options.debug\n ) {\n console.info('correction', measuredItemSize - itemSize)\n }\n\n if (!this.destinationOffset) {\n this._scrollToOffset(\n this.scrollOffset + (measuredItemSize - itemSize),\n false,\n )\n }\n }\n\n this.pendingMeasuredCacheIndexes.push(index)\n this.itemMeasurementsCache = {\n ...this.itemMeasurementsCache,\n [item.key]: measuredItemSize,\n }\n this.notify()\n }\n }\n\n const virtualItems: VirtualItem<TItemElement>[] = []\n\n const currentMeasureElements: typeof this.measureElementCache = {}\n\n for (let k = 0, len = indexes.length; k < len; k++) {\n const i = indexes[k]!\n const measurement = measurements[i]!\n\n const item = {\n ...measurement,\n measureElement: (currentMeasureElements[i] =\n this.measureElementCache[i] ?? makeMeasureElement(i)),\n }\n virtualItems.push(item)\n }\n\n this.measureElementCache = currentMeasureElements\n\n return virtualItems\n },\n {\n key: process.env.NODE_ENV === 'development' && 'getIndexes',\n },\n )\n\n scrollToOffset = (\n toOffset: number,\n { align }: ScrollToOffsetOptions = { align: 'start' },\n ) => {\n const attempt = () => {\n const offset = this.scrollOffset\n const size = this.getSize()\n\n if (align === 'auto') {\n if (toOffset <= offset) {\n align = 'start'\n } else if (toOffset >= offset + size) {\n align = 'end'\n } else {\n align = 'start'\n }\n }\n\n if (align === 'start') {\n this._scrollToOffset(toOffset, true)\n } else if (align === 'end') {\n this._scrollToOffset(toOffset - size, true)\n } else if (align === 'center') {\n this._scrollToOffset(toOffset - size / 2, true)\n }\n }\n\n attempt()\n requestAnimationFrame(() => {\n attempt()\n })\n }\n\n scrollToIndex = (\n index: number,\n { align, ...rest }: ScrollToIndexOptions = { align: 'auto' },\n ) => {\n const measurements = this.getMeasurements()\n const offset = this.scrollOffset\n const size = this.getSize()\n const { count } = this.options\n\n const measurement = measurements[Math.max(0, Math.min(index, count - 1))]\n\n if (!measurement) {\n return\n }\n\n if (align === 'auto') {\n if (measurement.end >= offset + size - this.options.scrollPaddingEnd) {\n align = 'end'\n } else if (\n measurement.start <=\n offset + this.options.scrollPaddingStart\n ) {\n align = 'start'\n } else {\n return\n }\n }\n\n const toOffset =\n align === 'end'\n ? measurement.end + this.options.scrollPaddingEnd\n : measurement.start - this.options.scrollPaddingStart\n\n this.scrollToOffset(toOffset, { align, ...rest })\n }\n\n getTotalSize = () =>\n (this.getMeasurements()[this.options.count - 1]?.end ||\n this.options.paddingStart) + this.options.paddingEnd\n\n private _scrollToOffset = (offset: number, canSmooth: boolean) => {\n clearTimeout(this.scrollCheckFrame)\n\n this.destinationOffset = offset\n this.options.scrollToFn(\n offset,\n this.options.enableSmoothScroll && canSmooth,\n this,\n )\n\n let scrollCheckFrame: ReturnType<typeof setTimeout>\n\n const check = () => {\n let lastOffset = this.scrollOffset\n this.scrollCheckFrame = scrollCheckFrame = setTimeout(() => {\n if (this.scrollCheckFrame !== scrollCheckFrame) {\n return\n }\n\n if (this.scrollOffset === lastOffset) {\n this.destinationOffset = undefined\n return\n }\n lastOffset = this.scrollOffset\n check()\n }, 100)\n }\n\n check()\n }\n\n measure = () => {\n this.itemMeasurementsCache = {}\n this.notify()\n }\n}\n\nconst findNearestBinarySearch = (\n low: number,\n high: number,\n getCurrentValue: (i: number) => number,\n value: number,\n) => {\n while (low <= high) {\n const middle = ((low + high) / 2) | 0\n const currentValue = getCurrentValue(middle)\n\n if (currentValue < value) {\n low = middle + 1\n } else if (currentValue > value) {\n high = middle - 1\n } else {\n return middle\n }\n }\n\n if (low > 0) {\n return low - 1\n } else {\n return 0\n }\n}\n\nfunction calculateRange({\n measurements,\n outerSize,\n scrollOffset,\n}: {\n measurements: Item[]\n outerSize: number\n scrollOffset: number\n}) {\n const count = measurements.length - 1\n const getOffset = (index: number) => measurements[index]!.start\n\n const startIndex = findNearestBinarySearch(0, count, getOffset, scrollOffset)\n let endIndex = startIndex\n\n while (\n endIndex < count &&\n measurements[endIndex]!.end < scrollOffset + outerSize\n ) {\n endIndex++\n }\n\n return { startIndex, endIndex }\n}\n"],"names":["rafId","props","observedNodes","Map","run","changedStates","forEach","state","node","a","b","newRect","getBoundingClientRect","rect","some","prop","push","callbacks","cb","window","requestAnimationFrame","memo","getDeps","fn","opts","result","deps","depTime","key","debug","Date","now","newDeps","length","dep","index","resultTime","onChange","depEndTime","Math","round","resultEndTime","resultFpsPercentage","pad","str","num","String","console","info","max","min","defaultKeyExtractor","defaultRangeExtractor","range","start","startIndex","overscan","end","endIndex","count","arr","i","memoRectCallback","instance","prev","height","width","options","horizontal","scrollProps","element","createOffsetObserver","mode","scrollElement","propX","propY","prevX","prevY","scroll","onScroll","e","target","currentTarget","scrollX","scrollY","addEventListener","capture","passive","removeEventListener","observeElementOffset","observeWindowOffset","measureElement","constructor","_this","this","unsubs","measurementsCache","itemMeasurementsCache","pendingMeasuredCacheIndexes","measureElementCache","setOptions","Object","entries","_ref","value","initialOffset","paddingStart","paddingEnd","scrollPaddingStart","scrollPaddingEnd","getItemKey","rangeExtractor","enableSmoothScroll","initialRect","notify","_this$options$onChang","_this$options","call","cleanup","filter","Boolean","d","_didMount","_willUpdate","getScrollElement","observeElementRect","scrollRect","offset","scrollOffset","getSize","getMeasurements","measurements","slice","measuredSize","size","estimateSize","process","calculateRange","outerSize","_ref2","low","high","getCurrentValue","middle","currentValue","findNearestBinarySearch","getIndexes","getVirtualItems","indexes","makeMeasureElement","measurableItem","_this$itemMeasurement","item","measuredItemSize","itemSize","destinationOffset","_scrollToOffset","virtualItems","currentMeasureElements","k","len","_this$measureElementC","scrollToOffset","toOffset","_temp","align","attempt","scrollToIndex","_temp2","rest","measurement","getTotalSize","_this$getMeasurements","canSmooth","scrollCheckFrame","clearTimeout","scrollToFn","check","lastOffset","setTimeout","undefined","measure","_instance$scrollEleme2","scrollTo","behavior","onResize","observer","observe","wasEmpty","has","get","set","hasRectChanged","unobserve","indexOf","splice","cancelAnimationFrame","observeRect","memoizedCallback","innerWidth","innerHeight","_instance$scrollEleme"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tanstack/virtual-core",
3
3
  "author": "Tanner Linsley",
4
- "version": "3.0.0-beta.0",
4
+ "version": "3.0.0-beta.12",
5
5
  "description": "Headless UI for virtualizing scrollable elements in TS/JS + Frameworks",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/tanstack/virtual#readme",
@@ -33,7 +33,7 @@
33
33
  "node": ">=12"
34
34
  },
35
35
  "files": [
36
- "build",
36
+ "build/*",
37
37
  "src"
38
38
  ],
39
39
  "dependencies": {
package/src/index.ts CHANGED
@@ -1,6 +1,4 @@
1
1
  import observeRect from '@reach/observe-rect'
2
- import { check } from 'prettier'
3
- import React from 'react'
4
2
  import { memo } from './utils'
5
3
 
6
4
  export * from './utils'
@@ -9,7 +7,7 @@ export * from './utils'
9
7
 
10
8
  type ScrollAlignment = 'start' | 'center' | 'end' | 'auto'
11
9
 
12
- interface ScrollToOptions {
10
+ export interface ScrollToOptions {
13
11
  align: ScrollAlignment
14
12
  }
15
13
 
@@ -60,19 +58,40 @@ export const defaultRangeExtractor = (range: Range) => {
60
58
  return arr
61
59
  }
62
60
 
61
+ const memoRectCallback = (
62
+ instance: Virtualizer<any, any>,
63
+ cb: (rect: Rect) => void,
64
+ ) => {
65
+ let prev: Rect = { height: -1, width: -1 }
66
+
67
+ return (rect: Rect) => {
68
+ if (
69
+ instance.options.horizontal
70
+ ? rect.width !== prev.width
71
+ : rect.height !== prev.height
72
+ ) {
73
+ cb(rect)
74
+ }
75
+
76
+ prev = rect
77
+ }
78
+ }
79
+
63
80
  export const observeElementRect = (
64
81
  instance: Virtualizer<any, any>,
65
82
  cb: (rect: Rect) => void,
66
83
  ) => {
84
+ const onResize = memoRectCallback(instance, cb)
85
+
67
86
  const observer = observeRect(instance.scrollElement as Element, (rect) => {
68
- cb(rect)
87
+ onResize(rect)
69
88
  })
70
89
 
71
90
  if (!instance.scrollElement) {
72
91
  return
73
92
  }
74
93
 
75
- cb(instance.scrollElement.getBoundingClientRect())
94
+ onResize(instance.scrollElement.getBoundingClientRect())
76
95
 
77
96
  observer.observe()
78
97
 
@@ -85,12 +104,12 @@ export const observeWindowRect = (
85
104
  instance: Virtualizer<any, any>,
86
105
  cb: (rect: Rect) => void,
87
106
  ) => {
88
- const onResize = () => {
89
- cb({
107
+ const memoizedCallback = memoRectCallback(instance, cb)
108
+ const onResize = () =>
109
+ memoizedCallback({
90
110
  width: instance.scrollElement.innerWidth,
91
111
  height: instance.scrollElement.innerHeight,
92
112
  })
93
- }
94
113
 
95
114
  if (!instance.scrollElement) {
96
115
  return
@@ -108,60 +127,58 @@ export const observeWindowRect = (
108
127
  }
109
128
  }
110
129
 
111
- export const observeElementOffset = (
112
- instance: Virtualizer<any, any>,
113
- cb: (offset: number) => void,
114
- ) => {
115
- const onScroll = () =>
116
- cb(
117
- instance.scrollElement[
118
- instance.options.horizontal ? 'scrollLeft' : 'scrollTop'
119
- ],
120
- )
130
+ type ObserverMode = 'element' | 'window'
121
131
 
122
- if (!instance.scrollElement) {
123
- return
124
- }
132
+ const scrollProps = {
133
+ element: ['scrollLeft', 'scrollTop'],
134
+ window: ['scrollX', 'scrollY'],
135
+ } as const
125
136
 
126
- onScroll()
137
+ const createOffsetObserver = (mode: ObserverMode) => {
138
+ return (instance: Virtualizer<any, any>, cb: (offset: number) => void) => {
139
+ if (!instance.scrollElement) {
140
+ return
141
+ }
127
142
 
128
- instance.scrollElement.addEventListener('scroll', onScroll, {
129
- capture: false,
130
- passive: true,
131
- })
143
+ const propX = scrollProps[mode][0]
144
+ const propY = scrollProps[mode][1]
132
145
 
133
- return () => {
134
- instance.scrollElement.removeEventListener('scroll', onScroll)
135
- }
136
- }
146
+ let prevX: number = instance.scrollElement[propX]
147
+ let prevY: number = instance.scrollElement[propY]
137
148
 
138
- export const observeWindowOffset = (
139
- instance: Virtualizer<any, any>,
140
- cb: (offset: number) => void,
141
- ) => {
142
- const onScroll = () =>
143
- cb(
144
- instance.scrollElement[
145
- instance.options.horizontal ? 'scrollX' : 'scrollY'
146
- ],
147
- )
149
+ const scroll = () => {
150
+ cb(instance.scrollElement[instance.options.horizontal ? propX : propY])
151
+ }
148
152
 
149
- if (!instance.scrollElement) {
150
- return
151
- }
153
+ scroll()
152
154
 
153
- onScroll()
155
+ const onScroll = (e: Event) => {
156
+ const target = e.currentTarget as HTMLElement & Window
157
+ const scrollX = target[propX]
158
+ const scrollY = target[propY]
154
159
 
155
- instance.scrollElement.addEventListener('scroll', onScroll, {
156
- capture: false,
157
- passive: true,
158
- })
160
+ if (instance.options.horizontal ? prevX - scrollX : prevY - scrollY) {
161
+ scroll()
162
+ }
159
163
 
160
- return () => {
161
- instance.scrollElement.removeEventListener('scroll', onScroll)
164
+ prevX = scrollX
165
+ prevY = scrollY
166
+ }
167
+
168
+ instance.scrollElement.addEventListener('scroll', onScroll, {
169
+ capture: false,
170
+ passive: true,
171
+ })
172
+
173
+ return () => {
174
+ instance.scrollElement.removeEventListener('scroll', onScroll)
175
+ }
162
176
  }
163
177
  }
164
178
 
179
+ export const observeElementOffset = createOffsetObserver('element')
180
+ export const observeWindowOffset = createOffsetObserver('window')
181
+
165
182
  export const measureElement = (
166
183
  element: unknown,
167
184
  instance: Virtualizer<any, any>,
@@ -229,6 +246,8 @@ export interface VirtualizerOptions<
229
246
  horizontal?: boolean
230
247
  paddingStart?: number
231
248
  paddingEnd?: number
249
+ scrollPaddingStart?: number
250
+ scrollPaddingEnd?: number
232
251
  initialOffset?: number
233
252
  getItemKey?: (index: number) => Key
234
253
  rangeExtractor?: (range: Range) => number[]
@@ -246,6 +265,10 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
246
265
  private scrollOffset: number
247
266
  private destinationOffset: undefined | number
248
267
  private scrollCheckFrame!: ReturnType<typeof setTimeout>
268
+ private measureElementCache: Record<
269
+ number,
270
+ (measurableItem: TItemElement | null) => void
271
+ > = {}
249
272
 
250
273
  constructor(opts: VirtualizerOptions<TScrollElement, TItemElement>) {
251
274
  this.setOptions(opts)
@@ -264,10 +287,12 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
264
287
  overscan: 1,
265
288
  paddingStart: 0,
266
289
  paddingEnd: 0,
290
+ scrollPaddingStart: 0,
291
+ scrollPaddingEnd: 0,
267
292
  horizontal: false,
268
293
  getItemKey: defaultKeyExtractor,
269
294
  rangeExtractor: defaultRangeExtractor,
270
- enableSmoothScroll: false,
295
+ enableSmoothScroll: true,
271
296
  onChange: () => {},
272
297
  measureElement,
273
298
  initialRect: { width: 0, height: 0 },
@@ -282,6 +307,7 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
282
307
  private cleanup = () => {
283
308
  this.unsubs.filter(Boolean).forEach((d) => d!())
284
309
  this.unsubs = []
310
+ this.scrollElement = null
285
311
  }
286
312
 
287
313
  _didMount = () => {
@@ -398,48 +424,61 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
398
424
  this.options.measureElement,
399
425
  ],
400
426
  (indexes, measurements, measureElement) => {
427
+ const makeMeasureElement =
428
+ (index: number) => (measurableItem: TItemElement | null) => {
429
+ const item = this.measurementsCache[index]!
430
+
431
+ if (!measurableItem) {
432
+ return
433
+ }
434
+
435
+ const measuredItemSize = measureElement(measurableItem, this)
436
+ const itemSize = this.itemMeasurementsCache[item.key] ?? item.size
437
+
438
+ if (measuredItemSize !== itemSize) {
439
+ if (item.start < this.scrollOffset) {
440
+ if (
441
+ process.env.NODE_ENV === 'development' &&
442
+ this.options.debug
443
+ ) {
444
+ console.info('correction', measuredItemSize - itemSize)
445
+ }
446
+
447
+ if (!this.destinationOffset) {
448
+ this._scrollToOffset(
449
+ this.scrollOffset + (measuredItemSize - itemSize),
450
+ false,
451
+ )
452
+ }
453
+ }
454
+
455
+ this.pendingMeasuredCacheIndexes.push(index)
456
+ this.itemMeasurementsCache = {
457
+ ...this.itemMeasurementsCache,
458
+ [item.key]: measuredItemSize,
459
+ }
460
+ this.notify()
461
+ }
462
+ }
463
+
401
464
  const virtualItems: VirtualItem<TItemElement>[] = []
402
465
 
466
+ const currentMeasureElements: typeof this.measureElementCache = {}
467
+
403
468
  for (let k = 0, len = indexes.length; k < len; k++) {
404
469
  const i = indexes[k]!
405
470
  const measurement = measurements[i]!
406
471
 
407
472
  const item = {
408
473
  ...measurement,
409
- measureElement: (measurableItem: TItemElement | null) => {
410
- if (measurableItem) {
411
- const measuredItemSize = measureElement(measurableItem, this)
412
-
413
- if (measuredItemSize !== item.size) {
414
- if (item.start < this.scrollOffset) {
415
- if (
416
- process.env.NODE_ENV === 'development' &&
417
- this.options.debug
418
- )
419
- console.info('correction', measuredItemSize - item.size)
420
-
421
- if (!this.destinationOffset) {
422
- this._scrollToOffset(
423
- this.scrollOffset + (measuredItemSize - item.size),
424
- false,
425
- )
426
- }
427
- }
428
-
429
- this.pendingMeasuredCacheIndexes.push(i)
430
- this.itemMeasurementsCache = {
431
- ...this.itemMeasurementsCache,
432
- [item.key]: measuredItemSize,
433
- }
434
- this.notify()
435
- }
436
- }
437
- },
474
+ measureElement: (currentMeasureElements[i] =
475
+ this.measureElementCache[i] ?? makeMeasureElement(i)),
438
476
  }
439
-
440
477
  virtualItems.push(item)
441
478
  }
442
479
 
480
+ this.measureElementCache = currentMeasureElements
481
+
443
482
  return virtualItems
444
483
  },
445
484
  {
@@ -496,9 +535,12 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
496
535
  }
497
536
 
498
537
  if (align === 'auto') {
499
- if (measurement.end >= offset + size) {
538
+ if (measurement.end >= offset + size - this.options.scrollPaddingEnd) {
500
539
  align = 'end'
501
- } else if (measurement.start <= offset) {
540
+ } else if (
541
+ measurement.start <=
542
+ offset + this.options.scrollPaddingStart
543
+ ) {
502
544
  align = 'start'
503
545
  } else {
504
546
  return
@@ -506,11 +548,9 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
506
548
  }
507
549
 
508
550
  const toOffset =
509
- align === 'center'
510
- ? measurement.start + measurement.size / 2
511
- : align === 'end'
512
- ? measurement.end
513
- : measurement.start
551
+ align === 'end'
552
+ ? measurement.end + this.options.scrollPaddingEnd
553
+ : measurement.start - this.options.scrollPaddingStart
514
554
 
515
555
  this.scrollToOffset(toOffset, { align, ...rest })
516
556
  }
@@ -523,7 +563,11 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
523
563
  clearTimeout(this.scrollCheckFrame)
524
564
 
525
565
  this.destinationOffset = offset
526
- this.options.scrollToFn(offset, canSmooth, this)
566
+ this.options.scrollToFn(
567
+ offset,
568
+ this.options.enableSmoothScroll && canSmooth,
569
+ this,
570
+ )
527
571
 
528
572
  let scrollCheckFrame: ReturnType<typeof setTimeout>
529
573
 
@@ -1,49 +0,0 @@
1
- /**
2
- * virtual-core
3
- *
4
- * Copyright (c) TanStack
5
- *
6
- * This source code is licensed under the MIT license found in the
7
- * LICENSE.md file in the root directory of this source tree.
8
- *
9
- * @license MIT
10
- */
11
- 'use strict';
12
-
13
- Object.defineProperty(exports, '__esModule', { value: true });
14
-
15
- function _extends() {
16
- _extends = Object.assign ? Object.assign.bind() : function (target) {
17
- for (var i = 1; i < arguments.length; i++) {
18
- var source = arguments[i];
19
-
20
- for (var key in source) {
21
- if (Object.prototype.hasOwnProperty.call(source, key)) {
22
- target[key] = source[key];
23
- }
24
- }
25
- }
26
-
27
- return target;
28
- };
29
- return _extends.apply(this, arguments);
30
- }
31
-
32
- function _objectWithoutPropertiesLoose(source, excluded) {
33
- if (source == null) return {};
34
- var target = {};
35
- var sourceKeys = Object.keys(source);
36
- var key, i;
37
-
38
- for (i = 0; i < sourceKeys.length; i++) {
39
- key = sourceKeys[i];
40
- if (excluded.indexOf(key) >= 0) continue;
41
- target[key] = source[key];
42
- }
43
-
44
- return target;
45
- }
46
-
47
- exports["extends"] = _extends;
48
- exports.objectWithoutPropertiesLoose = _objectWithoutPropertiesLoose;
49
- //# sourceMappingURL=_rollupPluginBabelHelpers.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"_rollupPluginBabelHelpers.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,7 +0,0 @@
1
- export declare type NoInfer<A extends any> = [A][A extends any ? 0 : never];
2
- export declare type PartialKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
3
- export declare function memo<TDeps extends readonly any[], TResult>(getDeps: () => [...TDeps], fn: (...args: NoInfer<[...TDeps]>) => TResult, opts: {
4
- key: any;
5
- debug?: () => any;
6
- onChange?: (result: TResult) => void;
7
- }): () => TResult;