@vielzeug/scroll 1.0.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dom-virtual-list.cjs +1 -1
- package/dist/dom-virtual-list.cjs.map +1 -1
- package/dist/dom-virtual-list.d.ts +17 -0
- package/dist/dom-virtual-list.d.ts.map +1 -1
- package/dist/dom-virtual-list.js +1 -1
- package/dist/dom-virtual-list.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/scroll.cjs +1 -1
- package/dist/scroll.cjs.map +1 -1
- package/dist/scroll.iife.js +1 -1
- package/dist/scroll.iife.js.map +1 -1
- package/dist/scroll.js +1 -1
- package/dist/scroll.js.map +1 -1
- package/dist/virtualizer.cjs +1 -1
- package/dist/virtualizer.cjs.map +1 -1
- package/dist/virtualizer.d.ts +7 -0
- package/dist/virtualizer.d.ts.map +1 -1
- package/dist/virtualizer.js +1 -1
- package/dist/virtualizer.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./errors.cjs");require("./_utils.cjs");const t=require("./virtualizer.cjs");function n(){let e=new Map,t=new Map,n=[],r=!1;return{acquire(i,a){if(!r)return a();let o=e.get(i);if(o)return t.set(i,o),o;let s=n.pop()??a();return t.set(i,s),s},beginCycle(){t=new Map,r=!0},clear(){for(let t of e.values())t.remove();e.clear(),t.clear(),n.length=0,r=!1},endCycle(){if(r){r=!1;for(let[r,i]of e)t.has(r)||(i.remove(),n.push(i));e=t}}}}function r(r){let i=[],a=!1,o=new AbortController,s=r.listElement,c=n(),l=null;function u(e){let t=i[e];return t!==void 0&&r.getItemKey?r.getItemKey(e,t):e}function d(e){if(r.estimateSize===void 0)return 36;if(typeof r.estimateSize==`number`)return r.estimateSize;let t=i[e];return t===void 0?36:r.estimateSize(e,t)}function f(e){r.horizontal?(s.style.height=``,s.style.width=`${e}px`):(s.style.height=`${e}px`,s.style.width=``)}function p(t){let n=i[t.index];if(n===void 0)throw new e.ScrollRangeError(`toRenderItem: index ${t.index} is out of range (currentItems.length=${i.length})`);return{...t,data:n}}function m(e){f(e.totalSize),c.beginCycle();try{r.render({items:e.items.map(p),listEl:s,recycle:(e,t)=>c.acquire(e,t),stickyItems:e.stickyItems.map(p),totalSize:e.totalSize})}finally{c.endCycle()}}function h(){c.clear(),r.clear?r.clear(s):s.textContent=``,s.style.height=``,s.style.width=``,s.style.position=``,s.style.contain=``}function g(){
|
|
1
|
+
const e=require("./errors.cjs");require("./_utils.cjs");const t=require("./virtualizer.cjs");function n(){let e=new Map,t=new Map,n=[],r=!1;return{acquire(i,a){if(!r)return a();let o=e.get(i);if(o)return t.set(i,o),o;let s=n.pop()??a();return t.set(i,s),s},beginCycle(){t=new Map,r=!0},clear(){for(let t of e.values())t.remove();e.clear(),t.clear(),n.length=0,r=!1},endCycle(){if(r){r=!1;for(let[r,i]of e)t.has(r)||(i.remove(),n.push(i));e=t}}}}function r(r){let i=[],a=!1,o=new AbortController,s=r.listElement,c=n(),l=null;function u(e){let t=i[e];return t!==void 0&&r.getItemKey?r.getItemKey(e,t):e}function d(e){if(r.estimateSize===void 0)return 36;if(typeof r.estimateSize==`number`)return r.estimateSize;let t=i[e];return t===void 0?36:r.estimateSize(e,t)}function f(e){r.horizontal?(s.style.height=``,s.style.width=`${e}px`):(s.style.height=`${e}px`,s.style.width=``)}function p(t){let n=i[t.index];if(n===void 0)throw new e.ScrollRangeError(`toRenderItem: index ${t.index} is out of range (currentItems.length=${i.length})`);return{...t,data:n}}function m(e){f(e.totalSize),c.beginCycle();try{r.render({items:e.items.map(p),listEl:s,recycle:(e,t)=>c.acquire(e,t),stickyItems:e.stickyItems.map(p),totalSize:e.totalSize})}finally{c.endCycle()}}function h(){c.clear(),r.clear?r.clear(s):s.textContent=``,s.style.height=``,s.style.width=``,s.style.position=``,s.style.contain=``}function g(){let e=r.stickToBottom;return e?e===!0?{enabled:!0,threshold:48}:{enabled:e.enabled??!0,threshold:e.threshold??48}:{enabled:!1,threshold:48}}function _(){let e=t.createVirtualizer(r.scrollElement,{count:i.length,estimateSize:d,gap:r.gap,getItemKey:u,horizontal:r.horizontal,measurementCache:r.measurementCache,onChange:m,overscan:r.overscan??3,sticky:r.sticky?e=>{let t=i[e];return t!==void 0&&r.sticky(e,t)}:void 0});return l=e,s.style.position=`relative`,s.style.contain=`layout`,e}function v(){a||(a=!0,o.abort(),l?.dispose(),l=null,h())}return{get count(){return l?.count??0},get disposalSignal(){return o.signal},dispose:v,get disposed(){return a},invalidate(){a||l?.invalidate()},isAtEnd(e){return l?.isAtEnd(e)??!0},get isScrolling(){return l?.isScrolling??!1},get items(){return l?.items??[]},measure(e,t){a||l?.measure(e,t)},measureBatch(e){a||l?.measureBatch(e)},measureEl(e,t){return a?()=>{}:l?.measureEl(e,t)??(()=>{})},refresh(){a||l?.refresh()},get scrollOffset(){return l?.scrollOffset??0},scrollToBottom(e){a||l?.scrollToBottom(e)},scrollToIndex(e,t){a||l?.scrollToIndex(e,t)},scrollToOffset(e,t){a||l?.scrollToOffset(e,t)},scrollToTop(e){a||l?.scrollToTop(e)},setItems(e){if(a)return;let t=g(),n=t.enabled&&(l?.isAtEnd(t.threshold)??!0);if(i=e,e.length===0){l?.dispose(),l=null,h();return}if(!l){let e=_();n&&e.scrollToBottom();return}let o=e.length!==l.count;l.update({count:e.length}),o?r.getItemKey||l.invalidate():r.getItemKey?l.refresh():l.invalidate(),n&&l.scrollToBottom()},get stickyItems(){return l?.stickyItems??[]},[Symbol.dispose]:v,get totalSize(){return l?.totalSize??0}}}function i(e,t){let n=document.createElement(`div`);n.style.cssText=t.horizontal?`overflow: auto hidden; width: 100%; height: 100%;`:`overflow: hidden auto; width: 100%; height: 100%;`,t.containerClass&&(n.className=t.containerClass);let i=document.createElement(`div`);n.appendChild(i),e.appendChild(n);let a;try{a=r({...t,listElement:i,scrollElement:n})}catch(e){throw n.remove(),e}let o=a.dispose.bind(a);return Object.assign(a,{dispose(){o(),n.remove()},[Symbol.dispose](){o(),n.remove()}})}exports.createDomVirtualList=r,exports.createVirtualScroller=i;
|
|
2
2
|
//# sourceMappingURL=dom-virtual-list.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dom-virtual-list.cjs","names":[],"sources":["../src/dom-virtual-list.ts"],"sourcesContent":["import { DEFAULT_ESTIMATE_SIZE, DEFAULT_OVERSCAN, type MeasurementCache, type Overscan } from './_utils';\nimport { ScrollRangeError } from './errors';\nimport {\n createVirtualizer,\n type ScrollToIndexOptions,\n type VirtualItem,\n type Virtualizer,\n type VirtualizerState,\n type VirtualKey,\n} from './virtualizer';\n\nexport type {\n MeasurementCache,\n Overscan,\n ScrollToIndexOptions,\n VirtualItem,\n Virtualizer,\n VirtualizerState,\n VirtualKey,\n};\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\n/** A `VirtualItem` enriched with the corresponding data record. */\nexport type VirtualRenderItem<T> = VirtualItem & { readonly data: T };\n\n/**\n * Recycle a DOM node by key. If the pool has a live node for `key`, it is\n * returned and reused; otherwise `create()` is called to produce a new one.\n */\nexport type RecycleFn = (key: VirtualKey, create: () => HTMLElement) => HTMLElement;\n\nexport type DomVirtualListRenderArgs<T> = {\n items: Array<VirtualRenderItem<T>>;\n listEl: HTMLElement;\n recycle: RecycleFn;\n /** Sticky items from the underlying virtualizer, enriched with data. */\n stickyItems: Array<VirtualRenderItem<T>>;\n totalSize: number;\n};\n\nexport type DomVirtualListOptions<T> = {\n /** Custom teardown that clears listEl. Defaults to `listEl.textContent = ''`. */\n clear?: (listEl: HTMLElement) => void;\n estimateSize?: number | ((index: number, item: T) => number);\n gap?: number;\n getItemKey?: (index: number, item: T) => VirtualKey;\n horizontal?: boolean;\n listElement: HTMLElement;\n /** External measurement cache for scroll restoration. */\n measurementCache?: MeasurementCache;\n overscan?: Overscan;\n render: (args: DomVirtualListRenderArgs<T>) => void;\n scrollElement: HTMLElement | Window;\n /** Mark items as sticky headers. Receives the item index and the item data. */\n sticky?: (index: number, item: T) => boolean;\n};\n\n/**\n * R11: Controller extends Virtualizer so all methods (scrollToIndex, refresh,\n * scrollToOffset, etc.) are accessible directly on the controller without\n * needing to unwrap an inner virtualizer handle.\n *\n * `prepend` and `update` are omitted — use `setItems()` for item updates and\n * there is no direct `prepend` concept in DomVirtualList.\n */\nexport type DomVirtualListController<T> = Omit<Virtualizer, 'prepend' | 'update'> & {\n setItems: (items: T[]) => void;\n};\n\nexport type VirtualScrollerOptions<T> = Omit<DomVirtualListOptions<T>, 'listElement' | 'scrollElement'> & {\n /** Additional CSS class names on the generated scroll container. */\n containerClass?: string;\n};\n\n// ─── Node pool ────────────────────────────────────────────────────────────────\n\nfunction createNodePool() {\n let live = new Map<VirtualKey, HTMLElement>();\n let nextLive = new Map<VirtualKey, HTMLElement>();\n const stale: HTMLElement[] = [];\n let inCycle = false;\n\n return {\n acquire(key: VirtualKey, create: () => HTMLElement): HTMLElement {\n if (!inCycle) return create();\n\n const existing = live.get(key);\n\n if (existing) {\n nextLive.set(key, existing);\n\n return existing;\n }\n\n const node = stale.pop() ?? create();\n\n nextLive.set(key, node);\n\n return node;\n },\n\n beginCycle(): void {\n nextLive = new Map();\n inCycle = true;\n },\n\n clear(): void {\n for (const node of live.values()) node.remove();\n\n live.clear();\n nextLive.clear();\n stale.length = 0;\n inCycle = false;\n },\n\n endCycle(): void {\n if (!inCycle) return;\n\n inCycle = false;\n\n for (const [key, node] of live) {\n if (!nextLive.has(key)) {\n node.remove();\n stale.push(node);\n }\n }\n\n live = nextLive;\n },\n };\n}\n\n// ─── Implementation ────────────────────────────────────────────────────────────\n\nexport function createDomVirtualList<T>(options: DomVirtualListOptions<T>): DomVirtualListController<T> {\n let currentItems: T[] = [];\n let isDestroyed = false;\n const ac = new AbortController();\n const listEl = options.listElement;\n\n // Pool must be declared before virtualizer since handleChange (passed as onChange)\n // is invoked during createVirtualizer initialization via computeVisible.\n const pool = createNodePool();\n\n // Virtualizer is lazily created on the first non-empty setItems call.\n let virtualizer: Virtualizer | null = null;\n\n function resolveKey(index: number): VirtualKey {\n const item = currentItems[index];\n\n if (item !== undefined && options.getItemKey) return options.getItemKey(index, item);\n\n return index;\n }\n\n function resolveEstimate(index: number): number {\n if (options.estimateSize === undefined) return DEFAULT_ESTIMATE_SIZE;\n\n if (typeof options.estimateSize === 'number') return options.estimateSize;\n\n const item = currentItems[index];\n\n return item !== undefined ? options.estimateSize(index, item) : DEFAULT_ESTIMATE_SIZE;\n }\n\n function applyListSize(totalSize: number): void {\n if (options.horizontal) {\n listEl.style.height = '';\n listEl.style.width = `${totalSize}px`;\n } else {\n listEl.style.height = `${totalSize}px`;\n listEl.style.width = '';\n }\n }\n\n /**\n * R10: Throw rather than silently produce `undefined as T`.\n * This catches bugs where `vi.index` is out of range for `currentItems`.\n */\n function toRenderItem(vi: VirtualItem): VirtualRenderItem<T> {\n const data = currentItems[vi.index];\n\n if (data === undefined) {\n throw new ScrollRangeError(\n `toRenderItem: index ${vi.index} is out of range (currentItems.length=${currentItems.length})`,\n );\n }\n\n return { ...vi, data };\n }\n\n function handleChange(state: VirtualizerState): void {\n applyListSize(state.totalSize);\n\n pool.beginCycle();\n\n // R5: try/finally ensures endCycle() runs even if render() throws, keeping\n // the pool in a consistent state.\n try {\n options.render({\n items: state.items.map(toRenderItem),\n listEl,\n recycle: (key, create) => pool.acquire(key, create),\n stickyItems: state.stickyItems.map(toRenderItem),\n totalSize: state.totalSize,\n });\n } finally {\n pool.endCycle();\n }\n }\n\n function clearAndReset(): void {\n pool.clear();\n\n if (options.clear) {\n options.clear(listEl);\n } else {\n listEl.textContent = '';\n }\n\n listEl.style.height = '';\n listEl.style.width = '';\n listEl.style.position = '';\n listEl.style.contain = '';\n }\n\n function spawnVirtualizer(): void {\n virtualizer = createVirtualizer(options.scrollElement, {\n count: currentItems.length,\n estimateSize: resolveEstimate,\n gap: options.gap,\n getItemKey: resolveKey,\n horizontal: options.horizontal,\n measurementCache: options.measurementCache,\n onChange: handleChange,\n overscan: options.overscan ?? DEFAULT_OVERSCAN,\n sticky: options.sticky\n ? (index) => {\n const item = currentItems[index];\n\n return item !== undefined && options.sticky!(index, item);\n }\n : undefined,\n });\n\n listEl.style.position = 'relative';\n listEl.style.contain = 'layout';\n }\n\n function _dispose(): void {\n if (isDestroyed) return;\n\n isDestroyed = true;\n ac.abort();\n virtualizer?.dispose();\n virtualizer = null;\n clearAndReset();\n }\n\n return {\n // ── Virtualizer passthrough (R11) ──────────────────────────────────────\n get count() {\n return virtualizer?.count ?? 0;\n },\n\n get disposalSignal() {\n return ac.signal;\n },\n\n dispose: _dispose,\n\n get disposed() {\n return isDestroyed;\n },\n\n invalidate() {\n if (isDestroyed) return;\n\n virtualizer?.invalidate();\n },\n\n get isScrolling() {\n return virtualizer?.isScrolling ?? false;\n },\n\n get items() {\n return virtualizer?.items ?? [];\n },\n\n measure(index, size) {\n if (isDestroyed) return;\n\n virtualizer?.measure(index, size);\n },\n\n measureBatch(entries) {\n if (isDestroyed) return;\n\n virtualizer?.measureBatch(entries);\n },\n\n measureEl(index, el) {\n if (isDestroyed) return () => {};\n\n return virtualizer?.measureEl(index, el) ?? (() => {});\n },\n\n refresh() {\n if (isDestroyed) return;\n\n virtualizer?.refresh();\n },\n\n get scrollOffset() {\n return virtualizer?.scrollOffset ?? 0;\n },\n\n scrollToBottom(scrollOptions) {\n if (isDestroyed) return;\n\n virtualizer?.scrollToBottom(scrollOptions);\n },\n\n scrollToIndex(index, scrollOptions) {\n if (isDestroyed) return;\n\n virtualizer?.scrollToIndex(index, scrollOptions);\n },\n\n scrollToOffset(offset, scrollOptions) {\n if (isDestroyed) return;\n\n virtualizer?.scrollToOffset(offset, scrollOptions);\n },\n\n scrollToTop(scrollOptions) {\n if (isDestroyed) return;\n\n virtualizer?.scrollToTop(scrollOptions);\n },\n\n // ── DomVirtualList-specific ────────────────────────────────────────────\n setItems(items) {\n if (isDestroyed) return;\n\n currentItems = items;\n\n if (items.length === 0) {\n virtualizer?.dispose();\n virtualizer = null;\n clearAndReset();\n\n return;\n }\n\n if (!virtualizer) {\n spawnVirtualizer();\n\n return;\n }\n\n const countChanged = items.length !== virtualizer.count;\n\n // Only count needs explicit update — estimateSize and getItemKey are\n // closures that already reflect the latest currentItems automatically.\n virtualizer.update({ count: items.length });\n\n // When count changed, update() already triggered rebuild + computeVisible().\n // Only force re-emission when count is unchanged (data changed, count same).\n if (!countChanged) {\n // refresh() re-emits with current sizes for stable keys;\n // invalidate() clears position-based measurements when no stable keys.\n if (options.getItemKey) {\n virtualizer.refresh();\n } else {\n virtualizer.invalidate();\n }\n } else if (!options.getItemKey) {\n // Count changed AND no stable keys: position-based measurements are now\n // stale. Clear them so the next render remeasures from fresh estimates.\n virtualizer.invalidate();\n }\n },\n\n get stickyItems() {\n return virtualizer?.stickyItems ?? [];\n },\n\n [Symbol.dispose]: _dispose,\n\n get totalSize() {\n return virtualizer?.totalSize ?? 0;\n },\n };\n}\n\n// ─── F5: createVirtualScroller ────────────────────────────────────────────────\n\n/**\n * High-level factory that creates the scroll container and inner list element,\n * appends them to `container`, and returns a fully wired `DomVirtualListController`.\n *\n * @example\n * ```ts\n * const list = createVirtualScroller(document.getElementById('root')!, {\n * render({ items, listEl, recycle }) { … },\n * });\n * list.setItems(data);\n * ```\n */\nexport function createVirtualScroller<T>(\n container: HTMLElement,\n options: VirtualScrollerOptions<T>,\n): DomVirtualListController<T> {\n const scrollEl = document.createElement('div');\n\n scrollEl.style.cssText = options.horizontal\n ? 'overflow: auto hidden; width: 100%; height: 100%;'\n : 'overflow: hidden auto; width: 100%; height: 100%;';\n\n if (options.containerClass) scrollEl.className = options.containerClass;\n\n const listEl = document.createElement('div');\n\n scrollEl.appendChild(listEl);\n container.appendChild(scrollEl);\n\n let ctrl: DomVirtualListController<T>;\n\n try {\n ctrl = createDomVirtualList<T>({\n ...options,\n listElement: listEl,\n scrollElement: scrollEl,\n });\n } catch (e) {\n // Remove the scroll container if construction fails so we don't leak DOM nodes.\n scrollEl.remove();\n throw e;\n }\n\n // Override dispose and [Symbol.dispose] to also remove the scroll container.\n // Capture the original dispose before overwriting so there's no self-reference.\n const innerDispose = ctrl.dispose.bind(ctrl);\n\n return Object.assign(ctrl, {\n dispose() {\n innerDispose();\n scrollEl.remove();\n },\n [Symbol.dispose]() {\n innerDispose();\n scrollEl.remove();\n },\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dom-virtual-list.cjs","names":[],"sources":["../src/dom-virtual-list.ts"],"sourcesContent":["import { DEFAULT_ESTIMATE_SIZE, DEFAULT_OVERSCAN, type MeasurementCache, type Overscan } from './_utils';\nimport { ScrollRangeError } from './errors';\nimport {\n createVirtualizer,\n type ScrollToIndexOptions,\n type VirtualItem,\n type Virtualizer,\n type VirtualizerState,\n type VirtualKey,\n} from './virtualizer';\n\nexport type {\n MeasurementCache,\n Overscan,\n ScrollToIndexOptions,\n VirtualItem,\n Virtualizer,\n VirtualizerState,\n VirtualKey,\n};\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\n/** A `VirtualItem` enriched with the corresponding data record. */\nexport type VirtualRenderItem<T> = VirtualItem & { readonly data: T };\n\n/**\n * Recycle a DOM node by key. If the pool has a live node for `key`, it is\n * returned and reused; otherwise `create()` is called to produce a new one.\n */\nexport type RecycleFn = (key: VirtualKey, create: () => HTMLElement) => HTMLElement;\n\nexport type DomVirtualListRenderArgs<T> = {\n items: Array<VirtualRenderItem<T>>;\n listEl: HTMLElement;\n recycle: RecycleFn;\n /** Sticky items from the underlying virtualizer, enriched with data. */\n stickyItems: Array<VirtualRenderItem<T>>;\n totalSize: number;\n};\n\nexport type StickToBottomOptions = {\n /** Enable/disable the behavior. Default: `true` once this object is provided. */\n enabled?: boolean;\n /**\n * Distance in pixels from the end still considered \"at the end\" — the bottom edge in\n * vertical mode, the trailing edge in horizontal mode. Default: `48`.\n */\n threshold?: number;\n};\n\nexport type DomVirtualListOptions<T> = {\n /** Custom teardown that clears listEl. Defaults to `listEl.textContent = ''`. */\n clear?: (listEl: HTMLElement) => void;\n estimateSize?: number | ((index: number, item: T) => number);\n gap?: number;\n getItemKey?: (index: number, item: T) => VirtualKey;\n horizontal?: boolean;\n listElement: HTMLElement;\n /** External measurement cache for scroll restoration. */\n measurementCache?: MeasurementCache;\n overscan?: Overscan;\n render: (args: DomVirtualListRenderArgs<T>) => void;\n scrollElement: HTMLElement | Window;\n /**\n * Auto-scroll to the end after `setItems()` whenever the list was already at (or near) the\n * end just before the update — the chat \"stick to bottom on new message\" pattern. Fires on\n * *any* update while at the end, not just growth, so it also follows a streaming last\n * item that grows in place without changing `items.length`. Does nothing while the user\n * has scrolled away from the end. Pass `true` for defaults, or an options object.\n */\n stickToBottom?: boolean | StickToBottomOptions;\n /** Mark items as sticky headers. Receives the item index and the item data. */\n sticky?: (index: number, item: T) => boolean;\n};\n\n/**\n * R11: Controller extends Virtualizer so all methods (scrollToIndex, refresh,\n * scrollToOffset, etc.) are accessible directly on the controller without\n * needing to unwrap an inner virtualizer handle.\n *\n * `prepend` and `update` are omitted — use `setItems()` for item updates and\n * there is no direct `prepend` concept in DomVirtualList.\n */\nexport type DomVirtualListController<T> = Omit<Virtualizer, 'prepend' | 'update'> & {\n setItems: (items: T[]) => void;\n};\n\nexport type VirtualScrollerOptions<T> = Omit<DomVirtualListOptions<T>, 'listElement' | 'scrollElement'> & {\n /** Additional CSS class names on the generated scroll container. */\n containerClass?: string;\n};\n\n// ─── Node pool ────────────────────────────────────────────────────────────────\n\nfunction createNodePool() {\n let live = new Map<VirtualKey, HTMLElement>();\n let nextLive = new Map<VirtualKey, HTMLElement>();\n const stale: HTMLElement[] = [];\n let inCycle = false;\n\n return {\n acquire(key: VirtualKey, create: () => HTMLElement): HTMLElement {\n if (!inCycle) return create();\n\n const existing = live.get(key);\n\n if (existing) {\n nextLive.set(key, existing);\n\n return existing;\n }\n\n const node = stale.pop() ?? create();\n\n nextLive.set(key, node);\n\n return node;\n },\n\n beginCycle(): void {\n nextLive = new Map();\n inCycle = true;\n },\n\n clear(): void {\n for (const node of live.values()) node.remove();\n\n live.clear();\n nextLive.clear();\n stale.length = 0;\n inCycle = false;\n },\n\n endCycle(): void {\n if (!inCycle) return;\n\n inCycle = false;\n\n for (const [key, node] of live) {\n if (!nextLive.has(key)) {\n node.remove();\n stale.push(node);\n }\n }\n\n live = nextLive;\n },\n };\n}\n\n// ─── Implementation ────────────────────────────────────────────────────────────\n\nexport function createDomVirtualList<T>(options: DomVirtualListOptions<T>): DomVirtualListController<T> {\n let currentItems: T[] = [];\n let isDestroyed = false;\n const ac = new AbortController();\n const listEl = options.listElement;\n\n // Pool must be declared before virtualizer since handleChange (passed as onChange)\n // is invoked during createVirtualizer initialization via computeVisible.\n const pool = createNodePool();\n\n // Virtualizer is lazily created on the first non-empty setItems call.\n let virtualizer: Virtualizer | null = null;\n\n function resolveKey(index: number): VirtualKey {\n const item = currentItems[index];\n\n if (item !== undefined && options.getItemKey) return options.getItemKey(index, item);\n\n return index;\n }\n\n function resolveEstimate(index: number): number {\n if (options.estimateSize === undefined) return DEFAULT_ESTIMATE_SIZE;\n\n if (typeof options.estimateSize === 'number') return options.estimateSize;\n\n const item = currentItems[index];\n\n return item !== undefined ? options.estimateSize(index, item) : DEFAULT_ESTIMATE_SIZE;\n }\n\n function applyListSize(totalSize: number): void {\n if (options.horizontal) {\n listEl.style.height = '';\n listEl.style.width = `${totalSize}px`;\n } else {\n listEl.style.height = `${totalSize}px`;\n listEl.style.width = '';\n }\n }\n\n /**\n * R10: Throw rather than silently produce `undefined as T`.\n * This catches bugs where `vi.index` is out of range for `currentItems`.\n */\n function toRenderItem(vi: VirtualItem): VirtualRenderItem<T> {\n const data = currentItems[vi.index];\n\n if (data === undefined) {\n throw new ScrollRangeError(\n `toRenderItem: index ${vi.index} is out of range (currentItems.length=${currentItems.length})`,\n );\n }\n\n return { ...vi, data };\n }\n\n function handleChange(state: VirtualizerState): void {\n applyListSize(state.totalSize);\n\n pool.beginCycle();\n\n // R5: try/finally ensures endCycle() runs even if render() throws, keeping\n // the pool in a consistent state.\n try {\n options.render({\n items: state.items.map(toRenderItem),\n listEl,\n recycle: (key, create) => pool.acquire(key, create),\n stickyItems: state.stickyItems.map(toRenderItem),\n totalSize: state.totalSize,\n });\n } finally {\n pool.endCycle();\n }\n }\n\n function clearAndReset(): void {\n pool.clear();\n\n if (options.clear) {\n options.clear(listEl);\n } else {\n listEl.textContent = '';\n }\n\n listEl.style.height = '';\n listEl.style.width = '';\n listEl.style.position = '';\n listEl.style.contain = '';\n }\n\n const DEFAULT_STICK_THRESHOLD = 48;\n\n function resolveStickToBottom(): { enabled: boolean; threshold: number } {\n const opt = options.stickToBottom;\n\n if (!opt) return { enabled: false, threshold: DEFAULT_STICK_THRESHOLD };\n\n if (opt === true) return { enabled: true, threshold: DEFAULT_STICK_THRESHOLD };\n\n return { enabled: opt.enabled ?? true, threshold: opt.threshold ?? DEFAULT_STICK_THRESHOLD };\n }\n\n function spawnVirtualizer(): Virtualizer {\n const v = createVirtualizer(options.scrollElement, {\n count: currentItems.length,\n estimateSize: resolveEstimate,\n gap: options.gap,\n getItemKey: resolveKey,\n horizontal: options.horizontal,\n measurementCache: options.measurementCache,\n onChange: handleChange,\n overscan: options.overscan ?? DEFAULT_OVERSCAN,\n sticky: options.sticky\n ? (index) => {\n const item = currentItems[index];\n\n return item !== undefined && options.sticky!(index, item);\n }\n : undefined,\n });\n\n virtualizer = v;\n listEl.style.position = 'relative';\n listEl.style.contain = 'layout';\n\n return v;\n }\n\n function _dispose(): void {\n if (isDestroyed) return;\n\n isDestroyed = true;\n ac.abort();\n virtualizer?.dispose();\n virtualizer = null;\n clearAndReset();\n }\n\n return {\n // ── Virtualizer passthrough (R11) ──────────────────────────────────────\n get count() {\n return virtualizer?.count ?? 0;\n },\n\n get disposalSignal() {\n return ac.signal;\n },\n\n dispose: _dispose,\n\n get disposed() {\n return isDestroyed;\n },\n\n invalidate() {\n if (isDestroyed) return;\n\n virtualizer?.invalidate();\n },\n\n isAtEnd(threshold) {\n return virtualizer?.isAtEnd(threshold) ?? true;\n },\n\n get isScrolling() {\n return virtualizer?.isScrolling ?? false;\n },\n\n get items() {\n return virtualizer?.items ?? [];\n },\n\n measure(index, size) {\n if (isDestroyed) return;\n\n virtualizer?.measure(index, size);\n },\n\n measureBatch(entries) {\n if (isDestroyed) return;\n\n virtualizer?.measureBatch(entries);\n },\n\n measureEl(index, el) {\n if (isDestroyed) return () => {};\n\n return virtualizer?.measureEl(index, el) ?? (() => {});\n },\n\n refresh() {\n if (isDestroyed) return;\n\n virtualizer?.refresh();\n },\n\n get scrollOffset() {\n return virtualizer?.scrollOffset ?? 0;\n },\n\n scrollToBottom(scrollOptions) {\n if (isDestroyed) return;\n\n virtualizer?.scrollToBottom(scrollOptions);\n },\n\n scrollToIndex(index, scrollOptions) {\n if (isDestroyed) return;\n\n virtualizer?.scrollToIndex(index, scrollOptions);\n },\n\n scrollToOffset(offset, scrollOptions) {\n if (isDestroyed) return;\n\n virtualizer?.scrollToOffset(offset, scrollOptions);\n },\n\n scrollToTop(scrollOptions) {\n if (isDestroyed) return;\n\n virtualizer?.scrollToTop(scrollOptions);\n },\n\n // ── DomVirtualList-specific ────────────────────────────────────────────\n setItems(items) {\n if (isDestroyed) return;\n\n // Read *before* mutating state — the \"was the list already at the end?\" check must\n // reflect the pre-update layout, not the one `render()` is about to produce below.\n const stick = resolveStickToBottom();\n const wasAtEnd = stick.enabled && (virtualizer?.isAtEnd(stick.threshold) ?? true);\n\n currentItems = items;\n\n if (items.length === 0) {\n virtualizer?.dispose();\n virtualizer = null;\n clearAndReset();\n\n return;\n }\n\n if (!virtualizer) {\n const v = spawnVirtualizer();\n\n if (wasAtEnd) v.scrollToBottom();\n\n return;\n }\n\n const countChanged = items.length !== virtualizer.count;\n\n // Only count needs explicit update — estimateSize and getItemKey are\n // closures that already reflect the latest currentItems automatically.\n virtualizer.update({ count: items.length });\n\n // When count changed, update() already triggered rebuild + computeVisible().\n // Only force re-emission when count is unchanged (data changed, count same).\n if (!countChanged) {\n // refresh() re-emits with current sizes for stable keys;\n // invalidate() clears position-based measurements when no stable keys.\n if (options.getItemKey) {\n virtualizer.refresh();\n } else {\n virtualizer.invalidate();\n }\n } else if (!options.getItemKey) {\n // Count changed AND no stable keys: position-based measurements are now\n // stale. Clear them so the next render remeasures from fresh estimates.\n virtualizer.invalidate();\n }\n\n if (wasAtEnd) virtualizer.scrollToBottom();\n },\n\n get stickyItems() {\n return virtualizer?.stickyItems ?? [];\n },\n\n [Symbol.dispose]: _dispose,\n\n get totalSize() {\n return virtualizer?.totalSize ?? 0;\n },\n };\n}\n\n// ─── F5: createVirtualScroller ────────────────────────────────────────────────\n\n/**\n * High-level factory that creates the scroll container and inner list element,\n * appends them to `container`, and returns a fully wired `DomVirtualListController`.\n *\n * @example\n * ```ts\n * const list = createVirtualScroller(document.getElementById('root')!, {\n * render({ items, listEl, recycle }) { … },\n * });\n * list.setItems(data);\n * ```\n */\nexport function createVirtualScroller<T>(\n container: HTMLElement,\n options: VirtualScrollerOptions<T>,\n): DomVirtualListController<T> {\n const scrollEl = document.createElement('div');\n\n scrollEl.style.cssText = options.horizontal\n ? 'overflow: auto hidden; width: 100%; height: 100%;'\n : 'overflow: hidden auto; width: 100%; height: 100%;';\n\n if (options.containerClass) scrollEl.className = options.containerClass;\n\n const listEl = document.createElement('div');\n\n scrollEl.appendChild(listEl);\n container.appendChild(scrollEl);\n\n let ctrl: DomVirtualListController<T>;\n\n try {\n ctrl = createDomVirtualList<T>({\n ...options,\n listElement: listEl,\n scrollElement: scrollEl,\n });\n } catch (e) {\n // Remove the scroll container if construction fails so we don't leak DOM nodes.\n scrollEl.remove();\n throw e;\n }\n\n // Override dispose and [Symbol.dispose] to also remove the scroll container.\n // Capture the original dispose before overwriting so there's no self-reference.\n const innerDispose = ctrl.dispose.bind(ctrl);\n\n return Object.assign(ctrl, {\n dispose() {\n innerDispose();\n scrollEl.remove();\n },\n [Symbol.dispose]() {\n innerDispose();\n scrollEl.remove();\n },\n });\n}\n"],"mappings":"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"}
|
|
@@ -18,6 +18,15 @@ export type DomVirtualListRenderArgs<T> = {
|
|
|
18
18
|
stickyItems: Array<VirtualRenderItem<T>>;
|
|
19
19
|
totalSize: number;
|
|
20
20
|
};
|
|
21
|
+
export type StickToBottomOptions = {
|
|
22
|
+
/** Enable/disable the behavior. Default: `true` once this object is provided. */
|
|
23
|
+
enabled?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Distance in pixels from the end still considered "at the end" — the bottom edge in
|
|
26
|
+
* vertical mode, the trailing edge in horizontal mode. Default: `48`.
|
|
27
|
+
*/
|
|
28
|
+
threshold?: number;
|
|
29
|
+
};
|
|
21
30
|
export type DomVirtualListOptions<T> = {
|
|
22
31
|
/** Custom teardown that clears listEl. Defaults to `listEl.textContent = ''`. */
|
|
23
32
|
clear?: (listEl: HTMLElement) => void;
|
|
@@ -31,6 +40,14 @@ export type DomVirtualListOptions<T> = {
|
|
|
31
40
|
overscan?: Overscan;
|
|
32
41
|
render: (args: DomVirtualListRenderArgs<T>) => void;
|
|
33
42
|
scrollElement: HTMLElement | Window;
|
|
43
|
+
/**
|
|
44
|
+
* Auto-scroll to the end after `setItems()` whenever the list was already at (or near) the
|
|
45
|
+
* end just before the update — the chat "stick to bottom on new message" pattern. Fires on
|
|
46
|
+
* *any* update while at the end, not just growth, so it also follows a streaming last
|
|
47
|
+
* item that grows in place without changing `items.length`. Does nothing while the user
|
|
48
|
+
* has scrolled away from the end. Pass `true` for defaults, or an options object.
|
|
49
|
+
*/
|
|
50
|
+
stickToBottom?: boolean | StickToBottomOptions;
|
|
34
51
|
/** Mark items as sticky headers. Receives the item index and the item data. */
|
|
35
52
|
sticky?: (index: number, item: T) => boolean;
|
|
36
53
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dom-virtual-list.d.ts","sourceRoot":"","sources":["../src/dom-virtual-list.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2C,KAAK,gBAAgB,EAAE,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzG,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EAChB,MAAM,eAAe,CAAC;AAEvB,YAAY,EACV,gBAAgB,EAChB,QAAQ,EACR,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,UAAU,GACX,CAAC;AAIF,mEAAmE;AACnE,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI,WAAW,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;CAAE,CAAC;AAEtE;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,WAAW,KAAK,WAAW,CAAC;AAEpF,MAAM,MAAM,wBAAwB,CAAC,CAAC,IAAI;IACxC,KAAK,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,SAAS,CAAC;IACnB,wEAAwE;IACxE,WAAW,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;IACzC,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,qBAAqB,CAAC,CAAC,IAAI;IACrC,iFAAiF;IACjF,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC,CAAC;IAC7D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,KAAK,UAAU,CAAC;IACpD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;IACzB,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE,wBAAwB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IACpD,aAAa,EAAE,WAAW,GAAG,MAAM,CAAC;IACpC,+EAA+E;IAC/E,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC;CAC9C,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,wBAAwB,CAAC,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,QAAQ,CAAC,GAAG;IAClF,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,sBAAsB,CAAC,CAAC,IAAI,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,aAAa,GAAG,eAAe,CAAC,GAAG;IACxG,oEAAoE;IACpE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AA8DF,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,OAAO,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,wBAAwB,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"dom-virtual-list.d.ts","sourceRoot":"","sources":["../src/dom-virtual-list.ts"],"names":[],"mappings":"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"}
|
package/dist/dom-virtual-list.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ScrollRangeError as e}from"./errors.js";import"./_utils.js";import{createVirtualizer as t}from"./virtualizer.js";function n(){let e=new Map,t=new Map,n=[],r=!1;return{acquire(i,a){if(!r)return a();let o=e.get(i);if(o)return t.set(i,o),o;let s=n.pop()??a();return t.set(i,s),s},beginCycle(){t=new Map,r=!0},clear(){for(let t of e.values())t.remove();e.clear(),t.clear(),n.length=0,r=!1},endCycle(){if(r){r=!1;for(let[r,i]of e)t.has(r)||(i.remove(),n.push(i));e=t}}}}function r(r){let i=[],a=!1,o=new AbortController,s=r.listElement,c=n(),l=null;function u(e){let t=i[e];return t!==void 0&&r.getItemKey?r.getItemKey(e,t):e}function d(e){if(r.estimateSize===void 0)return 36;if(typeof r.estimateSize==`number`)return r.estimateSize;let t=i[e];return t===void 0?36:r.estimateSize(e,t)}function f(e){r.horizontal?(s.style.height=``,s.style.width=`${e}px`):(s.style.height=`${e}px`,s.style.width=``)}function p(t){let n=i[t.index];if(n===void 0)throw new e(`toRenderItem: index ${t.index} is out of range (currentItems.length=${i.length})`);return{...t,data:n}}function m(e){f(e.totalSize),c.beginCycle();try{r.render({items:e.items.map(p),listEl:s,recycle:(e,t)=>c.acquire(e,t),stickyItems:e.stickyItems.map(p),totalSize:e.totalSize})}finally{c.endCycle()}}function h(){c.clear(),r.clear?r.clear(s):s.textContent=``,s.style.height=``,s.style.width=``,s.style.position=``,s.style.contain=``}function g(){
|
|
1
|
+
import{ScrollRangeError as e}from"./errors.js";import"./_utils.js";import{createVirtualizer as t}from"./virtualizer.js";function n(){let e=new Map,t=new Map,n=[],r=!1;return{acquire(i,a){if(!r)return a();let o=e.get(i);if(o)return t.set(i,o),o;let s=n.pop()??a();return t.set(i,s),s},beginCycle(){t=new Map,r=!0},clear(){for(let t of e.values())t.remove();e.clear(),t.clear(),n.length=0,r=!1},endCycle(){if(r){r=!1;for(let[r,i]of e)t.has(r)||(i.remove(),n.push(i));e=t}}}}function r(r){let i=[],a=!1,o=new AbortController,s=r.listElement,c=n(),l=null;function u(e){let t=i[e];return t!==void 0&&r.getItemKey?r.getItemKey(e,t):e}function d(e){if(r.estimateSize===void 0)return 36;if(typeof r.estimateSize==`number`)return r.estimateSize;let t=i[e];return t===void 0?36:r.estimateSize(e,t)}function f(e){r.horizontal?(s.style.height=``,s.style.width=`${e}px`):(s.style.height=`${e}px`,s.style.width=``)}function p(t){let n=i[t.index];if(n===void 0)throw new e(`toRenderItem: index ${t.index} is out of range (currentItems.length=${i.length})`);return{...t,data:n}}function m(e){f(e.totalSize),c.beginCycle();try{r.render({items:e.items.map(p),listEl:s,recycle:(e,t)=>c.acquire(e,t),stickyItems:e.stickyItems.map(p),totalSize:e.totalSize})}finally{c.endCycle()}}function h(){c.clear(),r.clear?r.clear(s):s.textContent=``,s.style.height=``,s.style.width=``,s.style.position=``,s.style.contain=``}function g(){let e=r.stickToBottom;return e?e===!0?{enabled:!0,threshold:48}:{enabled:e.enabled??!0,threshold:e.threshold??48}:{enabled:!1,threshold:48}}function _(){let e=t(r.scrollElement,{count:i.length,estimateSize:d,gap:r.gap,getItemKey:u,horizontal:r.horizontal,measurementCache:r.measurementCache,onChange:m,overscan:r.overscan??3,sticky:r.sticky?e=>{let t=i[e];return t!==void 0&&r.sticky(e,t)}:void 0});return l=e,s.style.position=`relative`,s.style.contain=`layout`,e}function v(){a||(a=!0,o.abort(),l?.dispose(),l=null,h())}return{get count(){return l?.count??0},get disposalSignal(){return o.signal},dispose:v,get disposed(){return a},invalidate(){a||l?.invalidate()},isAtEnd(e){return l?.isAtEnd(e)??!0},get isScrolling(){return l?.isScrolling??!1},get items(){return l?.items??[]},measure(e,t){a||l?.measure(e,t)},measureBatch(e){a||l?.measureBatch(e)},measureEl(e,t){return a?()=>{}:l?.measureEl(e,t)??(()=>{})},refresh(){a||l?.refresh()},get scrollOffset(){return l?.scrollOffset??0},scrollToBottom(e){a||l?.scrollToBottom(e)},scrollToIndex(e,t){a||l?.scrollToIndex(e,t)},scrollToOffset(e,t){a||l?.scrollToOffset(e,t)},scrollToTop(e){a||l?.scrollToTop(e)},setItems(e){if(a)return;let t=g(),n=t.enabled&&(l?.isAtEnd(t.threshold)??!0);if(i=e,e.length===0){l?.dispose(),l=null,h();return}if(!l){let e=_();n&&e.scrollToBottom();return}let o=e.length!==l.count;l.update({count:e.length}),o?r.getItemKey||l.invalidate():r.getItemKey?l.refresh():l.invalidate(),n&&l.scrollToBottom()},get stickyItems(){return l?.stickyItems??[]},[Symbol.dispose]:v,get totalSize(){return l?.totalSize??0}}}function i(e,t){let n=document.createElement(`div`);n.style.cssText=t.horizontal?`overflow: auto hidden; width: 100%; height: 100%;`:`overflow: hidden auto; width: 100%; height: 100%;`,t.containerClass&&(n.className=t.containerClass);let i=document.createElement(`div`);n.appendChild(i),e.appendChild(n);let a;try{a=r({...t,listElement:i,scrollElement:n})}catch(e){throw n.remove(),e}let o=a.dispose.bind(a);return Object.assign(a,{dispose(){o(),n.remove()},[Symbol.dispose](){o(),n.remove()}})}export{r as createDomVirtualList,i as createVirtualScroller};
|
|
2
2
|
//# sourceMappingURL=dom-virtual-list.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dom-virtual-list.js","names":[],"sources":["../src/dom-virtual-list.ts"],"sourcesContent":["import { DEFAULT_ESTIMATE_SIZE, DEFAULT_OVERSCAN, type MeasurementCache, type Overscan } from './_utils';\nimport { ScrollRangeError } from './errors';\nimport {\n createVirtualizer,\n type ScrollToIndexOptions,\n type VirtualItem,\n type Virtualizer,\n type VirtualizerState,\n type VirtualKey,\n} from './virtualizer';\n\nexport type {\n MeasurementCache,\n Overscan,\n ScrollToIndexOptions,\n VirtualItem,\n Virtualizer,\n VirtualizerState,\n VirtualKey,\n};\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\n/** A `VirtualItem` enriched with the corresponding data record. */\nexport type VirtualRenderItem<T> = VirtualItem & { readonly data: T };\n\n/**\n * Recycle a DOM node by key. If the pool has a live node for `key`, it is\n * returned and reused; otherwise `create()` is called to produce a new one.\n */\nexport type RecycleFn = (key: VirtualKey, create: () => HTMLElement) => HTMLElement;\n\nexport type DomVirtualListRenderArgs<T> = {\n items: Array<VirtualRenderItem<T>>;\n listEl: HTMLElement;\n recycle: RecycleFn;\n /** Sticky items from the underlying virtualizer, enriched with data. */\n stickyItems: Array<VirtualRenderItem<T>>;\n totalSize: number;\n};\n\nexport type DomVirtualListOptions<T> = {\n /** Custom teardown that clears listEl. Defaults to `listEl.textContent = ''`. */\n clear?: (listEl: HTMLElement) => void;\n estimateSize?: number | ((index: number, item: T) => number);\n gap?: number;\n getItemKey?: (index: number, item: T) => VirtualKey;\n horizontal?: boolean;\n listElement: HTMLElement;\n /** External measurement cache for scroll restoration. */\n measurementCache?: MeasurementCache;\n overscan?: Overscan;\n render: (args: DomVirtualListRenderArgs<T>) => void;\n scrollElement: HTMLElement | Window;\n /** Mark items as sticky headers. Receives the item index and the item data. */\n sticky?: (index: number, item: T) => boolean;\n};\n\n/**\n * R11: Controller extends Virtualizer so all methods (scrollToIndex, refresh,\n * scrollToOffset, etc.) are accessible directly on the controller without\n * needing to unwrap an inner virtualizer handle.\n *\n * `prepend` and `update` are omitted — use `setItems()` for item updates and\n * there is no direct `prepend` concept in DomVirtualList.\n */\nexport type DomVirtualListController<T> = Omit<Virtualizer, 'prepend' | 'update'> & {\n setItems: (items: T[]) => void;\n};\n\nexport type VirtualScrollerOptions<T> = Omit<DomVirtualListOptions<T>, 'listElement' | 'scrollElement'> & {\n /** Additional CSS class names on the generated scroll container. */\n containerClass?: string;\n};\n\n// ─── Node pool ────────────────────────────────────────────────────────────────\n\nfunction createNodePool() {\n let live = new Map<VirtualKey, HTMLElement>();\n let nextLive = new Map<VirtualKey, HTMLElement>();\n const stale: HTMLElement[] = [];\n let inCycle = false;\n\n return {\n acquire(key: VirtualKey, create: () => HTMLElement): HTMLElement {\n if (!inCycle) return create();\n\n const existing = live.get(key);\n\n if (existing) {\n nextLive.set(key, existing);\n\n return existing;\n }\n\n const node = stale.pop() ?? create();\n\n nextLive.set(key, node);\n\n return node;\n },\n\n beginCycle(): void {\n nextLive = new Map();\n inCycle = true;\n },\n\n clear(): void {\n for (const node of live.values()) node.remove();\n\n live.clear();\n nextLive.clear();\n stale.length = 0;\n inCycle = false;\n },\n\n endCycle(): void {\n if (!inCycle) return;\n\n inCycle = false;\n\n for (const [key, node] of live) {\n if (!nextLive.has(key)) {\n node.remove();\n stale.push(node);\n }\n }\n\n live = nextLive;\n },\n };\n}\n\n// ─── Implementation ────────────────────────────────────────────────────────────\n\nexport function createDomVirtualList<T>(options: DomVirtualListOptions<T>): DomVirtualListController<T> {\n let currentItems: T[] = [];\n let isDestroyed = false;\n const ac = new AbortController();\n const listEl = options.listElement;\n\n // Pool must be declared before virtualizer since handleChange (passed as onChange)\n // is invoked during createVirtualizer initialization via computeVisible.\n const pool = createNodePool();\n\n // Virtualizer is lazily created on the first non-empty setItems call.\n let virtualizer: Virtualizer | null = null;\n\n function resolveKey(index: number): VirtualKey {\n const item = currentItems[index];\n\n if (item !== undefined && options.getItemKey) return options.getItemKey(index, item);\n\n return index;\n }\n\n function resolveEstimate(index: number): number {\n if (options.estimateSize === undefined) return DEFAULT_ESTIMATE_SIZE;\n\n if (typeof options.estimateSize === 'number') return options.estimateSize;\n\n const item = currentItems[index];\n\n return item !== undefined ? options.estimateSize(index, item) : DEFAULT_ESTIMATE_SIZE;\n }\n\n function applyListSize(totalSize: number): void {\n if (options.horizontal) {\n listEl.style.height = '';\n listEl.style.width = `${totalSize}px`;\n } else {\n listEl.style.height = `${totalSize}px`;\n listEl.style.width = '';\n }\n }\n\n /**\n * R10: Throw rather than silently produce `undefined as T`.\n * This catches bugs where `vi.index` is out of range for `currentItems`.\n */\n function toRenderItem(vi: VirtualItem): VirtualRenderItem<T> {\n const data = currentItems[vi.index];\n\n if (data === undefined) {\n throw new ScrollRangeError(\n `toRenderItem: index ${vi.index} is out of range (currentItems.length=${currentItems.length})`,\n );\n }\n\n return { ...vi, data };\n }\n\n function handleChange(state: VirtualizerState): void {\n applyListSize(state.totalSize);\n\n pool.beginCycle();\n\n // R5: try/finally ensures endCycle() runs even if render() throws, keeping\n // the pool in a consistent state.\n try {\n options.render({\n items: state.items.map(toRenderItem),\n listEl,\n recycle: (key, create) => pool.acquire(key, create),\n stickyItems: state.stickyItems.map(toRenderItem),\n totalSize: state.totalSize,\n });\n } finally {\n pool.endCycle();\n }\n }\n\n function clearAndReset(): void {\n pool.clear();\n\n if (options.clear) {\n options.clear(listEl);\n } else {\n listEl.textContent = '';\n }\n\n listEl.style.height = '';\n listEl.style.width = '';\n listEl.style.position = '';\n listEl.style.contain = '';\n }\n\n function spawnVirtualizer(): void {\n virtualizer = createVirtualizer(options.scrollElement, {\n count: currentItems.length,\n estimateSize: resolveEstimate,\n gap: options.gap,\n getItemKey: resolveKey,\n horizontal: options.horizontal,\n measurementCache: options.measurementCache,\n onChange: handleChange,\n overscan: options.overscan ?? DEFAULT_OVERSCAN,\n sticky: options.sticky\n ? (index) => {\n const item = currentItems[index];\n\n return item !== undefined && options.sticky!(index, item);\n }\n : undefined,\n });\n\n listEl.style.position = 'relative';\n listEl.style.contain = 'layout';\n }\n\n function _dispose(): void {\n if (isDestroyed) return;\n\n isDestroyed = true;\n ac.abort();\n virtualizer?.dispose();\n virtualizer = null;\n clearAndReset();\n }\n\n return {\n // ── Virtualizer passthrough (R11) ──────────────────────────────────────\n get count() {\n return virtualizer?.count ?? 0;\n },\n\n get disposalSignal() {\n return ac.signal;\n },\n\n dispose: _dispose,\n\n get disposed() {\n return isDestroyed;\n },\n\n invalidate() {\n if (isDestroyed) return;\n\n virtualizer?.invalidate();\n },\n\n get isScrolling() {\n return virtualizer?.isScrolling ?? false;\n },\n\n get items() {\n return virtualizer?.items ?? [];\n },\n\n measure(index, size) {\n if (isDestroyed) return;\n\n virtualizer?.measure(index, size);\n },\n\n measureBatch(entries) {\n if (isDestroyed) return;\n\n virtualizer?.measureBatch(entries);\n },\n\n measureEl(index, el) {\n if (isDestroyed) return () => {};\n\n return virtualizer?.measureEl(index, el) ?? (() => {});\n },\n\n refresh() {\n if (isDestroyed) return;\n\n virtualizer?.refresh();\n },\n\n get scrollOffset() {\n return virtualizer?.scrollOffset ?? 0;\n },\n\n scrollToBottom(scrollOptions) {\n if (isDestroyed) return;\n\n virtualizer?.scrollToBottom(scrollOptions);\n },\n\n scrollToIndex(index, scrollOptions) {\n if (isDestroyed) return;\n\n virtualizer?.scrollToIndex(index, scrollOptions);\n },\n\n scrollToOffset(offset, scrollOptions) {\n if (isDestroyed) return;\n\n virtualizer?.scrollToOffset(offset, scrollOptions);\n },\n\n scrollToTop(scrollOptions) {\n if (isDestroyed) return;\n\n virtualizer?.scrollToTop(scrollOptions);\n },\n\n // ── DomVirtualList-specific ────────────────────────────────────────────\n setItems(items) {\n if (isDestroyed) return;\n\n currentItems = items;\n\n if (items.length === 0) {\n virtualizer?.dispose();\n virtualizer = null;\n clearAndReset();\n\n return;\n }\n\n if (!virtualizer) {\n spawnVirtualizer();\n\n return;\n }\n\n const countChanged = items.length !== virtualizer.count;\n\n // Only count needs explicit update — estimateSize and getItemKey are\n // closures that already reflect the latest currentItems automatically.\n virtualizer.update({ count: items.length });\n\n // When count changed, update() already triggered rebuild + computeVisible().\n // Only force re-emission when count is unchanged (data changed, count same).\n if (!countChanged) {\n // refresh() re-emits with current sizes for stable keys;\n // invalidate() clears position-based measurements when no stable keys.\n if (options.getItemKey) {\n virtualizer.refresh();\n } else {\n virtualizer.invalidate();\n }\n } else if (!options.getItemKey) {\n // Count changed AND no stable keys: position-based measurements are now\n // stale. Clear them so the next render remeasures from fresh estimates.\n virtualizer.invalidate();\n }\n },\n\n get stickyItems() {\n return virtualizer?.stickyItems ?? [];\n },\n\n [Symbol.dispose]: _dispose,\n\n get totalSize() {\n return virtualizer?.totalSize ?? 0;\n },\n };\n}\n\n// ─── F5: createVirtualScroller ────────────────────────────────────────────────\n\n/**\n * High-level factory that creates the scroll container and inner list element,\n * appends them to `container`, and returns a fully wired `DomVirtualListController`.\n *\n * @example\n * ```ts\n * const list = createVirtualScroller(document.getElementById('root')!, {\n * render({ items, listEl, recycle }) { … },\n * });\n * list.setItems(data);\n * ```\n */\nexport function createVirtualScroller<T>(\n container: HTMLElement,\n options: VirtualScrollerOptions<T>,\n): DomVirtualListController<T> {\n const scrollEl = document.createElement('div');\n\n scrollEl.style.cssText = options.horizontal\n ? 'overflow: auto hidden; width: 100%; height: 100%;'\n : 'overflow: hidden auto; width: 100%; height: 100%;';\n\n if (options.containerClass) scrollEl.className = options.containerClass;\n\n const listEl = document.createElement('div');\n\n scrollEl.appendChild(listEl);\n container.appendChild(scrollEl);\n\n let ctrl: DomVirtualListController<T>;\n\n try {\n ctrl = createDomVirtualList<T>({\n ...options,\n listElement: listEl,\n scrollElement: scrollEl,\n });\n } catch (e) {\n // Remove the scroll container if construction fails so we don't leak DOM nodes.\n scrollEl.remove();\n throw e;\n }\n\n // Override dispose and [Symbol.dispose] to also remove the scroll container.\n // Capture the original dispose before overwriting so there's no self-reference.\n const innerDispose = ctrl.dispose.bind(ctrl);\n\n return Object.assign(ctrl, {\n dispose() {\n innerDispose();\n scrollEl.remove();\n },\n [Symbol.dispose]() {\n innerDispose();\n scrollEl.remove();\n },\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dom-virtual-list.js","names":[],"sources":["../src/dom-virtual-list.ts"],"sourcesContent":["import { DEFAULT_ESTIMATE_SIZE, DEFAULT_OVERSCAN, type MeasurementCache, type Overscan } from './_utils';\nimport { ScrollRangeError } from './errors';\nimport {\n createVirtualizer,\n type ScrollToIndexOptions,\n type VirtualItem,\n type Virtualizer,\n type VirtualizerState,\n type VirtualKey,\n} from './virtualizer';\n\nexport type {\n MeasurementCache,\n Overscan,\n ScrollToIndexOptions,\n VirtualItem,\n Virtualizer,\n VirtualizerState,\n VirtualKey,\n};\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\n/** A `VirtualItem` enriched with the corresponding data record. */\nexport type VirtualRenderItem<T> = VirtualItem & { readonly data: T };\n\n/**\n * Recycle a DOM node by key. If the pool has a live node for `key`, it is\n * returned and reused; otherwise `create()` is called to produce a new one.\n */\nexport type RecycleFn = (key: VirtualKey, create: () => HTMLElement) => HTMLElement;\n\nexport type DomVirtualListRenderArgs<T> = {\n items: Array<VirtualRenderItem<T>>;\n listEl: HTMLElement;\n recycle: RecycleFn;\n /** Sticky items from the underlying virtualizer, enriched with data. */\n stickyItems: Array<VirtualRenderItem<T>>;\n totalSize: number;\n};\n\nexport type StickToBottomOptions = {\n /** Enable/disable the behavior. Default: `true` once this object is provided. */\n enabled?: boolean;\n /**\n * Distance in pixels from the end still considered \"at the end\" — the bottom edge in\n * vertical mode, the trailing edge in horizontal mode. Default: `48`.\n */\n threshold?: number;\n};\n\nexport type DomVirtualListOptions<T> = {\n /** Custom teardown that clears listEl. Defaults to `listEl.textContent = ''`. */\n clear?: (listEl: HTMLElement) => void;\n estimateSize?: number | ((index: number, item: T) => number);\n gap?: number;\n getItemKey?: (index: number, item: T) => VirtualKey;\n horizontal?: boolean;\n listElement: HTMLElement;\n /** External measurement cache for scroll restoration. */\n measurementCache?: MeasurementCache;\n overscan?: Overscan;\n render: (args: DomVirtualListRenderArgs<T>) => void;\n scrollElement: HTMLElement | Window;\n /**\n * Auto-scroll to the end after `setItems()` whenever the list was already at (or near) the\n * end just before the update — the chat \"stick to bottom on new message\" pattern. Fires on\n * *any* update while at the end, not just growth, so it also follows a streaming last\n * item that grows in place without changing `items.length`. Does nothing while the user\n * has scrolled away from the end. Pass `true` for defaults, or an options object.\n */\n stickToBottom?: boolean | StickToBottomOptions;\n /** Mark items as sticky headers. Receives the item index and the item data. */\n sticky?: (index: number, item: T) => boolean;\n};\n\n/**\n * R11: Controller extends Virtualizer so all methods (scrollToIndex, refresh,\n * scrollToOffset, etc.) are accessible directly on the controller without\n * needing to unwrap an inner virtualizer handle.\n *\n * `prepend` and `update` are omitted — use `setItems()` for item updates and\n * there is no direct `prepend` concept in DomVirtualList.\n */\nexport type DomVirtualListController<T> = Omit<Virtualizer, 'prepend' | 'update'> & {\n setItems: (items: T[]) => void;\n};\n\nexport type VirtualScrollerOptions<T> = Omit<DomVirtualListOptions<T>, 'listElement' | 'scrollElement'> & {\n /** Additional CSS class names on the generated scroll container. */\n containerClass?: string;\n};\n\n// ─── Node pool ────────────────────────────────────────────────────────────────\n\nfunction createNodePool() {\n let live = new Map<VirtualKey, HTMLElement>();\n let nextLive = new Map<VirtualKey, HTMLElement>();\n const stale: HTMLElement[] = [];\n let inCycle = false;\n\n return {\n acquire(key: VirtualKey, create: () => HTMLElement): HTMLElement {\n if (!inCycle) return create();\n\n const existing = live.get(key);\n\n if (existing) {\n nextLive.set(key, existing);\n\n return existing;\n }\n\n const node = stale.pop() ?? create();\n\n nextLive.set(key, node);\n\n return node;\n },\n\n beginCycle(): void {\n nextLive = new Map();\n inCycle = true;\n },\n\n clear(): void {\n for (const node of live.values()) node.remove();\n\n live.clear();\n nextLive.clear();\n stale.length = 0;\n inCycle = false;\n },\n\n endCycle(): void {\n if (!inCycle) return;\n\n inCycle = false;\n\n for (const [key, node] of live) {\n if (!nextLive.has(key)) {\n node.remove();\n stale.push(node);\n }\n }\n\n live = nextLive;\n },\n };\n}\n\n// ─── Implementation ────────────────────────────────────────────────────────────\n\nexport function createDomVirtualList<T>(options: DomVirtualListOptions<T>): DomVirtualListController<T> {\n let currentItems: T[] = [];\n let isDestroyed = false;\n const ac = new AbortController();\n const listEl = options.listElement;\n\n // Pool must be declared before virtualizer since handleChange (passed as onChange)\n // is invoked during createVirtualizer initialization via computeVisible.\n const pool = createNodePool();\n\n // Virtualizer is lazily created on the first non-empty setItems call.\n let virtualizer: Virtualizer | null = null;\n\n function resolveKey(index: number): VirtualKey {\n const item = currentItems[index];\n\n if (item !== undefined && options.getItemKey) return options.getItemKey(index, item);\n\n return index;\n }\n\n function resolveEstimate(index: number): number {\n if (options.estimateSize === undefined) return DEFAULT_ESTIMATE_SIZE;\n\n if (typeof options.estimateSize === 'number') return options.estimateSize;\n\n const item = currentItems[index];\n\n return item !== undefined ? options.estimateSize(index, item) : DEFAULT_ESTIMATE_SIZE;\n }\n\n function applyListSize(totalSize: number): void {\n if (options.horizontal) {\n listEl.style.height = '';\n listEl.style.width = `${totalSize}px`;\n } else {\n listEl.style.height = `${totalSize}px`;\n listEl.style.width = '';\n }\n }\n\n /**\n * R10: Throw rather than silently produce `undefined as T`.\n * This catches bugs where `vi.index` is out of range for `currentItems`.\n */\n function toRenderItem(vi: VirtualItem): VirtualRenderItem<T> {\n const data = currentItems[vi.index];\n\n if (data === undefined) {\n throw new ScrollRangeError(\n `toRenderItem: index ${vi.index} is out of range (currentItems.length=${currentItems.length})`,\n );\n }\n\n return { ...vi, data };\n }\n\n function handleChange(state: VirtualizerState): void {\n applyListSize(state.totalSize);\n\n pool.beginCycle();\n\n // R5: try/finally ensures endCycle() runs even if render() throws, keeping\n // the pool in a consistent state.\n try {\n options.render({\n items: state.items.map(toRenderItem),\n listEl,\n recycle: (key, create) => pool.acquire(key, create),\n stickyItems: state.stickyItems.map(toRenderItem),\n totalSize: state.totalSize,\n });\n } finally {\n pool.endCycle();\n }\n }\n\n function clearAndReset(): void {\n pool.clear();\n\n if (options.clear) {\n options.clear(listEl);\n } else {\n listEl.textContent = '';\n }\n\n listEl.style.height = '';\n listEl.style.width = '';\n listEl.style.position = '';\n listEl.style.contain = '';\n }\n\n const DEFAULT_STICK_THRESHOLD = 48;\n\n function resolveStickToBottom(): { enabled: boolean; threshold: number } {\n const opt = options.stickToBottom;\n\n if (!opt) return { enabled: false, threshold: DEFAULT_STICK_THRESHOLD };\n\n if (opt === true) return { enabled: true, threshold: DEFAULT_STICK_THRESHOLD };\n\n return { enabled: opt.enabled ?? true, threshold: opt.threshold ?? DEFAULT_STICK_THRESHOLD };\n }\n\n function spawnVirtualizer(): Virtualizer {\n const v = createVirtualizer(options.scrollElement, {\n count: currentItems.length,\n estimateSize: resolveEstimate,\n gap: options.gap,\n getItemKey: resolveKey,\n horizontal: options.horizontal,\n measurementCache: options.measurementCache,\n onChange: handleChange,\n overscan: options.overscan ?? DEFAULT_OVERSCAN,\n sticky: options.sticky\n ? (index) => {\n const item = currentItems[index];\n\n return item !== undefined && options.sticky!(index, item);\n }\n : undefined,\n });\n\n virtualizer = v;\n listEl.style.position = 'relative';\n listEl.style.contain = 'layout';\n\n return v;\n }\n\n function _dispose(): void {\n if (isDestroyed) return;\n\n isDestroyed = true;\n ac.abort();\n virtualizer?.dispose();\n virtualizer = null;\n clearAndReset();\n }\n\n return {\n // ── Virtualizer passthrough (R11) ──────────────────────────────────────\n get count() {\n return virtualizer?.count ?? 0;\n },\n\n get disposalSignal() {\n return ac.signal;\n },\n\n dispose: _dispose,\n\n get disposed() {\n return isDestroyed;\n },\n\n invalidate() {\n if (isDestroyed) return;\n\n virtualizer?.invalidate();\n },\n\n isAtEnd(threshold) {\n return virtualizer?.isAtEnd(threshold) ?? true;\n },\n\n get isScrolling() {\n return virtualizer?.isScrolling ?? false;\n },\n\n get items() {\n return virtualizer?.items ?? [];\n },\n\n measure(index, size) {\n if (isDestroyed) return;\n\n virtualizer?.measure(index, size);\n },\n\n measureBatch(entries) {\n if (isDestroyed) return;\n\n virtualizer?.measureBatch(entries);\n },\n\n measureEl(index, el) {\n if (isDestroyed) return () => {};\n\n return virtualizer?.measureEl(index, el) ?? (() => {});\n },\n\n refresh() {\n if (isDestroyed) return;\n\n virtualizer?.refresh();\n },\n\n get scrollOffset() {\n return virtualizer?.scrollOffset ?? 0;\n },\n\n scrollToBottom(scrollOptions) {\n if (isDestroyed) return;\n\n virtualizer?.scrollToBottom(scrollOptions);\n },\n\n scrollToIndex(index, scrollOptions) {\n if (isDestroyed) return;\n\n virtualizer?.scrollToIndex(index, scrollOptions);\n },\n\n scrollToOffset(offset, scrollOptions) {\n if (isDestroyed) return;\n\n virtualizer?.scrollToOffset(offset, scrollOptions);\n },\n\n scrollToTop(scrollOptions) {\n if (isDestroyed) return;\n\n virtualizer?.scrollToTop(scrollOptions);\n },\n\n // ── DomVirtualList-specific ────────────────────────────────────────────\n setItems(items) {\n if (isDestroyed) return;\n\n // Read *before* mutating state — the \"was the list already at the end?\" check must\n // reflect the pre-update layout, not the one `render()` is about to produce below.\n const stick = resolveStickToBottom();\n const wasAtEnd = stick.enabled && (virtualizer?.isAtEnd(stick.threshold) ?? true);\n\n currentItems = items;\n\n if (items.length === 0) {\n virtualizer?.dispose();\n virtualizer = null;\n clearAndReset();\n\n return;\n }\n\n if (!virtualizer) {\n const v = spawnVirtualizer();\n\n if (wasAtEnd) v.scrollToBottom();\n\n return;\n }\n\n const countChanged = items.length !== virtualizer.count;\n\n // Only count needs explicit update — estimateSize and getItemKey are\n // closures that already reflect the latest currentItems automatically.\n virtualizer.update({ count: items.length });\n\n // When count changed, update() already triggered rebuild + computeVisible().\n // Only force re-emission when count is unchanged (data changed, count same).\n if (!countChanged) {\n // refresh() re-emits with current sizes for stable keys;\n // invalidate() clears position-based measurements when no stable keys.\n if (options.getItemKey) {\n virtualizer.refresh();\n } else {\n virtualizer.invalidate();\n }\n } else if (!options.getItemKey) {\n // Count changed AND no stable keys: position-based measurements are now\n // stale. Clear them so the next render remeasures from fresh estimates.\n virtualizer.invalidate();\n }\n\n if (wasAtEnd) virtualizer.scrollToBottom();\n },\n\n get stickyItems() {\n return virtualizer?.stickyItems ?? [];\n },\n\n [Symbol.dispose]: _dispose,\n\n get totalSize() {\n return virtualizer?.totalSize ?? 0;\n },\n };\n}\n\n// ─── F5: createVirtualScroller ────────────────────────────────────────────────\n\n/**\n * High-level factory that creates the scroll container and inner list element,\n * appends them to `container`, and returns a fully wired `DomVirtualListController`.\n *\n * @example\n * ```ts\n * const list = createVirtualScroller(document.getElementById('root')!, {\n * render({ items, listEl, recycle }) { … },\n * });\n * list.setItems(data);\n * ```\n */\nexport function createVirtualScroller<T>(\n container: HTMLElement,\n options: VirtualScrollerOptions<T>,\n): DomVirtualListController<T> {\n const scrollEl = document.createElement('div');\n\n scrollEl.style.cssText = options.horizontal\n ? 'overflow: auto hidden; width: 100%; height: 100%;'\n : 'overflow: hidden auto; width: 100%; height: 100%;';\n\n if (options.containerClass) scrollEl.className = options.containerClass;\n\n const listEl = document.createElement('div');\n\n scrollEl.appendChild(listEl);\n container.appendChild(scrollEl);\n\n let ctrl: DomVirtualListController<T>;\n\n try {\n ctrl = createDomVirtualList<T>({\n ...options,\n listElement: listEl,\n scrollElement: scrollEl,\n });\n } catch (e) {\n // Remove the scroll container if construction fails so we don't leak DOM nodes.\n scrollEl.remove();\n throw e;\n }\n\n // Override dispose and [Symbol.dispose] to also remove the scroll container.\n // Capture the original dispose before overwriting so there's no self-reference.\n const innerDispose = ctrl.dispose.bind(ctrl);\n\n return Object.assign(ctrl, {\n dispose() {\n innerDispose();\n scrollEl.remove();\n },\n [Symbol.dispose]() {\n innerDispose();\n scrollEl.remove();\n },\n });\n}\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { ScrollError, ScrollRangeError } from './errors';
|
|
2
2
|
export { createDomVirtualList, createVirtualScroller } from './dom-virtual-list';
|
|
3
|
-
export type { DomVirtualListController, DomVirtualListOptions, DomVirtualListRenderArgs, RecycleFn, VirtualRenderItem, VirtualScrollerOptions, } from './dom-virtual-list';
|
|
3
|
+
export type { DomVirtualListController, DomVirtualListOptions, DomVirtualListRenderArgs, RecycleFn, StickToBottomOptions, VirtualRenderItem, VirtualScrollerOptions, } from './dom-virtual-list';
|
|
4
4
|
export { createGridVirtualizer } from './grid-virtualizer';
|
|
5
5
|
export type { GridRangeChangeEvent, GridVirtualizer, GridVirtualizerOptions, GridVirtualizerState, GridVirtualizerUpdateOptions, ScrollToCellOptions, } from './grid-virtualizer';
|
|
6
6
|
export { createGroupedVirtualizer } from './grouped-virtualizer';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AACjF,YAAY,EACV,wBAAwB,EACxB,qBAAqB,EACrB,wBAAwB,EACxB,SAAS,EACT,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,YAAY,EACV,oBAAoB,EACpB,eAAe,EACf,sBAAsB,EACtB,oBAAoB,EACpB,4BAA4B,EAC5B,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,EACvB,qBAAqB,EACrB,6BAA6B,GAC9B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,gCAAgC,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AACzF,YAAY,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACxF,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACnH,YAAY,EACV,gBAAgB,EAChB,QAAQ,EACR,YAAY,EACZ,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,wBAAwB,EACxB,UAAU,GACX,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AACjF,YAAY,EACV,wBAAwB,EACxB,qBAAqB,EACrB,wBAAwB,EACxB,SAAS,EACT,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,YAAY,EACV,oBAAoB,EACpB,eAAe,EACf,sBAAsB,EACtB,oBAAoB,EACpB,4BAA4B,EAC5B,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,EACvB,qBAAqB,EACrB,6BAA6B,GAC9B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,gCAAgC,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AACzF,YAAY,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACxF,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACnH,YAAY,EACV,gBAAgB,EAChB,QAAQ,EACR,YAAY,EACZ,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,wBAAwB,EACxB,UAAU,GACX,MAAM,eAAe,CAAC"}
|
package/dist/scroll.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@vielzeug/ripple");var t=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},n=class extends t{},r=!globalThis.__SCROLL_PROD__;function i(e){r&&console.warn(`[@vielzeug/scroll] ${e}`)}function a(){return new Map}var o=36,s=3;function c(e,t=0){return Number.isFinite(e)?Math.max(0,Math.floor(e)):t}function l(e,t){return!Number.isFinite(e)||e<=0||e>1e7?t:e}function u(e){return typeof Window<`u`&&e instanceof Window||typeof e.innerHeight==`number`&&typeof e.document==`object`}function d(e,t){if(typeof e==`number`){let n=c(e,t);return{end:n,start:n}}return{end:c(e?.end??t),start:c(e?.start??t)}}function f(e,t,n){let r=new ResizeObserver(e=>{for(let t of e)n(t)});return r.observe(t),e.addEventListener(`abort`,()=>r.disconnect(),{once:!0}),()=>r.disconnect()}function p(e,t){if(typeof e==`function`)return n=>{try{return l(e(n),t)}catch(e){return i(`estimateSize threw for index ${n}: ${e}`),t}};let n=typeof e==`number`?l(e,t):t;return()=>n}function m(e,t,n){if(u(e))return e.addEventListener(`scroll`,t,{passive:!0}),e.addEventListener(`resize`,n,{passive:!0}),{detach(){e.removeEventListener(`scroll`,t),e.removeEventListener(`resize`,n)},scrollTo:(t,n,r)=>e.scrollTo({behavior:r,left:t,top:n}),x:{readOffset:()=>e.scrollX,readViewportSize:()=>e.innerWidth,writeOffset:(t,n)=>e.scrollTo({behavior:n,left:t})},y:{readOffset:()=>e.scrollY,readViewportSize:()=>e.innerHeight,writeOffset:(t,n)=>e.scrollTo({behavior:n,top:t})}};let r=new ResizeObserver(n);return e.addEventListener(`scroll`,t,{passive:!0}),r.observe(e),{detach(){e.removeEventListener(`scroll`,t),r.disconnect()},scrollTo:(t,n,r)=>e.scrollTo({behavior:r,left:t,top:n}),x:{readOffset:()=>e.scrollLeft,readViewportSize:()=>e.clientWidth,writeOffset:(t,n)=>e.scrollTo({behavior:n,left:t})},y:{readOffset:()=>e.scrollTop,readViewportSize:()=>e.clientHeight,writeOffset:(t,n)=>e.scrollTo({behavior:n,top:t})}}}function h(e,t,n){let r=e,i=n,a=t,o=[],s=0,c=-1,l=-1,u=-1,d=!1,f=1/0;function p(e){return o[e]??0}function m(e){return p(e)+a(e)}function h(e=!1){e&&(c=-1,l=-1,u=-1);let t=[0],n=0;for(let e=0;e<r;e++)n+=a(e)+(e<r-1?i:0),t.push(n);o=t,s=n}function g(e){if(!Number.isFinite(e)||e>=r)return;c=-1,l=-1,u=-1;let t=o[e]??0;for(let n=e;n<r;n++)o[n]=t,t+=a(n)+(n<r-1?i:0);o[r]=t,s=t}function _(e){let t=0,n=r-1;for(;t<n;){let r=t+n>>1;m(r)<=e?t=r+1:n=r}return t}function v(e,t){let n=t,i=r-1;for(;n<i;){let t=n+i+1>>1;p(t)<e?n=t:i=t-1}return n}function y(e,t,n,i){let a=_(e),o=v(t,a);return{renderEnd:Math.min(r-1,o+i),renderStart:Math.max(0,a-n)}}function b(e,t){return e===c&&t===l&&s===u}function x(e,t){c=e,l=t,u=s}function S(){c=-1,l=-1,u=-1}function C(e){d||(d=!0,queueMicrotask(()=>{d=!1;let t=f;f=1/0,g(t),e()}))}function w(e){e<f&&(f=e)}function T(e){let t=p(e),n=a(e);return{end:t+n,index:e,size:n,start:t}}return{commitDedup:x,computeRange:y,consumeMinChangedIndex(){let e=f;return f=1/0,e},get count(){return r},endAt:m,findFirst:_,findLast:v,get gap(){return i},isDedupSame:b,itemAt:T,markChanged:w,get minChangedIndex(){return f},get pendingBuild(){return d},get prevEnd(){return l},get prevStart(){return c},get prevTotalSize(){return u},rebuild:h,rebuildFrom:g,resetDedup:S,scheduleBuild:C,setCount(e){r=e},setGap(e){i=e},sizeAt(e){return a(e)},startAt:p,get totalSize(){return s}}}function g(e,t){let n=!!t.horizontal,r=e=>e,i=c(t.count),a=p(t.estimateSize,36),o=c(t.gap??0),s=t.getItemKey??r,u=d(t.overscan,3),g=t.sticky??null,_=t.onChange,v=t.onScrollEnd,y=t.onScrollingChange,b=typeof t.scrollEndDelay==`number`&&t.scrollEndDelay>=0?t.scrollEndDelay:150,x=!1,S=null,C=`onscrollend`in e;function w(){x&&(x=!1,y?.(!1),v?.(M))}function T(){x||(x=!0,y?.(!0)),C||(S!==null&&clearTimeout(S),S=setTimeout(w,b))}let E=t.measurementCache??new Map,D=[],O=[],k=h(i,e=>E.get(s(e))??a(e),o),A=-1,j=0,M=0,N=0,P=-1,F=!1;function I(e){let t=Number.isFinite(e)?e:0,n=Math.max(0,k.totalSize-N);return Math.min(n,Math.max(0,t))}function L(){if(!g||i===0||M<=0)return[];let e=-1,t=0,n=i-1;for(;t<=n;){let r=t+n>>1;k.startAt(r)<M?(e=r,t=r+1):n=r-1}if(e<0)return[];let r=-1;for(let t=e;t>=0;t--)if(g(t)){r=t;break}if(r===-1)return[];let a=k.sizeAt(r),o=1/0;for(let e=r+1;e<i;e++)if(g(e)){o=k.startAt(e);break}let s=Math.min(M,o-a);return[{end:s+a,index:r,size:a,start:s}]}function R(){if(F)return;if(i===0||N<=0){(k.prevStart!==-1||k.prevTotalSize!==k.totalSize)&&(k.commitDedup(-1,-1),D=[],O=[],_&&_({items:D,stickyItems:O,totalSize:k.totalSize}));return}let{renderEnd:e,renderStart:t}=k.computeRange(M,M+N,u.start,u.end),n=g!==null&&M!==P;if(k.isDedupSame(t,e)&&!n)return;k.commitDedup(t,e),P=M;let r=[];for(let n=t;n<=e;n++)r.push(k.itemAt(n));D=r,O=g?L():[],_&&_({items:D,stickyItems:O,totalSize:k.totalSize})}function ee(e,t){if(!Number.isFinite(e))return!1;let n=Math.floor(e);if(n<0||n>=i)return!1;let r=l(t,-1);if(r<=0)return!1;let a=s(n);return E.get(a)===r?!1:(E.set(a,r),k.markChanged(n),!0)}function z(){if(M<=0||i===0){A=-1;return}A=k.findFirst(M),j=M-k.startAt(A)}function B(){if(A<0)return;let e=k.startAt(A)+j-M;if(A=-1,Math.abs(e)>=1){let t=I(M+e);$.writeOffset(t,`auto`),M=t}}function V(e){if(F)return;let t=!1;for(let{index:n,size:r}of e)ee(n,r)&&(t=!0);t&&(k.pendingBuild||z(),k.scheduleBuild(()=>{B(),R()}))}function H(e,t){V([{index:e,size:t}])}function U(e,t){return F?()=>{}:f(Z.signal,t,t=>{H(e,n?t.contentRect.width:t.contentRect.height)})}function W(e){let t=!1,n=!1;if(e.count!==void 0){let n=c(e.count);n!==i&&(i=n,k.setCount(i),t=!0)}if(Object.hasOwn(e,`estimateSize`)&&(a=p(e.estimateSize,36),E.clear(),t=!0),e.gap!==void 0){let n=c(e.gap);n!==o&&(o=n,k.setGap(o),t=!0)}if(Object.hasOwn(e,`getItemKey`)){let n=e.getItemKey??r;n!==s&&(s=n,E.clear(),t=!0)}if(Object.hasOwn(e,`measurementCache`)&&e.measurementCache!==void 0&&(E=e.measurementCache,t=!0),e.overscan!==void 0){let t=d(e.overscan,3);(t.start!==u.start||t.end!==u.end)&&(u=t,n=!0)}if(Object.hasOwn(e,`sticky`)){let t=e.sticky??null;t!==g&&(g=t,k.resetDedup()),n=!0}t&&(z(),k.rebuild(!0),B()),(n||t)&&R()}function G(){F||(E.clear(),k.rebuild(!0),R())}function K(){F||(k.rebuild(!0),R())}function q(e){if(F)return;let t=c(e);if(t===0)return;i+=t,k.setCount(i),k.rebuild(!0);let n=k.startAt(t),r=I(M+n);$.writeOffset(r,`auto`),M=r,R()}function J(e){F||W(e)}function te(t,n={}){if(F||i<=0)return;let r=Math.max(0,Math.min(Number.isFinite(t)?Math.floor(t):0,i-1)),a=n.align??`auto`,o=n.behavior??`auto`,s=k.startAt(r),c=k.sizeAt(r),l=s+c,u;if(a===`start`)u=s;else if(a===`end`)u=l-N;else if(a===`center`)u=s-(N-c)/2;else{let e=M,t=e+N;if(s>=e&&l<=t){n.onComplete&&queueMicrotask(n.onComplete);return}u=s<e?s:l-N}$.writeOffset(I(u),o),n.onComplete&&(o===`smooth`&&`onscrollend`in e?e.addEventListener(`scrollend`,n.onComplete,{once:!0}):queueMicrotask(n.onComplete))}function Y(e,t={}){F||$.writeOffset(I(e),t.behavior??`auto`)}function X(e={}){Y(0,e)}function ne(e={}){Y(Math.max(0,k.totalSize-N),e)}let Z=new AbortController;function re(){F||(F=!0,Z.abort(),S!==null&&(clearTimeout(S),S=null),C&&e.removeEventListener(`scrollend`,w),Q.detach())}function ie(){M=I($.readOffset()),T(),R()}function ae(){N=$.readViewportSize(),R()}let Q=m(e,ie,ae),$=n?Q.x:Q.y;return C&&e.addEventListener(`scrollend`,w,{passive:!0}),k.rebuild(!0),t.initialOffset!==void 0&&$.writeOffset(I(t.initialOffset),`auto`),M=I($.readOffset()),N=$.readViewportSize(),R(),{get count(){return i},get disposalSignal(){return Z.signal},dispose:re,get disposed(){return F},invalidate:G,get isScrolling(){return x},get items(){return D},measure:H,measureBatch:V,measureEl:U,prepend:q,refresh:K,get scrollOffset(){return M},scrollToBottom:ne,scrollToIndex:te,scrollToOffset:Y,scrollToTop:X,get stickyItems(){return O},[Symbol.dispose]:re,get totalSize(){return k.totalSize},update:J}}function _(){let e=new Map,t=new Map,n=[],r=!1;return{acquire(i,a){if(!r)return a();let o=e.get(i);if(o)return t.set(i,o),o;let s=n.pop()??a();return t.set(i,s),s},beginCycle(){t=new Map,r=!0},clear(){for(let t of e.values())t.remove();e.clear(),t.clear(),n.length=0,r=!1},endCycle(){if(r){r=!1;for(let[r,i]of e)t.has(r)||(i.remove(),n.push(i));e=t}}}}function v(e){let t=[],r=!1,i=new AbortController,a=e.listElement,o=_(),s=null;function c(n){let r=t[n];return r!==void 0&&e.getItemKey?e.getItemKey(n,r):n}function l(n){if(e.estimateSize===void 0)return 36;if(typeof e.estimateSize==`number`)return e.estimateSize;let r=t[n];return r===void 0?36:e.estimateSize(n,r)}function u(t){e.horizontal?(a.style.height=``,a.style.width=`${t}px`):(a.style.height=`${t}px`,a.style.width=``)}function d(e){let r=t[e.index];if(r===void 0)throw new n(`toRenderItem: index ${e.index} is out of range (currentItems.length=${t.length})`);return{...e,data:r}}function f(t){u(t.totalSize),o.beginCycle();try{e.render({items:t.items.map(d),listEl:a,recycle:(e,t)=>o.acquire(e,t),stickyItems:t.stickyItems.map(d),totalSize:t.totalSize})}finally{o.endCycle()}}function p(){o.clear(),e.clear?e.clear(a):a.textContent=``,a.style.height=``,a.style.width=``,a.style.position=``,a.style.contain=``}function m(){s=g(e.scrollElement,{count:t.length,estimateSize:l,gap:e.gap,getItemKey:c,horizontal:e.horizontal,measurementCache:e.measurementCache,onChange:f,overscan:e.overscan??3,sticky:e.sticky?n=>{let r=t[n];return r!==void 0&&e.sticky(n,r)}:void 0}),a.style.position=`relative`,a.style.contain=`layout`}function h(){r||(r=!0,i.abort(),s?.dispose(),s=null,p())}return{get count(){return s?.count??0},get disposalSignal(){return i.signal},dispose:h,get disposed(){return r},invalidate(){r||s?.invalidate()},get isScrolling(){return s?.isScrolling??!1},get items(){return s?.items??[]},measure(e,t){r||s?.measure(e,t)},measureBatch(e){r||s?.measureBatch(e)},measureEl(e,t){return r?()=>{}:s?.measureEl(e,t)??(()=>{})},refresh(){r||s?.refresh()},get scrollOffset(){return s?.scrollOffset??0},scrollToBottom(e){r||s?.scrollToBottom(e)},scrollToIndex(e,t){r||s?.scrollToIndex(e,t)},scrollToOffset(e,t){r||s?.scrollToOffset(e,t)},scrollToTop(e){r||s?.scrollToTop(e)},setItems(n){if(r)return;if(t=n,n.length===0){s?.dispose(),s=null,p();return}if(!s){m();return}let i=n.length!==s.count;s.update({count:n.length}),i?e.getItemKey||s.invalidate():e.getItemKey?s.refresh():s.invalidate()},get stickyItems(){return s?.stickyItems??[]},[Symbol.dispose]:h,get totalSize(){return s?.totalSize??0}}}function y(e,t){let n=document.createElement(`div`);n.style.cssText=t.horizontal?`overflow: auto hidden; width: 100%; height: 100%;`:`overflow: hidden auto; width: 100%; height: 100%;`,t.containerClass&&(n.className=t.containerClass);let r=document.createElement(`div`);n.appendChild(r),e.appendChild(n);let i;try{i=v({...t,listElement:r,scrollElement:n})}catch(e){throw n.remove(),e}let a=i.dispose.bind(i);return Object.assign(i,{dispose(){a(),n.remove()},[Symbol.dispose](){a(),n.remove()}})}function b(e,t,n,r,i,a){return a===`start`?e:a===`end`?t-i:a===`center`?e-(i-n)/2:e>=r&&t<=r+i?null:e<r?e:t-i}function x(e,t){let n=c(t.rowCount),r=c(t.colCount),i=p(t.estimateRowSize,36),a=p(t.estimateColSize,36),o=d(t.overscanY,3),s=d(t.overscanX,3),u=t.onChange,g=t.onRangeChange,_=t.rowMeasurementCache??new Map,v=t.colMeasurementCache??new Map,y=0,x=0,S=0,C=0,w=[],T=[],E=!1,D=h(n,e=>_.get(e)??i(e),c(t.rowGap??0)),O=h(r,e=>v.get(e)??a(e),c(t.colGap??0)),k=!1;function A(e){let t=Number.isFinite(e)?e:0;return Math.min(Math.max(0,D.totalSize-S),Math.max(0,t))}function j(e){let t=Number.isFinite(e)?e:0;return Math.min(Math.max(0,O.totalSize-C),Math.max(0,t))}function M(){if(E)return;let e=D.totalSize,t=O.totalSize;if(n===0||r===0||S<=0||C<=0){D.prevStart===-1&&D.prevTotalSize===e&&O.prevTotalSize===t||(D.commitDedup(-1,-1),O.commitDedup(-1,-1),w=[],T=[],u?.({cols:[],rows:[],totalHeight:e,totalWidth:t}));return}let{renderEnd:i,renderStart:a}=D.computeRange(y,y+S,o.start,o.end),{renderEnd:c,renderStart:l}=O.computeRange(x,x+C,s.start,s.end),d=D.isDedupSame(a,i),f=O.isDedupSame(l,c);if(d&&f)return;D.commitDedup(a,i),O.commitDedup(l,c),(!d||!f)&&g&&g({firstCol:l,firstRow:a,lastCol:c,lastRow:i});let p=[];for(let e=a;e<=i;e++)p.push(D.itemAt(e));let m=[];for(let e=l;e<=c;e++)m.push(O.itemAt(e));w=p,T=m,u?.({cols:T,rows:w,totalHeight:e,totalWidth:t})}function N(e,t){if(!Number.isFinite(e))return!1;let r=Math.floor(e);if(r<0||r>=n)return!1;let i=l(t,-1);return i<=0||_.get(r)===i?!1:(_.set(r,i),D.markChanged(r),!0)}function P(e,t){if(!Number.isFinite(e))return!1;let n=Math.floor(e);if(n<0||n>=r)return!1;let i=l(t,-1);return i<=0||v.get(n)===i?!1:(v.set(n,i),O.markChanged(n),!0)}function F(){k||(k=!0,queueMicrotask(()=>{k=!1;let e=D.consumeMinChangedIndex();e!==1/0&&D.rebuildFrom(e);let t=O.consumeMinChangedIndex();t!==1/0&&O.rebuildFrom(t),M()}))}function I(e,t){E||N(e,t)&&F()}function L(e,t){E||P(e,t)&&F()}function R(e,t){if(E)return;let n=!1;for(let{index:t,size:r}of e)N(t,r)&&(n=!0);for(let{index:e,size:r}of t)P(e,r)&&(n=!0);n&&F()}function ee(e,t){return E?()=>{}:f(q.signal,t,t=>I(e,t.contentRect.height))}function z(e,t){return E?()=>{}:f(q.signal,t,t=>L(e,t.contentRect.width))}function B(){E||(_.clear(),v.clear(),D.rebuild(!0),O.rebuild(!0),M())}function V(){E||(D.rebuild(!0),O.rebuild(!0),M())}function H(e){if(E)return;let t=c(e);if(t===0)return;n+=t,D.setCount(n),D.rebuild(!0);let r=D.startAt(t),i=A(y+r);X.scrollTo(x,i,`auto`),y=i,M()}function U(e,t={}){if(E||n===0)return;let r=Math.max(0,Math.min(Math.floor(Number.isFinite(e)?e:0),n-1)),i=t.behavior??`auto`,a=D.startAt(r),o=D.sizeAt(r),s=b(a,a+o,o,y,S,t.rowAlign??`auto`);s!==null&&X.scrollTo(x,A(s),i)}function W(e,t={}){if(E||r===0)return;let n=Math.max(0,Math.min(Math.floor(Number.isFinite(e)?e:0),r-1)),i=t.behavior??`auto`,a=O.startAt(n),o=O.sizeAt(n),s=b(a,a+o,o,x,C,t.colAlign??`auto`);s!==null&&X.scrollTo(j(s),y,i)}function G(e,t,i={}){if(E||n===0||r===0)return;let a=Math.max(0,Math.min(Math.floor(Number.isFinite(e)?e:0),n-1)),o=Math.max(0,Math.min(Math.floor(Number.isFinite(t)?t:0),r-1)),s=i.behavior??`auto`,c=D.startAt(a),l=D.sizeAt(a),u=O.startAt(o),d=O.sizeAt(o),f=b(c,c+l,l,y,S,i.rowAlign??`auto`),p=b(u,u+d,d,x,C,i.colAlign??`auto`);f===null&&p===null||X.scrollTo(p===null?x:j(p),f===null?y:A(f),s)}function K(e){if(E)return;let t=!1,l=!1,u=!1;if(e.rowCount!==void 0){let r=c(e.rowCount);r!==n&&(n=r,D.setCount(n),t=!0)}if(e.colCount!==void 0){let t=c(e.colCount);t!==r&&(r=t,O.setCount(r),l=!0)}if(e.rowGap!==void 0){let n=c(e.rowGap);n!==D.gap&&(D.setGap(n),t=!0)}if(e.colGap!==void 0){let t=c(e.colGap);t!==O.gap&&(O.setGap(t),l=!0)}if(`estimateRowSize`in e&&(i=p(e.estimateRowSize,36),_.clear(),t=!0),`estimateColSize`in e&&(a=p(e.estimateColSize,36),v.clear(),l=!0),e.overscanY!==void 0){let t=d(e.overscanY,3);(t.start!==o.start||t.end!==o.end)&&(o=t,u=!0)}if(e.overscanX!==void 0){let t=d(e.overscanX,3);(t.start!==s.start||t.end!==s.end)&&(s=t,u=!0)}t&&D.rebuild(!0),l&&O.rebuild(!0),(t||l||u)&&M()}let q=new AbortController;function J(){E||(E=!0,q.abort(),X.detach())}function te(){y=A(X.y.readOffset()),x=j(X.x.readOffset()),M()}function Y(){S=X.y.readViewportSize(),C=X.x.readViewportSize(),M()}let X=m(e,te,Y);return D.rebuild(!0),O.rebuild(!0),S=X.y.readViewportSize(),C=X.x.readViewportSize(),t.initialScrollTop===void 0?t.initialScrollLeft!==void 0&&X.scrollTo(j(t.initialScrollLeft),0,`auto`):X.scrollTo(t.initialScrollLeft??0,A(t.initialScrollTop),`auto`),y=A(X.y.readOffset()),x=j(X.x.readOffset()),M(),{get cols(){return T},get disposalSignal(){return q.signal},dispose:J,get disposed(){return E},invalidate:B,measureBatch:R,measureColEl:z,measureColumn:L,measureRow:I,measureRowEl:ee,prependRows:H,refresh:V,get rows(){return w},get scrollLeft(){return x},scrollToCell:G,scrollToColumn:W,get scrollTop(){return y},scrollToRow:U,[Symbol.dispose]:J,get totalHeight(){return D.totalSize},get totalWidth(){return O.totalSize},update:K}}function S(e){let t=[];for(let n=0;n<e.length;n++){let r=e[n];t.push({isHeader:!0,itemIndex:-1,label:r.label,sectionIndex:n});for(let e=0;e<r.items.length;e++)t.push({isHeader:!1,item:r.items[e],itemIndex:e,sectionIndex:n})}return t}function C(e,t){let n=t.sections,r=S(n);function i(e){return typeof e==`function`?t=>{let i=r[t];return e(n[i.sectionIndex],i.sectionIndex)}:p(typeof e==`number`?e:void 0,36)}let a=i(t.estimateHeaderSize),o=typeof t.estimateItemSize==`function`?t.estimateItemSize:null,s=o?null:p(typeof t.estimateItemSize==`number`?t.estimateItemSize:void 0,36),c=t.getItemKey;function l(e){let t=r[e];return t?t.isHeader?a(e):o?o(t.item,t.itemIndex,t.sectionIndex):s(e):36}function u(e){let t=r[e];return t?t.isHeader?`__header_${t.sectionIndex}`:c?c(t.item,t.itemIndex,t.sectionIndex):e:e}let d=!1,f=[];function m(e){let t=[],n=[],i=null;for(let i of e.items){let e=r[i.index];e&&(e.isHeader?n.push({...i,label:e.label??``,sectionIndex:e.sectionIndex}):t.push({...i,data:e.item,itemIndex:e.itemIndex,sectionIndex:e.sectionIndex}))}if(e.stickyItems.length>0){let t=e.stickyItems[0],n=r[t.index];n?.isHeader&&(i={...t,label:n.label??``,sectionIndex:n.sectionIndex})}return f=t,{headers:n,items:t,stickyHeader:i,totalSize:e.totalSize}}let h=g(e,{count:r.length,estimateSize:l,getItemKey:u,horizontal:t.horizontal,measurementCache:t.measurementCache,onChange:e=>{let n=m(e);t.onChange?.(n)},onScrollEnd:t.onScrollEnd,onScrollingChange:t.onScrollingChange,overscan:t.overscan??3,scrollEndDelay:t.scrollEndDelay,sticky:e=>r[e]?.isHeader??!1}),_=new AbortController;function v(){d||(d=!0,_.abort(),h.dispose())}function y(e,t){if(e<0||e>=n.length)return-1;let r=0;for(let i=0;i<n.length;i++){if(i===e)return t===void 0?r:r+1+t;r+=1+n[i].items.length}return-1}return{get count(){return h.count},get disposalSignal(){return _.signal},dispose:v,get disposed(){return d},invalidate(){d||h.invalidate()},get isScrolling(){return h.isScrolling},get items(){return f},measure(e,t){d||h.measure(e,t)},measureBatch(e){d||h.measureBatch(e)},measureEl(e,t){return d?()=>{}:h.measureEl(e,t)},refresh(){d||h.refresh()},get scrollOffset(){return h.scrollOffset},scrollToBottom(e){d||h.scrollToBottom(e)},scrollToIndex(e,t){d||h.scrollToIndex(e,t)},scrollToItem(e,t,n={}){if(d)return;let r=y(e,t);r<0||h.scrollToIndex(r,n)},scrollToOffset(e,t){d||h.scrollToOffset(e,t)},scrollToSection(e,t={}){if(d)return;let n=y(e);n<0||h.scrollToIndex(n,t)},scrollToTop(e){d||h.scrollToTop(e)},get stickyItems(){return h.stickyItems},[Symbol.dispose]:v,get totalSize(){return h.totalSize},update(e,t){if(d)return;t&&(t.estimateHeaderSize!==void 0&&(a=i(t.estimateHeaderSize)),t.estimateItemSize!==void 0&&(o=typeof t.estimateItemSize==`function`?t.estimateItemSize:null,s=o?null:p(typeof t.estimateItemSize==`number`?t.estimateItemSize:void 0,36)),t.getItemKey!==void 0&&(c=t.getItemKey)),n=e;let l=r.length;r=S(n);let u={count:r.length};t?.measurementCache!==void 0&&(u.measurementCache=t.measurementCache),t?.overscan!==void 0&&(u.overscan=t.overscan),r.length!==l||t?.measurementCache!==void 0||t?.overscan!==void 0?h.update(u):h.refresh()}}}function w(t,n){let r=(0,e.signal)({headers:[],items:[],stickyHeader:null,totalSize:0}),i=C(t,{...n,onChange:e=>{r.value=e}});return new Proxy(i,{get(e,t,n){return t===`state`?r:Reflect.get(e,t,n)},getOwnPropertyDescriptor(e,t){return t===`state`?{configurable:!0,enumerable:!0,value:r}:Reflect.getOwnPropertyDescriptor(e,t)},has(e,t){return t===`state`||Reflect.has(e,t)},ownKeys(e){return[...Reflect.ownKeys(e),`state`]}})}function T(t,n){let r=(0,e.signal)({items:[],stickyItems:[],totalSize:0}),i=g(t,{...n,onChange:e=>{r.value=e}});return new Proxy(i,{get(e,t,n){return t===`state`?r:Reflect.get(e,t,n)},getOwnPropertyDescriptor(e,t){return t===`state`?{configurable:!0,enumerable:!0,value:r}:Reflect.getOwnPropertyDescriptor(e,t)},has(e,t){return t===`state`||Reflect.has(e,t)},ownKeys(e){return[...Reflect.ownKeys(e),`state`]}})}exports.DEFAULT_ESTIMATE_SIZE=o,exports.DEFAULT_OVERSCAN=s,exports.ScrollError=t,exports.ScrollRangeError=n,exports.createDomVirtualList=v,exports.createGridVirtualizer=x,exports.createGroupedVirtualizer=C,exports.createMeasurementCache=a,exports.createReactiveGroupedVirtualizer=w,exports.createReactiveVirtualizer=T,exports.createVirtualScroller=y,exports.createVirtualizer=g;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@vielzeug/ripple");var t=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},n=class extends t{},r=!globalThis.__SCROLL_PROD__;function i(e){r&&console.warn(`[@vielzeug/scroll] ${e}`)}function a(){return new Map}var o=36,s=3;function c(e,t=0){return Number.isFinite(e)?Math.max(0,Math.floor(e)):t}function l(e,t){return!Number.isFinite(e)||e<=0||e>1e7?t:e}function u(e){return typeof Window<`u`&&e instanceof Window||typeof e.innerHeight==`number`&&typeof e.document==`object`}function d(e,t){if(typeof e==`number`){let n=c(e,t);return{end:n,start:n}}return{end:c(e?.end??t),start:c(e?.start??t)}}function f(e,t,n){let r=new ResizeObserver(e=>{for(let t of e)n(t)});return r.observe(t),e.addEventListener(`abort`,()=>r.disconnect(),{once:!0}),()=>r.disconnect()}function p(e,t){if(typeof e==`function`)return n=>{try{return l(e(n),t)}catch(e){return i(`estimateSize threw for index ${n}: ${e}`),t}};let n=typeof e==`number`?l(e,t):t;return()=>n}function m(e,t,n){if(u(e))return e.addEventListener(`scroll`,t,{passive:!0}),e.addEventListener(`resize`,n,{passive:!0}),{detach(){e.removeEventListener(`scroll`,t),e.removeEventListener(`resize`,n)},scrollTo:(t,n,r)=>e.scrollTo({behavior:r,left:t,top:n}),x:{readOffset:()=>e.scrollX,readViewportSize:()=>e.innerWidth,writeOffset:(t,n)=>e.scrollTo({behavior:n,left:t})},y:{readOffset:()=>e.scrollY,readViewportSize:()=>e.innerHeight,writeOffset:(t,n)=>e.scrollTo({behavior:n,top:t})}};let r=new ResizeObserver(n);return e.addEventListener(`scroll`,t,{passive:!0}),r.observe(e),{detach(){e.removeEventListener(`scroll`,t),r.disconnect()},scrollTo:(t,n,r)=>e.scrollTo({behavior:r,left:t,top:n}),x:{readOffset:()=>e.scrollLeft,readViewportSize:()=>e.clientWidth,writeOffset:(t,n)=>e.scrollTo({behavior:n,left:t})},y:{readOffset:()=>e.scrollTop,readViewportSize:()=>e.clientHeight,writeOffset:(t,n)=>e.scrollTo({behavior:n,top:t})}}}function h(e,t,n){let r=e,i=n,a=t,o=[],s=0,c=-1,l=-1,u=-1,d=!1,f=1/0;function p(e){return o[e]??0}function m(e){return p(e)+a(e)}function h(e=!1){e&&(c=-1,l=-1,u=-1);let t=[0],n=0;for(let e=0;e<r;e++)n+=a(e)+(e<r-1?i:0),t.push(n);o=t,s=n}function g(e){if(!Number.isFinite(e)||e>=r)return;c=-1,l=-1,u=-1;let t=o[e]??0;for(let n=e;n<r;n++)o[n]=t,t+=a(n)+(n<r-1?i:0);o[r]=t,s=t}function _(e){let t=0,n=r-1;for(;t<n;){let r=t+n>>1;m(r)<=e?t=r+1:n=r}return t}function v(e,t){let n=t,i=r-1;for(;n<i;){let t=n+i+1>>1;p(t)<e?n=t:i=t-1}return n}function y(e,t,n,i){let a=_(e),o=v(t,a);return{renderEnd:Math.min(r-1,o+i),renderStart:Math.max(0,a-n)}}function b(e,t){return e===c&&t===l&&s===u}function x(e,t){c=e,l=t,u=s}function S(){c=-1,l=-1,u=-1}function C(e){d||(d=!0,queueMicrotask(()=>{d=!1;let t=f;f=1/0,g(t),e()}))}function w(e){e<f&&(f=e)}function T(e){let t=p(e),n=a(e);return{end:t+n,index:e,size:n,start:t}}return{commitDedup:x,computeRange:y,consumeMinChangedIndex(){let e=f;return f=1/0,e},get count(){return r},endAt:m,findFirst:_,findLast:v,get gap(){return i},isDedupSame:b,itemAt:T,markChanged:w,get minChangedIndex(){return f},get pendingBuild(){return d},get prevEnd(){return l},get prevStart(){return c},get prevTotalSize(){return u},rebuild:h,rebuildFrom:g,resetDedup:S,scheduleBuild:C,setCount(e){r=e},setGap(e){i=e},sizeAt(e){return a(e)},startAt:p,get totalSize(){return s}}}function g(e,t){let n=!!t.horizontal,r=e=>e,i=c(t.count),a=p(t.estimateSize,36),o=c(t.gap??0),s=t.getItemKey??r,u=d(t.overscan,3),g=t.sticky??null,_=t.onChange,v=t.onScrollEnd,y=t.onScrollingChange,b=typeof t.scrollEndDelay==`number`&&t.scrollEndDelay>=0?t.scrollEndDelay:150,x=!1,S=null,C=`onscrollend`in e;function w(){x&&(x=!1,y?.(!1),v?.(M))}function T(){x||(x=!0,y?.(!0)),C||(S!==null&&clearTimeout(S),S=setTimeout(w,b))}let E=t.measurementCache??new Map,D=[],O=[],k=h(i,e=>E.get(s(e))??a(e),o),A=-1,j=0,M=0,N=0,P=-1,F=!1;function I(e){let t=Number.isFinite(e)?e:0,n=Math.max(0,k.totalSize-N);return Math.min(n,Math.max(0,t))}function L(){if(!g||i===0||M<=0)return[];let e=-1,t=0,n=i-1;for(;t<=n;){let r=t+n>>1;k.startAt(r)<M?(e=r,t=r+1):n=r-1}if(e<0)return[];let r=-1;for(let t=e;t>=0;t--)if(g(t)){r=t;break}if(r===-1)return[];let a=k.sizeAt(r),o=1/0;for(let e=r+1;e<i;e++)if(g(e)){o=k.startAt(e);break}let s=Math.min(M,o-a);return[{end:s+a,index:r,size:a,start:s}]}function R(){if(F)return;if(i===0||N<=0){(k.prevStart!==-1||k.prevTotalSize!==k.totalSize)&&(k.commitDedup(-1,-1),D=[],O=[],_&&_({items:D,stickyItems:O,totalSize:k.totalSize}));return}let{renderEnd:e,renderStart:t}=k.computeRange(M,M+N,u.start,u.end),n=g!==null&&M!==P;if(k.isDedupSame(t,e)&&!n)return;k.commitDedup(t,e),P=M;let r=[];for(let n=t;n<=e;n++)r.push(k.itemAt(n));D=r,O=g?L():[],_&&_({items:D,stickyItems:O,totalSize:k.totalSize})}function ee(e,t){if(!Number.isFinite(e))return!1;let n=Math.floor(e);if(n<0||n>=i)return!1;let r=l(t,-1);if(r<=0)return!1;let a=s(n);return E.get(a)===r?!1:(E.set(a,r),k.markChanged(n),!0)}function z(){if(M<=0||i===0){A=-1;return}A=k.findFirst(M),j=M-k.startAt(A)}function B(){if(A<0)return;let e=k.startAt(A)+j-M;if(A=-1,Math.abs(e)>=1){let t=I(M+e);$.writeOffset(t,`auto`),M=t}}function V(e){if(F)return;let t=!1;for(let{index:n,size:r}of e)ee(n,r)&&(t=!0);t&&(k.pendingBuild||z(),k.scheduleBuild(()=>{B(),R()}))}function H(e,t){V([{index:e,size:t}])}function U(e,t){return F?()=>{}:f(Z.signal,t,t=>{H(e,n?t.contentRect.width:t.contentRect.height)})}function W(e){let t=!1,n=!1;if(e.count!==void 0){let n=c(e.count);n!==i&&(i=n,k.setCount(i),t=!0)}if(Object.hasOwn(e,`estimateSize`)&&(a=p(e.estimateSize,36),E.clear(),t=!0),e.gap!==void 0){let n=c(e.gap);n!==o&&(o=n,k.setGap(o),t=!0)}if(Object.hasOwn(e,`getItemKey`)){let n=e.getItemKey??r;n!==s&&(s=n,E.clear(),t=!0)}if(Object.hasOwn(e,`measurementCache`)&&e.measurementCache!==void 0&&(E=e.measurementCache,t=!0),e.overscan!==void 0){let t=d(e.overscan,3);(t.start!==u.start||t.end!==u.end)&&(u=t,n=!0)}if(Object.hasOwn(e,`sticky`)){let t=e.sticky??null;t!==g&&(g=t,k.resetDedup()),n=!0}t&&(z(),k.rebuild(!0),B()),(n||t)&&R()}function G(){F||(E.clear(),k.rebuild(!0),R())}function K(){F||(k.rebuild(!0),R())}function q(e){if(F)return;let t=c(e);if(t===0)return;i+=t,k.setCount(i),k.rebuild(!0);let n=k.startAt(t),r=I(M+n);$.writeOffset(r,`auto`),M=r,R()}function J(e){F||W(e)}function te(t,n={}){if(F||i<=0)return;let r=Math.max(0,Math.min(Number.isFinite(t)?Math.floor(t):0,i-1)),a=n.align??`auto`,o=n.behavior??`auto`,s=k.startAt(r),c=k.sizeAt(r),l=s+c,u;if(a===`start`)u=s;else if(a===`end`)u=l-N;else if(a===`center`)u=s-(N-c)/2;else{let e=M,t=e+N;if(s>=e&&l<=t){n.onComplete&&queueMicrotask(n.onComplete);return}u=s<e?s:l-N}$.writeOffset(I(u),o),n.onComplete&&(o===`smooth`&&`onscrollend`in e?e.addEventListener(`scrollend`,n.onComplete,{once:!0}):queueMicrotask(n.onComplete))}function Y(e,t={}){F||$.writeOffset(I(e),t.behavior??`auto`)}function X(e={}){Y(0,e)}function ne(e={}){Y(Math.max(0,k.totalSize-N),e)}function re(e=0){return k.totalSize-(M+N)<=e}let Z=new AbortController;function ie(){F||(F=!0,Z.abort(),S!==null&&(clearTimeout(S),S=null),C&&e.removeEventListener(`scrollend`,w),Q.detach())}function ae(){M=I($.readOffset()),T(),R()}function oe(){N=$.readViewportSize(),R()}let Q=m(e,ae,oe),$=n?Q.x:Q.y;return C&&e.addEventListener(`scrollend`,w,{passive:!0}),k.rebuild(!0),t.initialOffset!==void 0&&$.writeOffset(I(t.initialOffset),`auto`),M=I($.readOffset()),N=$.readViewportSize(),R(),{get count(){return i},get disposalSignal(){return Z.signal},dispose:ie,get disposed(){return F},invalidate:G,isAtEnd:re,get isScrolling(){return x},get items(){return D},measure:H,measureBatch:V,measureEl:U,prepend:q,refresh:K,get scrollOffset(){return M},scrollToBottom:ne,scrollToIndex:te,scrollToOffset:Y,scrollToTop:X,get stickyItems(){return O},[Symbol.dispose]:ie,get totalSize(){return k.totalSize},update:J}}function _(){let e=new Map,t=new Map,n=[],r=!1;return{acquire(i,a){if(!r)return a();let o=e.get(i);if(o)return t.set(i,o),o;let s=n.pop()??a();return t.set(i,s),s},beginCycle(){t=new Map,r=!0},clear(){for(let t of e.values())t.remove();e.clear(),t.clear(),n.length=0,r=!1},endCycle(){if(r){r=!1;for(let[r,i]of e)t.has(r)||(i.remove(),n.push(i));e=t}}}}function v(e){let t=[],r=!1,i=new AbortController,a=e.listElement,o=_(),s=null;function c(n){let r=t[n];return r!==void 0&&e.getItemKey?e.getItemKey(n,r):n}function l(n){if(e.estimateSize===void 0)return 36;if(typeof e.estimateSize==`number`)return e.estimateSize;let r=t[n];return r===void 0?36:e.estimateSize(n,r)}function u(t){e.horizontal?(a.style.height=``,a.style.width=`${t}px`):(a.style.height=`${t}px`,a.style.width=``)}function d(e){let r=t[e.index];if(r===void 0)throw new n(`toRenderItem: index ${e.index} is out of range (currentItems.length=${t.length})`);return{...e,data:r}}function f(t){u(t.totalSize),o.beginCycle();try{e.render({items:t.items.map(d),listEl:a,recycle:(e,t)=>o.acquire(e,t),stickyItems:t.stickyItems.map(d),totalSize:t.totalSize})}finally{o.endCycle()}}function p(){o.clear(),e.clear?e.clear(a):a.textContent=``,a.style.height=``,a.style.width=``,a.style.position=``,a.style.contain=``}function m(){let t=e.stickToBottom;return t?t===!0?{enabled:!0,threshold:48}:{enabled:t.enabled??!0,threshold:t.threshold??48}:{enabled:!1,threshold:48}}function h(){let n=g(e.scrollElement,{count:t.length,estimateSize:l,gap:e.gap,getItemKey:c,horizontal:e.horizontal,measurementCache:e.measurementCache,onChange:f,overscan:e.overscan??3,sticky:e.sticky?n=>{let r=t[n];return r!==void 0&&e.sticky(n,r)}:void 0});return s=n,a.style.position=`relative`,a.style.contain=`layout`,n}function v(){r||(r=!0,i.abort(),s?.dispose(),s=null,p())}return{get count(){return s?.count??0},get disposalSignal(){return i.signal},dispose:v,get disposed(){return r},invalidate(){r||s?.invalidate()},isAtEnd(e){return s?.isAtEnd(e)??!0},get isScrolling(){return s?.isScrolling??!1},get items(){return s?.items??[]},measure(e,t){r||s?.measure(e,t)},measureBatch(e){r||s?.measureBatch(e)},measureEl(e,t){return r?()=>{}:s?.measureEl(e,t)??(()=>{})},refresh(){r||s?.refresh()},get scrollOffset(){return s?.scrollOffset??0},scrollToBottom(e){r||s?.scrollToBottom(e)},scrollToIndex(e,t){r||s?.scrollToIndex(e,t)},scrollToOffset(e,t){r||s?.scrollToOffset(e,t)},scrollToTop(e){r||s?.scrollToTop(e)},setItems(n){if(r)return;let i=m(),a=i.enabled&&(s?.isAtEnd(i.threshold)??!0);if(t=n,n.length===0){s?.dispose(),s=null,p();return}if(!s){let e=h();a&&e.scrollToBottom();return}let o=n.length!==s.count;s.update({count:n.length}),o?e.getItemKey||s.invalidate():e.getItemKey?s.refresh():s.invalidate(),a&&s.scrollToBottom()},get stickyItems(){return s?.stickyItems??[]},[Symbol.dispose]:v,get totalSize(){return s?.totalSize??0}}}function y(e,t){let n=document.createElement(`div`);n.style.cssText=t.horizontal?`overflow: auto hidden; width: 100%; height: 100%;`:`overflow: hidden auto; width: 100%; height: 100%;`,t.containerClass&&(n.className=t.containerClass);let r=document.createElement(`div`);n.appendChild(r),e.appendChild(n);let i;try{i=v({...t,listElement:r,scrollElement:n})}catch(e){throw n.remove(),e}let a=i.dispose.bind(i);return Object.assign(i,{dispose(){a(),n.remove()},[Symbol.dispose](){a(),n.remove()}})}function b(e,t,n,r,i,a){return a===`start`?e:a===`end`?t-i:a===`center`?e-(i-n)/2:e>=r&&t<=r+i?null:e<r?e:t-i}function x(e,t){let n=c(t.rowCount),r=c(t.colCount),i=p(t.estimateRowSize,36),a=p(t.estimateColSize,36),o=d(t.overscanY,3),s=d(t.overscanX,3),u=t.onChange,g=t.onRangeChange,_=t.rowMeasurementCache??new Map,v=t.colMeasurementCache??new Map,y=0,x=0,S=0,C=0,w=[],T=[],E=!1,D=h(n,e=>_.get(e)??i(e),c(t.rowGap??0)),O=h(r,e=>v.get(e)??a(e),c(t.colGap??0)),k=!1;function A(e){let t=Number.isFinite(e)?e:0;return Math.min(Math.max(0,D.totalSize-S),Math.max(0,t))}function j(e){let t=Number.isFinite(e)?e:0;return Math.min(Math.max(0,O.totalSize-C),Math.max(0,t))}function M(){if(E)return;let e=D.totalSize,t=O.totalSize;if(n===0||r===0||S<=0||C<=0){D.prevStart===-1&&D.prevTotalSize===e&&O.prevTotalSize===t||(D.commitDedup(-1,-1),O.commitDedup(-1,-1),w=[],T=[],u?.({cols:[],rows:[],totalHeight:e,totalWidth:t}));return}let{renderEnd:i,renderStart:a}=D.computeRange(y,y+S,o.start,o.end),{renderEnd:c,renderStart:l}=O.computeRange(x,x+C,s.start,s.end),d=D.isDedupSame(a,i),f=O.isDedupSame(l,c);if(d&&f)return;D.commitDedup(a,i),O.commitDedup(l,c),(!d||!f)&&g&&g({firstCol:l,firstRow:a,lastCol:c,lastRow:i});let p=[];for(let e=a;e<=i;e++)p.push(D.itemAt(e));let m=[];for(let e=l;e<=c;e++)m.push(O.itemAt(e));w=p,T=m,u?.({cols:T,rows:w,totalHeight:e,totalWidth:t})}function N(e,t){if(!Number.isFinite(e))return!1;let r=Math.floor(e);if(r<0||r>=n)return!1;let i=l(t,-1);return i<=0||_.get(r)===i?!1:(_.set(r,i),D.markChanged(r),!0)}function P(e,t){if(!Number.isFinite(e))return!1;let n=Math.floor(e);if(n<0||n>=r)return!1;let i=l(t,-1);return i<=0||v.get(n)===i?!1:(v.set(n,i),O.markChanged(n),!0)}function F(){k||(k=!0,queueMicrotask(()=>{k=!1;let e=D.consumeMinChangedIndex();e!==1/0&&D.rebuildFrom(e);let t=O.consumeMinChangedIndex();t!==1/0&&O.rebuildFrom(t),M()}))}function I(e,t){E||N(e,t)&&F()}function L(e,t){E||P(e,t)&&F()}function R(e,t){if(E)return;let n=!1;for(let{index:t,size:r}of e)N(t,r)&&(n=!0);for(let{index:e,size:r}of t)P(e,r)&&(n=!0);n&&F()}function ee(e,t){return E?()=>{}:f(q.signal,t,t=>I(e,t.contentRect.height))}function z(e,t){return E?()=>{}:f(q.signal,t,t=>L(e,t.contentRect.width))}function B(){E||(_.clear(),v.clear(),D.rebuild(!0),O.rebuild(!0),M())}function V(){E||(D.rebuild(!0),O.rebuild(!0),M())}function H(e){if(E)return;let t=c(e);if(t===0)return;n+=t,D.setCount(n),D.rebuild(!0);let r=D.startAt(t),i=A(y+r);X.scrollTo(x,i,`auto`),y=i,M()}function U(e,t={}){if(E||n===0)return;let r=Math.max(0,Math.min(Math.floor(Number.isFinite(e)?e:0),n-1)),i=t.behavior??`auto`,a=D.startAt(r),o=D.sizeAt(r),s=b(a,a+o,o,y,S,t.rowAlign??`auto`);s!==null&&X.scrollTo(x,A(s),i)}function W(e,t={}){if(E||r===0)return;let n=Math.max(0,Math.min(Math.floor(Number.isFinite(e)?e:0),r-1)),i=t.behavior??`auto`,a=O.startAt(n),o=O.sizeAt(n),s=b(a,a+o,o,x,C,t.colAlign??`auto`);s!==null&&X.scrollTo(j(s),y,i)}function G(e,t,i={}){if(E||n===0||r===0)return;let a=Math.max(0,Math.min(Math.floor(Number.isFinite(e)?e:0),n-1)),o=Math.max(0,Math.min(Math.floor(Number.isFinite(t)?t:0),r-1)),s=i.behavior??`auto`,c=D.startAt(a),l=D.sizeAt(a),u=O.startAt(o),d=O.sizeAt(o),f=b(c,c+l,l,y,S,i.rowAlign??`auto`),p=b(u,u+d,d,x,C,i.colAlign??`auto`);f===null&&p===null||X.scrollTo(p===null?x:j(p),f===null?y:A(f),s)}function K(e){if(E)return;let t=!1,l=!1,u=!1;if(e.rowCount!==void 0){let r=c(e.rowCount);r!==n&&(n=r,D.setCount(n),t=!0)}if(e.colCount!==void 0){let t=c(e.colCount);t!==r&&(r=t,O.setCount(r),l=!0)}if(e.rowGap!==void 0){let n=c(e.rowGap);n!==D.gap&&(D.setGap(n),t=!0)}if(e.colGap!==void 0){let t=c(e.colGap);t!==O.gap&&(O.setGap(t),l=!0)}if(`estimateRowSize`in e&&(i=p(e.estimateRowSize,36),_.clear(),t=!0),`estimateColSize`in e&&(a=p(e.estimateColSize,36),v.clear(),l=!0),e.overscanY!==void 0){let t=d(e.overscanY,3);(t.start!==o.start||t.end!==o.end)&&(o=t,u=!0)}if(e.overscanX!==void 0){let t=d(e.overscanX,3);(t.start!==s.start||t.end!==s.end)&&(s=t,u=!0)}t&&D.rebuild(!0),l&&O.rebuild(!0),(t||l||u)&&M()}let q=new AbortController;function J(){E||(E=!0,q.abort(),X.detach())}function te(){y=A(X.y.readOffset()),x=j(X.x.readOffset()),M()}function Y(){S=X.y.readViewportSize(),C=X.x.readViewportSize(),M()}let X=m(e,te,Y);return D.rebuild(!0),O.rebuild(!0),S=X.y.readViewportSize(),C=X.x.readViewportSize(),t.initialScrollTop===void 0?t.initialScrollLeft!==void 0&&X.scrollTo(j(t.initialScrollLeft),0,`auto`):X.scrollTo(t.initialScrollLeft??0,A(t.initialScrollTop),`auto`),y=A(X.y.readOffset()),x=j(X.x.readOffset()),M(),{get cols(){return T},get disposalSignal(){return q.signal},dispose:J,get disposed(){return E},invalidate:B,measureBatch:R,measureColEl:z,measureColumn:L,measureRow:I,measureRowEl:ee,prependRows:H,refresh:V,get rows(){return w},get scrollLeft(){return x},scrollToCell:G,scrollToColumn:W,get scrollTop(){return y},scrollToRow:U,[Symbol.dispose]:J,get totalHeight(){return D.totalSize},get totalWidth(){return O.totalSize},update:K}}function S(e){let t=[];for(let n=0;n<e.length;n++){let r=e[n];t.push({isHeader:!0,itemIndex:-1,label:r.label,sectionIndex:n});for(let e=0;e<r.items.length;e++)t.push({isHeader:!1,item:r.items[e],itemIndex:e,sectionIndex:n})}return t}function C(e,t){let n=t.sections,r=S(n);function i(e){return typeof e==`function`?t=>{let i=r[t];return e(n[i.sectionIndex],i.sectionIndex)}:p(typeof e==`number`?e:void 0,36)}let a=i(t.estimateHeaderSize),o=typeof t.estimateItemSize==`function`?t.estimateItemSize:null,s=o?null:p(typeof t.estimateItemSize==`number`?t.estimateItemSize:void 0,36),c=t.getItemKey;function l(e){let t=r[e];return t?t.isHeader?a(e):o?o(t.item,t.itemIndex,t.sectionIndex):s(e):36}function u(e){let t=r[e];return t?t.isHeader?`__header_${t.sectionIndex}`:c?c(t.item,t.itemIndex,t.sectionIndex):e:e}let d=!1,f=[];function m(e){let t=[],n=[],i=null;for(let i of e.items){let e=r[i.index];e&&(e.isHeader?n.push({...i,label:e.label??``,sectionIndex:e.sectionIndex}):t.push({...i,data:e.item,itemIndex:e.itemIndex,sectionIndex:e.sectionIndex}))}if(e.stickyItems.length>0){let t=e.stickyItems[0],n=r[t.index];n?.isHeader&&(i={...t,label:n.label??``,sectionIndex:n.sectionIndex})}return f=t,{headers:n,items:t,stickyHeader:i,totalSize:e.totalSize}}let h=g(e,{count:r.length,estimateSize:l,getItemKey:u,horizontal:t.horizontal,measurementCache:t.measurementCache,onChange:e=>{let n=m(e);t.onChange?.(n)},onScrollEnd:t.onScrollEnd,onScrollingChange:t.onScrollingChange,overscan:t.overscan??3,scrollEndDelay:t.scrollEndDelay,sticky:e=>r[e]?.isHeader??!1}),_=new AbortController;function v(){d||(d=!0,_.abort(),h.dispose())}function y(e,t){if(e<0||e>=n.length)return-1;let r=0;for(let i=0;i<n.length;i++){if(i===e)return t===void 0?r:r+1+t;r+=1+n[i].items.length}return-1}return{get count(){return h.count},get disposalSignal(){return _.signal},dispose:v,get disposed(){return d},invalidate(){d||h.invalidate()},get isScrolling(){return h.isScrolling},get items(){return f},measure(e,t){d||h.measure(e,t)},measureBatch(e){d||h.measureBatch(e)},measureEl(e,t){return d?()=>{}:h.measureEl(e,t)},refresh(){d||h.refresh()},get scrollOffset(){return h.scrollOffset},scrollToBottom(e){d||h.scrollToBottom(e)},scrollToIndex(e,t){d||h.scrollToIndex(e,t)},scrollToItem(e,t,n={}){if(d)return;let r=y(e,t);r<0||h.scrollToIndex(r,n)},scrollToOffset(e,t){d||h.scrollToOffset(e,t)},scrollToSection(e,t={}){if(d)return;let n=y(e);n<0||h.scrollToIndex(n,t)},scrollToTop(e){d||h.scrollToTop(e)},get stickyItems(){return h.stickyItems},[Symbol.dispose]:v,get totalSize(){return h.totalSize},update(e,t){if(d)return;t&&(t.estimateHeaderSize!==void 0&&(a=i(t.estimateHeaderSize)),t.estimateItemSize!==void 0&&(o=typeof t.estimateItemSize==`function`?t.estimateItemSize:null,s=o?null:p(typeof t.estimateItemSize==`number`?t.estimateItemSize:void 0,36)),t.getItemKey!==void 0&&(c=t.getItemKey)),n=e;let l=r.length;r=S(n);let u={count:r.length};t?.measurementCache!==void 0&&(u.measurementCache=t.measurementCache),t?.overscan!==void 0&&(u.overscan=t.overscan),r.length!==l||t?.measurementCache!==void 0||t?.overscan!==void 0?h.update(u):h.refresh()}}}function w(t,n){let r=(0,e.signal)({headers:[],items:[],stickyHeader:null,totalSize:0}),i=C(t,{...n,onChange:e=>{r.value=e}});return new Proxy(i,{get(e,t,n){return t===`state`?r:Reflect.get(e,t,n)},getOwnPropertyDescriptor(e,t){return t===`state`?{configurable:!0,enumerable:!0,value:r}:Reflect.getOwnPropertyDescriptor(e,t)},has(e,t){return t===`state`||Reflect.has(e,t)},ownKeys(e){return[...Reflect.ownKeys(e),`state`]}})}function T(t,n){let r=(0,e.signal)({items:[],stickyItems:[],totalSize:0}),i=g(t,{...n,onChange:e=>{r.value=e}});return new Proxy(i,{get(e,t,n){return t===`state`?r:Reflect.get(e,t,n)},getOwnPropertyDescriptor(e,t){return t===`state`?{configurable:!0,enumerable:!0,value:r}:Reflect.getOwnPropertyDescriptor(e,t)},has(e,t){return t===`state`||Reflect.has(e,t)},ownKeys(e){return[...Reflect.ownKeys(e),`state`]}})}exports.DEFAULT_ESTIMATE_SIZE=o,exports.DEFAULT_OVERSCAN=s,exports.ScrollError=t,exports.ScrollRangeError=n,exports.createDomVirtualList=v,exports.createGridVirtualizer=x,exports.createGroupedVirtualizer=C,exports.createMeasurementCache=a,exports.createReactiveGroupedVirtualizer=w,exports.createReactiveVirtualizer=T,exports.createVirtualScroller=y,exports.createVirtualizer=g;
|
|
2
2
|
//# sourceMappingURL=scroll.cjs.map
|