@pdanpdan/virtual-scroll 0.3.0 → 0.4.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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["scrollTimeout: ReturnType<typeof setTimeout> | undefined","lastItems: T[]","align: ScrollAlignment | ScrollAlignmentOptions | undefined","behavior: 'auto' | 'smooth' | undefined","scrollOptions: ScrollToOptions","items: RenderedItem<T>[]","prevStickyIdx: number | undefined","nextStickyIdx: number | undefined","correctionOptions: ScrollToIndexOptions","resizeObserver: ResizeObserver | null","cleanup: (() => void) | undefined"],"sources":["../src/utils/fenwick-tree.ts","../src/utils/scroll.ts","../src/composables/useVirtualScroll.ts","../src/components/VirtualScroll.vue","../src/components/VirtualScroll.vue"],"sourcesContent":["/**\n * Fenwick Tree (Binary Indexed Tree) implementation for efficient\n * prefix sum calculations and updates.\n */\nexport class FenwickTree {\n private tree: Float64Array;\n private values: Float64Array;\n\n constructor(size: number) {\n this.tree = new Float64Array(size + 1);\n this.values = new Float64Array(size);\n }\n\n /**\n * Update the value at a specific index and propagate changes.\n * @param index 0-based index\n * @param delta The change in value (new value - old value)\n */\n update(index: number, delta: number): void {\n if (index < 0 || index >= this.values.length) {\n return;\n }\n this.values[ index ] = this.values[ index ]! + delta;\n\n index++; // 1-based index\n while (index < this.tree.length) {\n this.tree[ index ] = this.tree[ index ]! + delta;\n index += index & -index;\n }\n }\n\n /**\n * Get the prefix sum up to a specific index (exclusive).\n * @param index 0-based index. query(n) returns sum of values from 0 to n-1.\n * @returns Sum of values in range [0, index)\n */\n query(index: number): number {\n let sum = 0;\n while (index > 0) {\n sum += this.tree[ index ] || 0;\n index -= index & -index;\n }\n return sum;\n }\n\n /**\n * Set the individual value at an index without updating the tree.\n * Call rebuild() after multiple sets to update the tree efficiently.\n */\n set(index: number, value: number): void {\n if (index < 0 || index >= this.values.length) {\n return;\n }\n this.values[ index ] = value;\n }\n\n /**\n * Get the number of items in the tree.\n */\n get length(): number {\n return this.values.length;\n }\n\n /**\n * Get the individual value at an index.\n */\n get(index: number): number {\n return this.values[ index ] || 0;\n }\n\n /**\n * Get the underlying values array.\n */\n getValues(): Readonly<Float64Array> {\n return this.values;\n }\n\n /**\n * Find the largest index such that the prefix sum is less than or equal to the given value.\n * Useful for finding which item is at a specific scroll offset.\n * @param value The prefix sum value to search for\n * @returns The 0-based index\n */\n findLowerBound(value: number): number {\n let index = 0;\n const len = this.tree.length;\n let power = 1 << Math.floor(Math.log2(len - 1));\n\n while (power > 0) {\n const nextIndex = index + power;\n if (nextIndex < len) {\n const treeVal = this.tree[ nextIndex ] || 0;\n if (treeVal <= value) {\n index = nextIndex;\n value -= treeVal;\n }\n }\n power >>= 1;\n }\n return index;\n }\n\n /**\n * Rebuild the entire tree from the current values array in O(N).\n * Useful after bulk updates to the values array.\n */\n rebuild(): void {\n this.tree.fill(0);\n for (let i = 0; i < this.values.length; i++) {\n this.tree[ i + 1 ] = this.values[ i ] || 0;\n }\n for (let i = 1; i < this.tree.length; i++) {\n const j = i + (i & -i);\n if (j < this.tree.length) {\n this.tree[ j ] = this.tree[ j ]! + this.tree[ i ]!;\n }\n }\n }\n\n /**\n * Resize the tree while preserving existing values.\n * @param size New size of the tree\n */\n resize(size: number): void {\n if (size === this.values.length) {\n return;\n }\n const newValues = new Float64Array(size);\n newValues.set(this.values.subarray(0, Math.min(size, this.values.length)));\n\n this.values = newValues;\n this.tree = new Float64Array(size + 1);\n this.rebuild();\n }\n\n /**\n * Shift values by a given offset and rebuild the tree.\n * Useful when items are prepended to the list.\n * @param offset Number of positions to shift (positive for prepending)\n */\n shift(offset: number): void {\n if (offset === 0) {\n return;\n }\n const size = this.values.length;\n const newValues = new Float64Array(size);\n if (offset > 0) {\n newValues.set(this.values.subarray(0, Math.min(size - offset, this.values.length)), offset);\n } else {\n newValues.set(this.values.subarray(-offset));\n }\n this.values = newValues;\n this.rebuild();\n }\n}\n","import type { ScrollDirection, ScrollToIndexOptions } from '../composables/useVirtualScroll';\n\n/**\n * Checks if the container has a bounding client rect method.\n *\n * @param container - The container element or window to check.\n * @returns True if the container is an HTMLElement with getBoundingClientRect.\n */\nexport function isElement(container: HTMLElement | Window | null | undefined): container is HTMLElement {\n return !!container && 'getBoundingClientRect' in container;\n}\n\n/**\n * Checks if the target is an element with scroll properties.\n *\n * @param target - The event target to check.\n * @returns True if the target is an HTMLElement with scroll properties.\n */\nexport function isScrollableElement(target: EventTarget | null): target is HTMLElement {\n return !!target && 'scrollLeft' in target;\n}\n\n/**\n * Helper to determine if an options argument is the full ScrollToIndexOptions object.\n *\n * @param options - The options object to check.\n * @returns True if the options object contains scroll-to-index specific properties.\n */\nexport function isScrollToIndexOptions(options: unknown): options is ScrollToIndexOptions {\n return typeof options === 'object' && options !== null && ('align' in options || 'behavior' in options || 'isCorrection' in options);\n}\n\n/**\n * Extracts the horizontal padding from a padding value or object.\n *\n * @param p - The padding value (number or object with x/y).\n * @param direction - The current scroll direction.\n * @returns The horizontal padding in pixels.\n */\nexport function getPaddingX(p: number | { x?: number; y?: number; } | undefined, direction?: ScrollDirection) {\n if (typeof p === 'object' && p !== null) {\n return p.x || 0;\n }\n return direction === 'horizontal' ? (p || 0) : 0;\n}\n\n/**\n * Extracts the vertical padding from a padding value or object.\n *\n * @param p - The padding value (number or object with x/y).\n * @param direction - The current scroll direction.\n * @returns The vertical padding in pixels.\n */\nexport function getPaddingY(p: number | { x?: number; y?: number; } | undefined, direction?: ScrollDirection) {\n if (typeof p === 'object' && p !== null) {\n return p.y || 0;\n }\n return (direction === 'vertical' || direction === 'both') ? (p || 0) : 0;\n}\n","import type { Ref } from 'vue';\n\n/* global ScrollToOptions */\nimport { computed, getCurrentInstance, nextTick, onMounted, onUnmounted, reactive, ref, watch } from 'vue';\n\nimport { FenwickTree } from '../utils/fenwick-tree';\nimport { getPaddingX, getPaddingY, isElement, isScrollableElement, isScrollToIndexOptions } from '../utils/scroll';\n\nexport const DEFAULT_ITEM_SIZE = 40;\nexport const DEFAULT_COLUMN_WIDTH = 100;\nexport const DEFAULT_BUFFER = 5;\n\nexport type ScrollDirection = 'vertical' | 'horizontal' | 'both';\nexport type ScrollAlignment = 'start' | 'center' | 'end' | 'auto';\n\n/** Options for scroll alignment in a single axis or both axes. */\nexport interface ScrollAlignmentOptions {\n /** Alignment on the X axis. */\n x?: ScrollAlignment;\n /** Alignment on the Y axis. */\n y?: ScrollAlignment;\n}\n\n/** Options for the scrollToIndex method. */\nexport interface ScrollToIndexOptions {\n /** Where to align the item in the viewport. */\n align?: ScrollAlignment | ScrollAlignmentOptions;\n /** Scroll behavior. */\n behavior?: 'auto' | 'smooth';\n /** Internal flag for recursive correction calls. */\n isCorrection?: boolean;\n}\n\n/** Configuration properties for the useVirtualScroll composable. */\nexport interface VirtualScrollProps<T = unknown> {\n /** Array of items to be virtualized. */\n items: T[];\n /** Fixed size of each item or a function that returns the size of an item. */\n itemSize?: number | ((item: T, index: number) => number) | undefined;\n /** Direction of the scroll: 'vertical', 'horizontal', or 'both'. */\n direction?: ScrollDirection | undefined;\n /** Number of items to render before the visible viewport. */\n bufferBefore?: number | undefined;\n /** Number of items to render after the visible viewport. */\n bufferAfter?: number | undefined;\n /** The scrollable container element or window. */\n container?: HTMLElement | Window | null | undefined;\n /** The host element that contains the items. */\n hostElement?: HTMLElement | null | undefined;\n /** Range of items to render for SSR. */\n ssrRange?: {\n start: number;\n end: number;\n colStart?: number;\n colEnd?: number;\n } | undefined;\n /** Number of columns for bidirectional scroll. */\n columnCount?: number | undefined;\n /** Fixed width of columns or an array/function for column widths. */\n columnWidth?: number | number[] | ((index: number) => number) | undefined;\n /** Padding at the start of the scroll container. */\n scrollPaddingStart?: number | { x?: number; y?: number; } | undefined;\n /** Padding at the end of the scroll container. */\n scrollPaddingEnd?: number | { x?: number; y?: number; } | undefined;\n /** Gap between items in pixels (vertical). */\n gap?: number | undefined;\n /** Gap between columns in pixels (horizontal/grid). */\n columnGap?: number | undefined;\n /** Indices of items that should stick to the top/start. */\n stickyIndices?: number[] | undefined;\n /** Distance from the end of the scrollable area to trigger 'load' event. */\n loadDistance?: number | undefined;\n /** Whether items are currently being loaded. */\n loading?: boolean | undefined;\n /** Whether to restore scroll position when items are prepended. */\n restoreScrollOnPrepend?: boolean | undefined;\n /** Initial scroll index to jump to on mount. */\n initialScrollIndex?: number | undefined;\n /** Alignment for the initial scroll index. */\n initialScrollAlign?: ScrollAlignment | ScrollAlignmentOptions | undefined;\n /** Default size for items before they are measured. */\n defaultItemSize?: number | undefined;\n /** Default width for columns before they are measured. */\n defaultColumnWidth?: number | undefined;\n /** Whether to enable debug mode. */\n debug?: boolean | undefined;\n}\n\n/** Represents an item currently rendered in the virtual scroll area. */\nexport interface RenderedItem<T = unknown> {\n /** The original data item. */\n item: T;\n /** The index of the item in the original array. */\n index: number;\n /** The calculated offset relative to the host element. */\n offset: { x: number; y: number; };\n /** The current measured or estimated size. */\n size: { width: number; height: number; };\n /** The original X offset before sticky adjustments. */\n originalX: number;\n /** The original Y offset before sticky adjustments. */\n originalY: number;\n /** Whether this item is configured to be sticky. */\n isSticky?: boolean;\n /** Whether this item is currently stuck at the threshold. */\n isStickyActive?: boolean;\n /** The offset applied for the sticky pushing effect. */\n stickyOffset: { x: number; y: number; };\n}\n\n/** Comprehensive state of the virtual scroll system. */\nexport interface ScrollDetails<T = unknown> {\n /** List of items currently rendered. */\n items: RenderedItem<T>[];\n /** Index of the first item partially or fully visible in the viewport. */\n currentIndex: number;\n /** Index of the first column partially or fully visible. */\n currentColIndex: number;\n /** Current scroll position relative to content start. */\n scrollOffset: { x: number; y: number; };\n /** Dimensions of the visible viewport. */\n viewportSize: { width: number; height: number; };\n /** Total calculated size of all items and gaps. */\n totalSize: { width: number; height: number; };\n /** Whether the container is currently being scrolled. */\n isScrolling: boolean;\n /** Whether the current scroll was initiated by a method call. */\n isProgrammaticScroll: boolean;\n /** Range of items currently being rendered. */\n range: { start: number; end: number; };\n /** Range of columns currently being rendered (for grid mode). */\n columnRange: { start: number; end: number; padStart: number; padEnd: number; };\n}\n\n/**\n * Composable for virtual scrolling logic.\n * Handles calculation of visible items, scroll events, and dynamic item sizes.\n *\n * @param props - Reactive properties for virtual scroll configuration\n */\nexport function useVirtualScroll<T = unknown>(props: Ref<VirtualScrollProps<T>>) {\n // --- State ---\n const scrollX = ref(0);\n const scrollY = ref(0);\n const isScrolling = ref(false);\n const isHydrated = ref(false);\n const isHydrating = ref(false);\n const isMounted = ref(false);\n const viewportWidth = ref(0);\n const viewportHeight = ref(0);\n const hostOffset = reactive({ x: 0, y: 0 });\n let scrollTimeout: ReturnType<typeof setTimeout> | undefined;\n\n const isProgrammaticScroll = ref(false);\n\n // --- Fenwick Trees for efficient size and offset management ---\n const itemSizesX = new FenwickTree(props.value.items.length);\n const itemSizesY = new FenwickTree(props.value.items.length);\n const columnSizes = new FenwickTree(props.value.columnCount || 0);\n\n const treeUpdateFlag = ref(0);\n\n let measuredColumns = new Uint8Array(0);\n let measuredItemsX = new Uint8Array(0);\n let measuredItemsY = new Uint8Array(0);\n\n // --- Scroll Queue / Correction ---\n const pendingScroll = ref<{\n rowIndex: number | null | undefined;\n colIndex: number | null | undefined;\n options: ScrollAlignment | ScrollAlignmentOptions | ScrollToIndexOptions | undefined;\n } | null>(null);\n\n // Track if sizes are initialized\n const sizesInitialized = ref(false);\n let lastItems: T[] = [];\n\n // --- Computed Config ---\n const isDynamicItemSize = computed(() =>\n props.value.itemSize === undefined || props.value.itemSize === null || props.value.itemSize === 0,\n );\n\n const isDynamicColumnWidth = computed(() =>\n props.value.columnWidth === undefined || props.value.columnWidth === null || props.value.columnWidth === 0,\n );\n\n const fixedItemSize = computed(() =>\n (typeof props.value.itemSize === 'number' && props.value.itemSize > 0) ? props.value.itemSize : null,\n );\n\n const defaultSize = computed(() => props.value.defaultItemSize || fixedItemSize.value || DEFAULT_ITEM_SIZE);\n\n const sortedStickyIndices = computed(() =>\n [ ...(props.value.stickyIndices || []) ].sort((a, b) => a - b),\n );\n\n // --- Size Calculations ---\n /**\n * Total width of all items in the scrollable area.\n */\n const totalWidth = computed(() => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n if (!isHydrated.value && props.value.ssrRange && !isMounted.value) {\n const { start = 0, end = 0, colStart = 0, colEnd = 0 } = props.value.ssrRange;\n const colCount = props.value.columnCount || 0;\n if (props.value.direction === 'both') {\n if (colCount <= 0) {\n return 0;\n }\n const effectiveColEnd = colEnd || colCount;\n const total = columnSizes.query(effectiveColEnd) - columnSizes.query(colStart);\n return Math.max(0, total - (effectiveColEnd > colStart ? (props.value.columnGap || 0) : 0));\n }\n /* v8 ignore else -- @preserve */\n if (props.value.direction === 'horizontal') {\n if (fixedItemSize.value !== null) {\n const len = end - start;\n return Math.max(0, len * (fixedItemSize.value + (props.value.columnGap || 0)) - (len > 0 ? (props.value.columnGap || 0) : 0));\n }\n const total = itemSizesX.query(end) - itemSizesX.query(start);\n return Math.max(0, total - (end > start ? (props.value.columnGap || 0) : 0));\n }\n }\n\n if (props.value.direction === 'both') {\n const colCount = props.value.columnCount || 0;\n if (colCount <= 0) {\n return 0;\n }\n const total = columnSizes.query(colCount);\n return Math.max(0, total - (props.value.columnGap || 0));\n }\n if (props.value.direction === 'vertical') {\n return 0;\n }\n if (fixedItemSize.value !== null) {\n const len = props.value.items.length;\n return Math.max(0, len * (fixedItemSize.value + (props.value.columnGap || 0)) - (len > 0 ? (props.value.columnGap || 0) : 0));\n }\n const total = itemSizesX.query(props.value.items.length);\n return Math.max(0, total - (props.value.items.length > 0 ? (props.value.columnGap || 0) : 0));\n });\n\n /**\n * Total height of all items in the scrollable area.\n */\n const totalHeight = computed(() => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n if (!isHydrated.value && props.value.ssrRange && !isMounted.value) {\n const { start, end } = props.value.ssrRange;\n /* v8 ignore else -- @preserve */\n if (props.value.direction === 'vertical' || props.value.direction === 'both') {\n if (fixedItemSize.value !== null) {\n const len = end - start;\n return Math.max(0, len * (fixedItemSize.value + (props.value.gap || 0)) - (len > 0 ? (props.value.gap || 0) : 0));\n }\n const total = itemSizesY.query(end) - itemSizesY.query(start);\n return Math.max(0, total - (end > start ? (props.value.gap || 0) : 0));\n }\n }\n\n if (props.value.direction === 'horizontal') {\n return 0;\n }\n if (fixedItemSize.value !== null) {\n const len = props.value.items.length;\n return Math.max(0, len * (fixedItemSize.value + (props.value.gap || 0)) - (len > 0 ? (props.value.gap || 0) : 0));\n }\n const total = itemSizesY.query(props.value.items.length);\n return Math.max(0, total - (props.value.items.length > 0 ? (props.value.gap || 0) : 0));\n });\n\n const relativeScrollX = computed(() => {\n const isHorizontal = props.value.direction === 'horizontal' || props.value.direction === 'both';\n const padding = isHorizontal ? getPaddingX(props.value.scrollPaddingStart, props.value.direction) : 0;\n return Math.max(0, scrollX.value + padding - hostOffset.x);\n });\n const relativeScrollY = computed(() => {\n const isVertical = props.value.direction === 'vertical' || props.value.direction === 'both';\n const padding = isVertical ? getPaddingY(props.value.scrollPaddingStart, props.value.direction) : 0;\n return Math.max(0, scrollY.value + padding - hostOffset.y);\n });\n\n // --- Scroll Helpers ---\n const getColumnWidth = (index: number) => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n const cw = props.value.columnWidth;\n if (typeof cw === 'number' && cw > 0) {\n return cw;\n }\n if (Array.isArray(cw) && cw.length > 0) {\n const val = cw[ index % cw.length ];\n return (val != null && val > 0) ? val : (props.value.defaultColumnWidth || DEFAULT_COLUMN_WIDTH);\n }\n /* v8 ignore else -- @preserve */\n if (typeof cw === 'function') {\n return cw(index);\n }\n return columnSizes.get(index) || props.value.defaultColumnWidth || DEFAULT_COLUMN_WIDTH;\n };\n\n // --- Public Scroll API ---\n /**\n * Scrolls to a specific row and column index.\n *\n * @param rowIndex - The row index to scroll to. Pass null to only scroll horizontally.\n * @param colIndex - The column index to scroll to. Pass null to only scroll vertically.\n * @param options - Scroll options including alignment ('start', 'center', 'end', 'auto') and behavior ('auto', 'smooth').\n */\n const scrollToIndex = (\n rowIndex: number | null | undefined,\n colIndex: number | null | undefined,\n options?: ScrollAlignment | ScrollAlignmentOptions | ScrollToIndexOptions,\n ) => {\n const isCorrection = typeof options === 'object' && options !== null && 'isCorrection' in options\n ? options.isCorrection\n : false;\n\n if (!isCorrection) {\n pendingScroll.value = { rowIndex, colIndex, options };\n }\n\n const container = props.value.container || window;\n const fixedSize = fixedItemSize.value;\n const gap = props.value.gap || 0;\n const columnGap = props.value.columnGap || 0;\n\n let align: ScrollAlignment | ScrollAlignmentOptions | undefined;\n let behavior: 'auto' | 'smooth' | undefined;\n\n if (isScrollToIndexOptions(options)) {\n align = options.align;\n behavior = options.behavior;\n } else {\n align = options as ScrollAlignment | ScrollAlignmentOptions;\n }\n\n const alignX = (typeof align === 'object' ? align.x : align) || 'auto';\n const alignY = (typeof align === 'object' ? align.y : align) || 'auto';\n\n const paddingStartX = getPaddingX(props.value.scrollPaddingStart, props.value.direction);\n const paddingEndX = getPaddingX(props.value.scrollPaddingEnd, props.value.direction);\n const paddingStartY = getPaddingY(props.value.scrollPaddingStart, props.value.direction);\n const paddingEndY = getPaddingY(props.value.scrollPaddingEnd, props.value.direction);\n\n const isVertical = props.value.direction === 'vertical' || props.value.direction === 'both';\n const isHorizontal = props.value.direction === 'horizontal' || props.value.direction === 'both';\n\n const usableWidth = viewportWidth.value - (isHorizontal ? (paddingStartX + paddingEndX) : 0);\n const usableHeight = viewportHeight.value - (isVertical ? (paddingStartY + paddingEndY) : 0);\n\n let targetX = relativeScrollX.value;\n let targetY = relativeScrollY.value;\n let itemWidth = 0;\n let itemHeight = 0;\n\n // Y calculation\n if (rowIndex !== null && rowIndex !== undefined) {\n if (rowIndex >= props.value.items.length) {\n targetY = totalHeight.value;\n itemHeight = 0;\n } else {\n targetY = fixedSize !== null ? rowIndex * (fixedSize + gap) : itemSizesY.query(rowIndex);\n itemHeight = fixedSize !== null ? fixedSize : itemSizesY.get(rowIndex) - gap;\n }\n\n // Apply Y Alignment\n if (alignY === 'start') {\n // targetY is already at the start of the list\n } else if (alignY === 'center') {\n targetY -= (usableHeight - itemHeight) / 2;\n } else if (alignY === 'end') {\n targetY -= (usableHeight - itemHeight);\n } else {\n const isVisibleY = targetY >= relativeScrollY.value && (targetY + itemHeight) <= (relativeScrollY.value + usableHeight);\n if (!isVisibleY) {\n if (targetY < relativeScrollY.value) {\n // keep targetY at start\n } else {\n targetY -= (usableHeight - itemHeight);\n }\n }\n }\n }\n\n // X calculation\n if (colIndex !== null && colIndex !== undefined) {\n const totalCols = props.value.columnCount || 0;\n if (colIndex >= totalCols && totalCols > 0) {\n targetX = totalWidth.value;\n itemWidth = 0;\n } else if (props.value.direction === 'horizontal') {\n targetX = fixedSize !== null ? colIndex * (fixedSize + columnGap) : itemSizesX.query(colIndex);\n itemWidth = fixedSize !== null ? fixedSize : itemSizesX.get(colIndex) - columnGap;\n } else {\n targetX = columnSizes.query(colIndex);\n itemWidth = columnSizes.get(colIndex) - columnGap;\n }\n\n // Apply X Alignment\n if (alignX === 'start') {\n // targetX is already at the start of the list\n } else if (alignX === 'center') {\n targetX -= (usableWidth - itemWidth) / 2;\n } else if (alignX === 'end') {\n targetX -= (usableWidth - itemWidth);\n } else {\n const isVisibleX = targetX >= relativeScrollX.value && (targetX + itemWidth) <= (relativeScrollX.value + usableWidth);\n if (!isVisibleX) {\n /* v8 ignore if -- @preserve */\n if (targetX < relativeScrollX.value) {\n // keep targetX at start\n } else {\n targetX -= (usableWidth - itemWidth);\n }\n }\n }\n }\n\n // Clamp to valid range\n targetX = Math.max(0, Math.min(targetX, Math.max(0, totalWidth.value - usableWidth)));\n targetY = Math.max(0, Math.min(targetY, Math.max(0, totalHeight.value - usableHeight)));\n\n const finalX = targetX + hostOffset.x - (isHorizontal ? paddingStartX : 0);\n const finalY = targetY + hostOffset.y - (isVertical ? paddingStartY : 0);\n\n // Check if we reached the target\n const tolerance = 1;\n let reachedX = (colIndex === null || colIndex === undefined) || Math.abs(relativeScrollX.value - targetX) < tolerance;\n let reachedY = (rowIndex === null || rowIndex === undefined) || Math.abs(relativeScrollY.value - targetY) < tolerance;\n\n if (!reachedX || !reachedY) {\n let curX = 0;\n let curY = 0;\n let maxW = 0;\n let maxH = 0;\n let viewW = 0;\n let viewH = 0;\n\n /* v8 ignore else -- @preserve */\n if (typeof window !== 'undefined') {\n if (container === window) {\n curX = window.scrollX;\n curY = window.scrollY;\n maxW = document.documentElement.scrollWidth;\n maxH = document.documentElement.scrollHeight;\n viewW = window.innerWidth;\n viewH = window.innerHeight;\n } else if (isElement(container)) {\n curX = container.scrollLeft;\n curY = container.scrollTop;\n maxW = container.scrollWidth;\n maxH = container.scrollHeight;\n viewW = container.clientWidth;\n viewH = container.clientHeight;\n }\n\n if (!reachedX && colIndex !== null && colIndex !== undefined) {\n const atLeft = curX <= tolerance && finalX <= tolerance;\n const atRight = curX >= maxW - viewW - tolerance && finalX >= maxW - viewW - tolerance;\n /* v8 ignore else -- @preserve */\n if (atLeft || atRight) {\n reachedX = true;\n }\n }\n\n if (!reachedY && rowIndex !== null && rowIndex !== undefined) {\n const atTop = curY <= tolerance && finalY <= tolerance;\n const atBottom = curY >= maxH - viewH - tolerance && finalY >= maxH - viewH - tolerance;\n if (atTop || atBottom) {\n reachedY = true;\n }\n }\n }\n }\n\n const scrollBehavior = isCorrection ? 'auto' : (behavior || 'smooth');\n isProgrammaticScroll.value = true;\n\n if (typeof window !== 'undefined' && container === window) {\n window.scrollTo({\n left: (colIndex === null || colIndex === undefined) ? undefined : Math.max(0, finalX),\n top: (rowIndex === null || rowIndex === undefined) ? undefined : Math.max(0, finalY),\n behavior: scrollBehavior,\n } as ScrollToOptions);\n } else if (isScrollableElement(container)) {\n const scrollOptions: ScrollToOptions = {\n behavior: scrollBehavior,\n };\n\n if (colIndex !== null && colIndex !== undefined) {\n scrollOptions.left = Math.max(0, finalX);\n }\n if (rowIndex !== null && rowIndex !== undefined) {\n scrollOptions.top = Math.max(0, finalY);\n }\n\n if (typeof container.scrollTo === 'function') {\n container.scrollTo(scrollOptions);\n } else {\n if (scrollOptions.left !== undefined) {\n container.scrollLeft = scrollOptions.left;\n }\n if (scrollOptions.top !== undefined) {\n container.scrollTop = scrollOptions.top;\n }\n }\n }\n\n if (scrollBehavior === 'auto' || scrollBehavior === undefined) {\n if (colIndex !== null && colIndex !== undefined) {\n scrollX.value = Math.max(0, finalX);\n }\n if (rowIndex !== null && rowIndex !== undefined) {\n scrollY.value = Math.max(0, finalY);\n }\n }\n\n if (reachedX && reachedY && !isScrolling.value) {\n pendingScroll.value = null;\n }\n };\n\n /**\n * Programmatically scroll to a specific pixel offset relative to the content start.\n *\n * @param x - The pixel offset to scroll to on the X axis. Pass null to keep current position.\n * @param y - The pixel offset to scroll to on the Y axis. Pass null to keep current position.\n * @param options - Scroll options (behavior)\n * @param options.behavior - The scroll behavior ('auto' | 'smooth')\n */\n const scrollToOffset = (x?: number | null, y?: number | null, options?: { behavior?: 'auto' | 'smooth'; }) => {\n const container = props.value.container || window;\n isProgrammaticScroll.value = true;\n\n const isVertical = props.value.direction === 'vertical' || props.value.direction === 'both';\n const isHorizontal = props.value.direction === 'horizontal' || props.value.direction === 'both';\n\n const paddingStartX = getPaddingX(props.value.scrollPaddingStart, props.value.direction);\n const paddingStartY = getPaddingY(props.value.scrollPaddingStart, props.value.direction);\n const paddingEndX = getPaddingX(props.value.scrollPaddingEnd, props.value.direction);\n const paddingEndY = getPaddingY(props.value.scrollPaddingEnd, props.value.direction);\n\n const usableWidth = viewportWidth.value - (isHorizontal ? (paddingStartX + paddingEndX) : 0);\n const usableHeight = viewportHeight.value - (isVertical ? (paddingStartY + paddingEndY) : 0);\n\n const clampedX = (x !== null && x !== undefined)\n ? (isHorizontal ? Math.max(0, Math.min(x, Math.max(0, totalWidth.value - usableWidth))) : Math.max(0, x))\n : null;\n const clampedY = (y !== null && y !== undefined)\n ? (isVertical ? Math.max(0, Math.min(y, Math.max(0, totalHeight.value - usableHeight))) : Math.max(0, y))\n : null;\n\n const currentX = (typeof window !== 'undefined' && container === window ? window.scrollX : (container as HTMLElement).scrollLeft);\n const currentY = (typeof window !== 'undefined' && container === window ? window.scrollY : (container as HTMLElement).scrollTop);\n\n const targetX = (clampedX !== null) ? clampedX + hostOffset.x - (isHorizontal ? paddingStartX : 0) : currentX;\n const targetY = (clampedY !== null) ? clampedY + hostOffset.y - (isVertical ? paddingStartY : 0) : currentY;\n\n if (typeof window !== 'undefined' && container === window) {\n window.scrollTo({\n left: (x !== null && x !== undefined) ? targetX : undefined,\n top: (y !== null && y !== undefined) ? targetY : undefined,\n behavior: options?.behavior || 'auto',\n } as ScrollToOptions);\n } else if (isScrollableElement(container)) {\n const scrollOptions: ScrollToOptions = {\n behavior: options?.behavior || 'auto',\n };\n\n if (x !== null && x !== undefined) {\n scrollOptions.left = targetX;\n }\n if (y !== null && y !== undefined) {\n scrollOptions.top = targetY;\n }\n\n if (typeof container.scrollTo === 'function') {\n container.scrollTo(scrollOptions);\n } else {\n if (scrollOptions.left !== undefined) {\n container.scrollLeft = scrollOptions.left;\n }\n if (scrollOptions.top !== undefined) {\n container.scrollTop = scrollOptions.top;\n }\n }\n }\n\n if (options?.behavior === 'auto' || options?.behavior === undefined) {\n if (x !== null && x !== undefined) {\n scrollX.value = targetX;\n }\n if (y !== null && y !== undefined) {\n scrollY.value = targetY;\n }\n }\n };\n\n // --- Measurement & Initialization ---\n const initializeSizes = () => {\n const newItems = props.value.items;\n const len = newItems.length;\n const colCount = props.value.columnCount || 0;\n\n itemSizesX.resize(len);\n itemSizesY.resize(len);\n columnSizes.resize(colCount);\n\n if (measuredItemsX.length !== len) {\n const newMeasuredX = new Uint8Array(len);\n newMeasuredX.set(measuredItemsX.subarray(0, Math.min(len, measuredItemsX.length)));\n measuredItemsX = newMeasuredX;\n }\n if (measuredItemsY.length !== len) {\n const newMeasuredY = new Uint8Array(len);\n newMeasuredY.set(measuredItemsY.subarray(0, Math.min(len, measuredItemsY.length)));\n measuredItemsY = newMeasuredY;\n }\n if (measuredColumns.length !== colCount) {\n const newMeasuredCols = new Uint8Array(colCount);\n newMeasuredCols.set(measuredColumns.subarray(0, Math.min(colCount, measuredColumns.length)));\n measuredColumns = newMeasuredCols;\n }\n\n let prependCount = 0;\n if (props.value.restoreScrollOnPrepend && lastItems.length > 0 && len > lastItems.length) {\n const oldFirstItem = lastItems[ 0 ];\n /* v8 ignore else -- @preserve */\n if (oldFirstItem !== undefined) {\n for (let i = 1; i <= len - lastItems.length; i++) {\n if (newItems[ i ] === oldFirstItem) {\n prependCount = i;\n break;\n }\n }\n }\n }\n\n if (prependCount > 0) {\n itemSizesX.shift(prependCount);\n itemSizesY.shift(prependCount);\n\n if (pendingScroll.value && pendingScroll.value.rowIndex !== null && pendingScroll.value.rowIndex !== undefined) {\n pendingScroll.value.rowIndex += prependCount;\n }\n\n const newMeasuredX = new Uint8Array(len);\n const newMeasuredY = new Uint8Array(len);\n newMeasuredX.set(measuredItemsX.subarray(0, Math.min(len - prependCount, measuredItemsX.length)), prependCount);\n newMeasuredY.set(measuredItemsY.subarray(0, Math.min(len - prependCount, measuredItemsY.length)), prependCount);\n measuredItemsX = newMeasuredX;\n measuredItemsY = newMeasuredY;\n\n // Calculate added size\n const gap = props.value.gap || 0;\n const columnGap = props.value.columnGap || 0;\n let addedX = 0;\n let addedY = 0;\n\n for (let i = 0; i < prependCount; i++) {\n const size = typeof props.value.itemSize === 'function'\n ? props.value.itemSize(newItems[ i ] as T, i)\n : defaultSize.value;\n\n if (props.value.direction === 'horizontal') {\n addedX += size + columnGap;\n } else {\n addedY += size + gap;\n }\n }\n\n /* v8 ignore else -- @preserve */\n if (addedX > 0 || addedY > 0) {\n nextTick(() => {\n scrollToOffset(\n addedX > 0 ? relativeScrollX.value + addedX : null,\n addedY > 0 ? relativeScrollY.value + addedY : null,\n { behavior: 'auto' },\n );\n });\n }\n }\n\n // Initialize columns if fixed width is provided\n if (colCount > 0) {\n const columnGap = props.value.columnGap || 0;\n let colNeedsRebuild = false;\n for (let i = 0; i < colCount; i++) {\n const width = getColumnWidth(i);\n const currentW = columnSizes.get(i);\n const isMeasured = measuredColumns[ i ] === 1;\n\n // If fixed/function, or if dynamic but not measured yet\n if (!isDynamicColumnWidth.value || !isMeasured || currentW === 0) {\n const targetW = width + columnGap;\n if (Math.abs(currentW - targetW) > 0.5) {\n columnSizes.set(i, targetW);\n measuredColumns[ i ] = isDynamicColumnWidth.value ? 0 : 1;\n colNeedsRebuild = true;\n } else if (!isDynamicColumnWidth.value) {\n measuredColumns[ i ] = 1;\n }\n }\n }\n if (colNeedsRebuild) {\n columnSizes.rebuild();\n }\n }\n\n const gap = props.value.gap || 0;\n const columnGap = props.value.columnGap || 0;\n let itemsNeedRebuild = false;\n\n for (let i = 0; i < len; i++) {\n const item = props.value.items[ i ];\n const currentX = itemSizesX.get(i);\n const currentY = itemSizesY.get(i);\n\n const size = typeof props.value.itemSize === 'function'\n ? props.value.itemSize(item as T, i)\n : defaultSize.value;\n\n const isVertical = props.value.direction === 'vertical';\n const isHorizontal = props.value.direction === 'horizontal';\n const isBoth = props.value.direction === 'both';\n\n const targetX = isHorizontal ? size + columnGap : 0;\n const targetY = (isVertical || isBoth) ? size + gap : 0;\n\n const isMeasuredX = measuredItemsX[ i ] === 1;\n const isMeasuredY = measuredItemsY[ i ] === 1;\n\n // Logic for X\n if (isHorizontal) {\n // If fixed/function, or if dynamic but not measured yet\n if (!isDynamicItemSize.value || !isMeasuredX || currentX === 0) {\n if (Math.abs(currentX - targetX) > 0.5) {\n itemSizesX.set(i, targetX);\n measuredItemsX[ i ] = isDynamicItemSize.value ? 0 : 1;\n itemsNeedRebuild = true;\n } else if (!isDynamicItemSize.value) {\n measuredItemsX[ i ] = 1;\n }\n }\n } else if (currentX !== 0) {\n itemSizesX.set(i, 0);\n measuredItemsX[ i ] = 0;\n itemsNeedRebuild = true;\n }\n\n // Logic for Y\n if (isVertical || isBoth) {\n if (!isDynamicItemSize.value || !isMeasuredY || currentY === 0) {\n if (Math.abs(currentY - targetY) > 0.5) {\n itemSizesY.set(i, targetY);\n measuredItemsY[ i ] = isDynamicItemSize.value ? 0 : 1;\n itemsNeedRebuild = true;\n } else if (!isDynamicItemSize.value) {\n measuredItemsY[ i ] = 1;\n }\n }\n } else if (currentY !== 0) {\n itemSizesY.set(i, 0);\n measuredItemsY[ i ] = 0;\n itemsNeedRebuild = true;\n }\n }\n\n if (itemsNeedRebuild) {\n itemSizesX.rebuild();\n itemSizesY.rebuild();\n }\n\n lastItems = [ ...newItems ];\n sizesInitialized.value = true;\n treeUpdateFlag.value++;\n };\n\n /**\n * Updates the host element's offset relative to the scroll container.\n */\n const updateHostOffset = () => {\n if (props.value.hostElement && typeof window !== 'undefined') {\n const rect = props.value.hostElement.getBoundingClientRect();\n const container = props.value.container || window;\n\n let newX = 0;\n let newY = 0;\n\n if (container === window) {\n newX = rect.left + window.scrollX;\n newY = rect.top + window.scrollY;\n } else if (container === props.value.hostElement) {\n newX = 0;\n newY = 0;\n } else if (isElement(container)) {\n const containerRect = container.getBoundingClientRect();\n newX = rect.left - containerRect.left + container.scrollLeft;\n newY = rect.top - containerRect.top + container.scrollTop;\n }\n\n if (Math.abs(hostOffset.x - newX) > 0.1 || Math.abs(hostOffset.y - newY) > 0.1) {\n hostOffset.x = newX;\n hostOffset.y = newY;\n }\n }\n };\n\n watch([\n () => props.value.items,\n () => props.value.direction,\n () => props.value.columnCount,\n () => props.value.columnWidth,\n () => props.value.itemSize,\n () => props.value.gap,\n () => props.value.columnGap,\n () => props.value.defaultItemSize,\n () => props.value.defaultColumnWidth,\n ], initializeSizes, { immediate: true });\n\n watch(() => [ props.value.container, props.value.hostElement ], () => {\n updateHostOffset();\n });\n\n // --- Range & Visible Items ---\n /**\n * Current range of items that should be rendered.\n */\n const range = computed(() => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n if ((!isHydrated.value || isHydrating.value) && props.value.ssrRange) {\n return {\n start: props.value.ssrRange.start,\n end: props.value.ssrRange.end,\n };\n }\n\n const direction = props.value.direction || 'vertical';\n const bufferBefore = (props.value.ssrRange && !isScrolling.value) ? 0 : (props.value.bufferBefore ?? DEFAULT_BUFFER);\n const bufferAfter = props.value.bufferAfter ?? DEFAULT_BUFFER;\n const gap = props.value.gap || 0;\n const columnGap = props.value.columnGap || 0;\n const fixedSize = fixedItemSize.value;\n const paddingStartX = getPaddingX(props.value.scrollPaddingStart, direction);\n const paddingEndX = getPaddingX(props.value.scrollPaddingEnd, direction);\n const paddingStartY = getPaddingY(props.value.scrollPaddingStart, direction);\n const paddingEndY = getPaddingY(props.value.scrollPaddingEnd, direction);\n\n const isVertical = direction === 'vertical' || direction === 'both';\n const isHorizontal = direction === 'horizontal' || direction === 'both';\n\n const usableWidth = viewportWidth.value - (isHorizontal ? (paddingStartX + paddingEndX) : 0);\n const usableHeight = viewportHeight.value - (isVertical ? (paddingStartY + paddingEndY) : 0);\n\n let start = 0;\n let end = props.value.items.length;\n\n if (isVertical) {\n if (fixedSize !== null) {\n start = Math.floor(relativeScrollY.value / (fixedSize + gap));\n end = Math.ceil((relativeScrollY.value + usableHeight) / (fixedSize + gap));\n } else {\n start = itemSizesY.findLowerBound(relativeScrollY.value);\n let currentY = itemSizesY.query(start);\n let i = start;\n while (i < props.value.items.length && currentY < relativeScrollY.value + usableHeight) {\n currentY = itemSizesY.query(++i);\n }\n end = i;\n }\n } else {\n if (fixedSize !== null) {\n start = Math.floor(relativeScrollX.value / (fixedSize + columnGap));\n end = Math.ceil((relativeScrollX.value + usableWidth) / (fixedSize + columnGap));\n } else {\n start = itemSizesX.findLowerBound(relativeScrollX.value);\n let currentX = itemSizesX.query(start);\n let i = start;\n while (i < props.value.items.length && currentX < relativeScrollX.value + usableWidth) {\n currentX = itemSizesX.query(++i);\n }\n end = i;\n }\n }\n\n return {\n start: Math.max(0, start - bufferBefore),\n end: Math.min(props.value.items.length, end + bufferAfter),\n };\n });\n\n /**\n * Index of the first visible item in the viewport.\n */\n const currentIndex = computed(() => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n const fixedSize = fixedItemSize.value;\n const gap = props.value.gap || 0;\n const columnGap = props.value.columnGap || 0;\n\n if (props.value.direction === 'horizontal') {\n if (fixedSize !== null) {\n return Math.floor(relativeScrollX.value / (fixedSize + columnGap));\n }\n return itemSizesX.findLowerBound(relativeScrollX.value);\n }\n if (fixedSize !== null) {\n return Math.floor(relativeScrollY.value / (fixedSize + gap));\n }\n return itemSizesY.findLowerBound(relativeScrollY.value);\n });\n\n /**\n * List of items to be rendered with their calculated offsets and sizes.\n */\n const renderedItems = computed<RenderedItem<T>[]>(() => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n const { start, end } = range.value;\n const items: RenderedItem<T>[] = [];\n const fixedSize = fixedItemSize.value;\n const gap = props.value.gap || 0;\n const columnGap = props.value.columnGap || 0;\n const stickyIndices = sortedStickyIndices.value;\n\n // Always include relevant sticky items\n const indicesToRender = new Set<number>();\n for (let i = start; i < end; i++) {\n indicesToRender.add(i);\n }\n\n if (isHydrated.value || !props.value.ssrRange) {\n const activeIdx = currentIndex.value;\n // find the largest index in stickyIndices that is < activeIdx\n let prevStickyIdx: number | undefined;\n let low = 0;\n let high = stickyIndices.length - 1;\n while (low <= high) {\n const mid = (low + high) >>> 1;\n if (stickyIndices[ mid ]! < activeIdx) {\n prevStickyIdx = stickyIndices[ mid ];\n low = mid + 1;\n } else {\n high = mid - 1;\n }\n }\n\n if (prevStickyIdx !== undefined) {\n indicesToRender.add(prevStickyIdx);\n }\n\n for (const idx of stickyIndices) {\n if (idx >= start && idx < end) {\n indicesToRender.add(idx);\n }\n }\n }\n\n const sortedIndices = Array.from(indicesToRender).sort((a, b) => a - b);\n\n const ssrStartRow = props.value.ssrRange?.start || 0;\n const ssrStartCol = props.value.ssrRange?.colStart || 0;\n\n let ssrOffsetX = 0;\n let ssrOffsetY = 0;\n\n if (!isHydrated.value && props.value.ssrRange) {\n ssrOffsetY = (props.value.direction === 'vertical' || props.value.direction === 'both')\n ? (fixedSize !== null ? ssrStartRow * (fixedSize + gap) : itemSizesY.query(ssrStartRow))\n : 0;\n\n if (props.value.direction === 'horizontal') {\n ssrOffsetX = fixedSize !== null ? ssrStartCol * (fixedSize + columnGap) : itemSizesX.query(ssrStartCol);\n } else if (props.value.direction === 'both') {\n ssrOffsetX = columnSizes.query(ssrStartCol);\n }\n }\n\n for (const i of sortedIndices) {\n const item = props.value.items[ i ];\n if (item === undefined) {\n continue;\n }\n\n let x = 0;\n let y = 0;\n let width = 0;\n let height = 0;\n\n if (props.value.direction === 'horizontal') {\n x = fixedSize !== null ? i * (fixedSize + columnGap) : itemSizesX.query(i);\n width = fixedSize !== null ? fixedSize : itemSizesX.get(i) - columnGap;\n height = viewportHeight.value;\n } else {\n // vertical or both\n y = (props.value.direction === 'vertical' || props.value.direction === 'both') && fixedSize !== null ? i * (fixedSize + gap) : itemSizesY.query(i);\n height = fixedSize !== null ? fixedSize : itemSizesY.get(i) - gap;\n width = props.value.direction === 'both' ? totalWidth.value : viewportWidth.value;\n }\n\n const isSticky = stickyIndices.includes(i);\n const originalX = x;\n const originalY = y;\n let isStickyActive = false;\n const stickyOffset = { x: 0, y: 0 };\n\n if (isSticky) {\n if (props.value.direction === 'vertical' || props.value.direction === 'both') {\n if (relativeScrollY.value > originalY) {\n isStickyActive = true;\n // Check if next sticky item pushes this one\n let nextStickyIdx: number | undefined;\n let low = 0;\n let high = stickyIndices.length - 1;\n while (low <= high) {\n const mid = (low + high) >>> 1;\n if (stickyIndices[ mid ]! > i) {\n nextStickyIdx = stickyIndices[ mid ];\n high = mid - 1;\n } else {\n low = mid + 1;\n }\n }\n\n if (nextStickyIdx !== undefined) {\n const nextStickyY = fixedSize !== null ? nextStickyIdx * (fixedSize + gap) : itemSizesY.query(nextStickyIdx);\n const distance = nextStickyY - relativeScrollY.value;\n /* v8 ignore else -- @preserve */\n if (distance < height) {\n stickyOffset.y = -(height - distance);\n }\n }\n }\n } else if (props.value.direction === 'horizontal') {\n if (relativeScrollX.value > originalX) {\n isStickyActive = true;\n // Check if next sticky item pushes this one\n let nextStickyIdx: number | undefined;\n let low = 0;\n let high = stickyIndices.length - 1;\n while (low <= high) {\n const mid = (low + high) >>> 1;\n if (stickyIndices[ mid ]! > i) {\n nextStickyIdx = stickyIndices[ mid ];\n high = mid - 1;\n } else {\n low = mid + 1;\n }\n }\n\n if (nextStickyIdx !== undefined) {\n const nextStickyX = fixedSize !== null ? nextStickyIdx * (fixedSize + columnGap) : itemSizesX.query(nextStickyIdx);\n const distance = nextStickyX - relativeScrollX.value;\n /* v8 ignore else -- @preserve */\n if (distance < width) {\n stickyOffset.x = -(width - distance);\n }\n }\n }\n }\n }\n\n items.push({\n item,\n index: i,\n offset: { x: originalX - ssrOffsetX, y: originalY - ssrOffsetY },\n size: { width, height },\n originalX,\n originalY,\n isSticky,\n isStickyActive,\n stickyOffset,\n });\n }\n return items;\n });\n\n const columnRange = computed(() => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n const totalCols = props.value.columnCount || 0;\n\n if (!totalCols) {\n return { start: 0, end: 0, padStart: 0, padEnd: 0 };\n }\n\n if ((!isHydrated.value || isHydrating.value) && props.value.ssrRange) {\n const { colStart = 0, colEnd = 0 } = props.value.ssrRange;\n const safeStart = Math.max(0, colStart);\n const safeEnd = Math.min(totalCols, colEnd || totalCols);\n return {\n start: safeStart,\n end: safeEnd,\n padStart: 0,\n padEnd: 0,\n };\n }\n\n const start = columnSizes.findLowerBound(relativeScrollX.value);\n let currentX = columnSizes.query(start);\n let end = start;\n\n while (end < totalCols && currentX < relativeScrollX.value + viewportWidth.value) {\n currentX = columnSizes.query(++end);\n }\n\n const colBuffer = (props.value.ssrRange && !isScrolling.value) ? 0 : 2;\n\n // Add buffer of columns\n const safeStart = Math.max(0, start - colBuffer);\n const safeEnd = Math.min(totalCols, end + colBuffer);\n\n const padStart = columnSizes.query(safeStart);\n\n return {\n start: safeStart,\n end: safeEnd,\n padStart,\n padEnd: columnSizes.query(totalCols) - columnSizes.query(safeEnd),\n };\n });\n\n /**\n * Detailed information about the current scroll state.\n */\n const scrollDetails = computed<ScrollDetails<T>>(() => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n const fixedSize = fixedItemSize.value;\n const columnGap = props.value.columnGap || 0;\n\n let currentColIndex = 0;\n if (props.value.direction === 'horizontal' || props.value.direction === 'both') {\n if (fixedSize !== null) {\n currentColIndex = Math.floor(relativeScrollX.value / (fixedSize + columnGap));\n } else {\n currentColIndex = itemSizesX.findLowerBound(relativeScrollX.value);\n }\n }\n\n return {\n items: renderedItems.value,\n currentIndex: currentIndex.value,\n currentColIndex,\n scrollOffset: { x: relativeScrollX.value, y: relativeScrollY.value },\n viewportSize: { width: viewportWidth.value, height: viewportHeight.value },\n totalSize: { width: totalWidth.value, height: totalHeight.value },\n isScrolling: isScrolling.value,\n isProgrammaticScroll: isProgrammaticScroll.value,\n range: range.value,\n columnRange: columnRange.value,\n };\n });\n\n // --- Event Handlers & Lifecycle ---\n /**\n * Stops any currently active programmatic scroll and clears pending corrections.\n */\n const stopProgrammaticScroll = () => {\n isProgrammaticScroll.value = false;\n pendingScroll.value = null;\n };\n\n /**\n * Event handler for scroll events.\n */\n const handleScroll = (e: Event) => {\n const target = e.target;\n if (typeof window === 'undefined') {\n return;\n }\n\n if (target === window || target === document) {\n scrollX.value = window.scrollX;\n scrollY.value = window.scrollY;\n } else if (isScrollableElement(target)) {\n scrollX.value = target.scrollLeft;\n scrollY.value = target.scrollTop;\n }\n\n if (!isScrolling.value) {\n if (!isProgrammaticScroll.value) {\n pendingScroll.value = null;\n }\n isScrolling.value = true;\n }\n clearTimeout(scrollTimeout);\n scrollTimeout = setTimeout(() => {\n isScrolling.value = false;\n isProgrammaticScroll.value = false;\n }, 250);\n };\n\n /**\n * Updates the size of multiple items in the Fenwick tree.\n *\n * @param updates - Array of updates\n */\n const updateItemSizes = (updates: Array<{ index: number; inlineSize: number; blockSize: number; element?: HTMLElement | undefined; }>) => {\n let needUpdate = false;\n const gap = props.value.gap || 0;\n const columnGap = props.value.columnGap || 0;\n\n for (const { index, inlineSize, blockSize, element } of updates) {\n const isMeasurable = isDynamicItemSize.value || typeof props.value.itemSize === 'function';\n if (isMeasurable && index >= 0) {\n if (props.value.direction === 'horizontal') {\n const oldWidth = itemSizesX.get(index);\n const targetWidth = inlineSize + columnGap;\n // Apply if:\n // 1. It's the first measurement (measuredItemsX[index] is 0)\n // 2. It's a significant change (> 0.5px)\n /* v8 ignore else -- @preserve */\n if (!measuredItemsX[ index ] || Math.abs(targetWidth - oldWidth) > 0.5) {\n itemSizesX.update(index, targetWidth - oldWidth);\n measuredItemsX[ index ] = 1;\n needUpdate = true;\n }\n }\n if (props.value.direction === 'vertical' || props.value.direction === 'both') {\n const oldHeight = itemSizesY.get(index);\n const targetHeight = blockSize + gap;\n\n if (props.value.direction === 'both') {\n // For grid, we should be careful with decreases because a row height is the max of all its cells.\n /* v8 ignore else -- @preserve */\n if (!measuredItemsY[ index ] || Math.abs(targetHeight - oldHeight) > 0.5) {\n itemSizesY.update(index, targetHeight - oldHeight);\n measuredItemsY[ index ] = 1;\n needUpdate = true;\n }\n } else if (!measuredItemsY[ index ] || Math.abs(targetHeight - oldHeight) > 0.5) {\n itemSizesY.update(index, targetHeight - oldHeight);\n measuredItemsY[ index ] = 1;\n needUpdate = true;\n }\n }\n }\n\n // Dynamic column width measurement\n const isColMeasurable = isDynamicColumnWidth.value || typeof props.value.columnWidth === 'function';\n if (\n props.value.direction === 'both'\n && element\n && props.value.columnCount\n && isColMeasurable\n ) {\n const cells = element.dataset.colIndex !== undefined\n ? [ element ]\n : Array.from(element.querySelectorAll('[data-col-index]')) as HTMLElement[];\n\n for (const child of cells) {\n const colIndex = Number.parseInt(child.dataset.colIndex!, 10);\n\n /* v8 ignore else -- @preserve */\n if (colIndex >= 0 && colIndex < (props.value.columnCount || 0)) {\n const w = child.offsetWidth;\n const oldW = columnSizes.get(colIndex);\n const targetW = w + columnGap;\n if (Math.abs(oldW - targetW) > 0.5) {\n columnSizes.update(colIndex, targetW - oldW);\n measuredColumns[ colIndex ] = 1;\n needUpdate = true;\n }\n }\n }\n }\n }\n\n if (needUpdate) {\n treeUpdateFlag.value++;\n }\n };\n\n /**\n * Updates the size of a specific item in the Fenwick tree.\n *\n * @param index - Index of the item\n * @param inlineSize - New inlineSize\n * @param blockSize - New blockSize\n * @param element - The element that was measured (optional)\n */\n const updateItemSize = (index: number, inlineSize: number, blockSize: number, element?: HTMLElement) => {\n updateItemSizes([ { index, inlineSize, blockSize, element } ]);\n };\n\n // --- Scroll Queue / Correction Watchers ---\n const checkPendingScroll = () => {\n if (pendingScroll.value && !isHydrating.value) {\n const { rowIndex, colIndex, options } = pendingScroll.value;\n const correctionOptions: ScrollToIndexOptions = isScrollToIndexOptions(options)\n ? { ...options, isCorrection: true }\n : { align: options as ScrollAlignment | ScrollAlignmentOptions, isCorrection: true };\n scrollToIndex(rowIndex, colIndex, correctionOptions);\n }\n };\n\n watch(treeUpdateFlag, checkPendingScroll);\n\n watch(isScrolling, (scrolling) => {\n if (!scrolling) {\n checkPendingScroll();\n }\n });\n\n let resizeObserver: ResizeObserver | null = null;\n\n const attachEvents = (container: HTMLElement | Window | null) => {\n if (!container || typeof window === 'undefined') {\n return;\n }\n const scrollTarget = container === window ? document : container;\n scrollTarget.addEventListener('scroll', handleScroll, { passive: true });\n\n if (container === window) {\n viewportWidth.value = window.innerWidth;\n viewportHeight.value = window.innerHeight;\n scrollX.value = window.scrollX;\n scrollY.value = window.scrollY;\n\n const onResize = () => {\n viewportWidth.value = window.innerWidth;\n viewportHeight.value = window.innerHeight;\n updateHostOffset();\n };\n window.addEventListener('resize', onResize);\n return () => {\n scrollTarget.removeEventListener('scroll', handleScroll);\n window.removeEventListener('resize', onResize);\n };\n } else {\n viewportWidth.value = (container as HTMLElement).clientWidth;\n viewportHeight.value = (container as HTMLElement).clientHeight;\n scrollX.value = (container as HTMLElement).scrollLeft;\n scrollY.value = (container as HTMLElement).scrollTop;\n\n resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n /* v8 ignore else -- @preserve */\n if (entry.target === container) {\n viewportWidth.value = (container as HTMLElement).clientWidth;\n viewportHeight.value = (container as HTMLElement).clientHeight;\n updateHostOffset();\n }\n }\n });\n resizeObserver.observe(container as HTMLElement);\n return () => {\n scrollTarget.removeEventListener('scroll', handleScroll);\n resizeObserver?.disconnect();\n };\n }\n };\n\n let cleanup: (() => void) | undefined;\n\n if (getCurrentInstance()) {\n onMounted(() => {\n isMounted.value = true;\n\n watch(() => props.value.container, (newContainer) => {\n cleanup?.();\n cleanup = attachEvents(newContainer || null);\n }, { immediate: true });\n\n updateHostOffset();\n\n if (props.value.ssrRange || props.value.initialScrollIndex !== undefined) {\n nextTick(() => {\n updateHostOffset();\n const initialIndex = props.value.initialScrollIndex !== undefined\n ? props.value.initialScrollIndex\n : props.value.ssrRange?.start;\n const initialAlign = props.value.initialScrollAlign || 'start';\n\n /* v8 ignore else -- @preserve */\n if (initialIndex !== undefined && initialIndex !== null) {\n scrollToIndex(initialIndex, props.value.ssrRange?.colStart, { align: initialAlign, behavior: 'auto' });\n }\n\n isHydrated.value = true;\n isHydrating.value = true;\n nextTick(() => {\n isHydrating.value = false;\n });\n });\n } else {\n isHydrated.value = true;\n }\n });\n\n onUnmounted(() => {\n cleanup?.();\n });\n }\n\n /**\n * The list of items currently rendered in the DOM.\n */\n const refresh = () => {\n itemSizesX.resize(0);\n itemSizesY.resize(0);\n columnSizes.resize(0);\n measuredColumns.fill(0);\n measuredItemsX.fill(0);\n measuredItemsY.fill(0);\n initializeSizes();\n };\n\n return {\n /**\n * Array of items to be rendered with their calculated offsets and sizes.\n */\n renderedItems,\n /**\n * Total calculated width of all items including gaps.\n */\n totalWidth,\n /**\n * Total calculated height of all items including gaps.\n */\n totalHeight,\n /**\n * Detailed information about the current scroll state.\n * Includes currentIndex, scrollOffset, viewportSize, totalSize, and isScrolling.\n */\n scrollDetails,\n /**\n * Programmatically scroll to a specific row and/or column.\n * @param rowIndex - The row index to scroll to\n * @param colIndex - The column index to scroll to\n * @param options - Alignment and behavior options\n */\n scrollToIndex,\n /**\n * Programmatically scroll to a specific pixel offset.\n * @param x - The pixel offset to scroll to on the X axis\n * @param y - The pixel offset to scroll to on the Y axis\n * @param options - Behavior options\n */\n scrollToOffset,\n /**\n * Stops any currently active programmatic scroll and clears pending corrections.\n */\n stopProgrammaticScroll,\n /**\n * Updates the stored size of an item. Should be called when an item is measured (e.g., via ResizeObserver).\n * @param index - The item index\n * @param width - The measured width\n * @param height - The measured height\n * @param element - The measured element (optional, used for grid column detection)\n */\n updateItemSize,\n /**\n * Updates the stored size of multiple items. Should be called when items are measured (e.g., via ResizeObserver).\n * @param updates - Array of item updates\n */\n updateItemSizes,\n /**\n * Recalculates the host element's offset relative to the scroll container.\n */\n updateHostOffset,\n /**\n * Information about the current visible range of columns.\n */\n columnRange,\n /**\n * Helper to get the width of a specific column based on current configuration.\n * @param index - The column index\n */\n getColumnWidth,\n /**\n * Resets all dynamic measurements and re-initializes from props.\n */\n refresh,\n /**\n * Whether the component has finished its first client-side mount and hydration.\n */\n isHydrated,\n };\n}\n","<script setup lang=\"ts\" generic=\"T\">\nimport type {\n RenderedItem,\n ScrollAlignment,\n ScrollAlignmentOptions,\n ScrollDetails,\n VirtualScrollProps,\n} from '../composables/useVirtualScroll';\nimport type { VNodeChild } from 'vue';\n\nimport { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';\n\nimport {\n DEFAULT_ITEM_SIZE,\n useVirtualScroll,\n} from '../composables/useVirtualScroll';\nimport { getPaddingX, getPaddingY } from '../utils/scroll';\n\nexport interface Props<T = unknown> {\n /** Array of items to be virtualized. */\n items: T[];\n /** Fixed size of each item or a function that returns the size of an item. Pass 0, null or undefined for dynamic size detection. */\n itemSize?: number | ((item: T, index: number) => number) | null;\n /** Direction of the scroll: 'vertical', 'horizontal', or 'both' (grid). */\n direction?: 'vertical' | 'horizontal' | 'both';\n /** Number of items to render before the visible viewport. */\n bufferBefore?: number;\n /** Number of items to render after the visible viewport. */\n bufferAfter?: number;\n /** The scrollable container element or window. If not provided, the host element is used. */\n container?: HTMLElement | Window | null;\n /** Range of items to render for SSR. */\n ssrRange?: {\n start: number;\n end: number;\n colStart?: number;\n colEnd?: number;\n };\n /** Number of columns for bidirectional (grid) scroll. */\n columnCount?: number;\n /** Fixed width of columns or an array/function for column widths. Pass 0, null or undefined for dynamic width. */\n columnWidth?: number | number[] | ((index: number) => number) | null;\n /** The HTML tag to use for the container. */\n containerTag?: string;\n /** The HTML tag to use for the items wrapper. */\n wrapperTag?: string;\n /** The HTML tag to use for each item. */\n itemTag?: string;\n /** Padding at the start of the scroll container. */\n scrollPaddingStart?: number | { x?: number; y?: number; };\n /** Padding at the end of the scroll container. */\n scrollPaddingEnd?: number | { x?: number; y?: number; };\n /** Whether the header slot content is sticky and should be accounted for in scroll padding. If true, header size is automatically measured. */\n stickyHeader?: boolean;\n /** Whether the footer slot content is sticky and should be accounted for in scroll padding. If true, footer size is automatically measured. */\n stickyFooter?: boolean;\n /** Gap between items in pixels (vertical). */\n gap?: number;\n /** Gap between columns in pixels (horizontal/grid). */\n columnGap?: number;\n /** Indices of items that should stick to the top/start. Supports iOS-style pushing effect. */\n stickyIndices?: number[];\n /** Distance from the end of the scrollable area to trigger 'load' event in pixels. */\n loadDistance?: number;\n /** Whether items are currently being loaded. Prevents multiple 'load' events and shows 'loading' slot. */\n loading?: boolean;\n /** Whether to automatically restore scroll position when items are prepended to the list. */\n restoreScrollOnPrepend?: boolean;\n /** Initial scroll index to jump to on mount. */\n initialScrollIndex?: number;\n /** Alignment for the initial scroll index. */\n initialScrollAlign?: ScrollAlignment | ScrollAlignmentOptions;\n /** Default size for items before they are measured. */\n defaultItemSize?: number;\n /** Default width for columns before they are measured. */\n defaultColumnWidth?: number;\n /** Whether to show debug information (buffers and offsets). */\n debug?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props<T>>(), {\n direction: 'vertical',\n bufferBefore: 5,\n bufferAfter: 5,\n columnCount: 0,\n containerTag: 'div',\n wrapperTag: 'div',\n itemTag: 'div',\n scrollPaddingStart: 0,\n scrollPaddingEnd: 0,\n stickyHeader: false,\n stickyFooter: false,\n gap: 0,\n columnGap: 0,\n stickyIndices: () => [],\n loadDistance: 200,\n loading: false,\n restoreScrollOnPrepend: false,\n debug: false,\n});\n\nconst emit = defineEmits<{\n (e: 'scroll', details: ScrollDetails<T>): void;\n (e: 'load', direction: 'vertical' | 'horizontal'): void;\n (e: 'visibleRangeChange', range: { start: number; end: number; colStart: number; colEnd: number; }): void;\n}>();\n\nconst slots = defineSlots<{\n /** Content rendered at the top of the scrollable area. Can be made sticky. */\n header?: (props: Record<string, never>) => VNodeChild;\n /** Slot for rendering each individual item. */\n item?: (props: {\n /** The data item being rendered. */\n item: T;\n /** The index of the item in the items array. */\n index: number;\n /** The current visible range of columns (for grid mode). */\n columnRange: { start: number; end: number; padStart: number; padEnd: number; };\n /** Function to get the width of a specific column. */\n getColumnWidth: (index: number) => number;\n /** Whether this item is configured to be sticky. */\n isSticky?: boolean | undefined;\n /** Whether this item is currently in a sticky state. */\n isStickyActive?: boolean | undefined;\n }) => VNodeChild;\n /** Content shown when `loading` prop is true. */\n loading?: (props: Record<string, never>) => VNodeChild;\n /** Content rendered at the bottom of the scrollable area. Can be made sticky. */\n footer?: (props: Record<string, never>) => VNodeChild;\n}>();\n\nconst hostRef = ref<HTMLElement | null>(null);\nconst wrapperRef = ref<HTMLElement | null>(null);\nconst headerRef = ref<HTMLElement | null>(null);\nconst footerRef = ref<HTMLElement | null>(null);\nconst itemRefs = new Map<number, HTMLElement>();\n\nconst measuredPaddingStart = ref(0);\nconst measuredPaddingEnd = ref(0);\n\nconst isHeaderFooterInsideContainer = computed(() => {\n const container = props.container === undefined\n ? hostRef.value\n : props.container;\n\n return container === hostRef.value\n || (typeof window !== 'undefined' && (container === window || container === null));\n});\n\nconst virtualScrollProps = computed(() => {\n const pStart = props.scrollPaddingStart;\n const pEnd = props.scrollPaddingEnd;\n\n /* v8 ignore start -- @preserve */\n const startX = typeof pStart === 'object'\n ? (pStart.x || 0)\n : (props.direction === 'horizontal' ? (pStart || 0) : 0);\n const startY = typeof pStart === 'object'\n ? (pStart.y || 0)\n : (props.direction !== 'horizontal' ? (pStart || 0) : 0);\n\n const endX = typeof pEnd === 'object'\n ? (pEnd.x || 0)\n : (props.direction === 'horizontal' ? (pEnd || 0) : 0);\n const endY = typeof pEnd === 'object'\n ? (pEnd.y || 0)\n : (props.direction !== 'horizontal' ? (pEnd || 0) : 0);\n /* v8 ignore stop -- @preserve */\n\n return {\n items: props.items,\n itemSize: props.itemSize,\n direction: props.direction,\n bufferBefore: props.bufferBefore,\n bufferAfter: props.bufferAfter,\n container: props.container === undefined\n ? hostRef.value\n : props.container,\n hostElement: wrapperRef.value,\n ssrRange: props.ssrRange,\n columnCount: props.columnCount,\n columnWidth: props.columnWidth,\n scrollPaddingStart: {\n x: startX,\n y: startY + (props.stickyHeader && isHeaderFooterInsideContainer.value ? measuredPaddingStart.value : 0),\n },\n scrollPaddingEnd: {\n x: endX,\n y: endY + (props.stickyFooter && isHeaderFooterInsideContainer.value ? measuredPaddingEnd.value : 0),\n },\n gap: props.gap,\n columnGap: props.columnGap,\n stickyIndices: props.stickyIndices,\n loadDistance: props.loadDistance,\n loading: props.loading,\n restoreScrollOnPrepend: props.restoreScrollOnPrepend,\n initialScrollIndex: props.initialScrollIndex,\n initialScrollAlign: props.initialScrollAlign,\n defaultItemSize: props.defaultItemSize,\n defaultColumnWidth: props.defaultColumnWidth,\n debug: props.debug,\n } as VirtualScrollProps<T>;\n});\n\nconst {\n isHydrated,\n columnRange,\n renderedItems,\n scrollDetails,\n totalHeight,\n totalWidth,\n getColumnWidth,\n scrollToIndex,\n scrollToOffset,\n updateHostOffset,\n updateItemSizes,\n refresh: coreRefresh,\n stopProgrammaticScroll,\n} = useVirtualScroll(virtualScrollProps);\n\n/**\n * Resets all dynamic measurements and re-initializes from props.\n * Also triggers manual re-measurement of all currently rendered items.\n */\nfunction refresh() {\n coreRefresh();\n nextTick(() => {\n const updates: { index: number; inlineSize: number; blockSize: number; element?: HTMLElement; }[] = [];\n\n for (const [ index, el ] of itemRefs.entries()) {\n /* v8 ignore else -- @preserve */\n if (el) {\n updates.push({\n index,\n inlineSize: el.offsetWidth,\n blockSize: el.offsetHeight,\n element: el,\n });\n }\n }\n\n if (updates.length > 0) {\n updateItemSizes(updates);\n }\n });\n}\n\n// Watch for scroll details and emit event\nwatch(scrollDetails, (details, oldDetails) => {\n if (!isHydrated.value) {\n return;\n }\n emit('scroll', details);\n\n if (\n !oldDetails\n || details.range.start !== oldDetails.range.start\n || details.range.end !== oldDetails.range.end\n || details.columnRange.start !== oldDetails.columnRange.start\n || details.columnRange.end !== oldDetails.columnRange.end\n ) {\n emit('visibleRangeChange', {\n start: details.range.start,\n end: details.range.end,\n colStart: details.columnRange.start,\n colEnd: details.columnRange.end,\n });\n }\n\n if (props.loading) {\n return;\n }\n\n // vertical or both\n if (props.direction !== 'horizontal') {\n const remaining = details.totalSize.height - (details.scrollOffset.y + details.viewportSize.height);\n if (remaining <= props.loadDistance) {\n emit('load', 'vertical');\n }\n }\n // horizontal or both\n if (props.direction !== 'vertical') {\n const remaining = details.totalSize.width - (details.scrollOffset.x + details.viewportSize.width);\n if (remaining <= props.loadDistance) {\n emit('load', 'horizontal');\n }\n }\n});\n\nwatch(isHydrated, (hydrated) => {\n /* v8 ignore else -- @preserve */\n if (hydrated) {\n emit('visibleRangeChange', {\n start: scrollDetails.value.range.start,\n end: scrollDetails.value.range.end,\n colStart: scrollDetails.value.columnRange.start,\n colEnd: scrollDetails.value.columnRange.end,\n });\n }\n}, { once: true });\n\n/* v8 ignore next 2 -- @preserve */\nconst hostResizeObserver = typeof window === 'undefined'\n ? null\n : new ResizeObserver(updateHostOffset);\n\n/* v8 ignore next 2 -- @preserve */\nconst itemResizeObserver = typeof window === 'undefined'\n ? null\n : new ResizeObserver((entries) => {\n const updates: { index: number; inlineSize: number; blockSize: number; element?: HTMLElement; }[] = [];\n\n for (const entry of entries) {\n const target = entry.target as HTMLElement;\n const index = Number(target.dataset.index);\n const colIndex = target.dataset.colIndex;\n\n if (colIndex !== undefined) {\n // It's a cell measurement. row index is not strictly needed for column width.\n // We use -1 as a placeholder for row index if it's a cell measurement.\n updates.push({ index: -1, inlineSize: 0, blockSize: 0, element: target });\n } else if (!Number.isNaN(index)) {\n let inlineSize = entry.contentRect.width;\n let blockSize = entry.contentRect.height;\n\n if (entry.borderBoxSize && entry.borderBoxSize.length > 0) {\n inlineSize = entry.borderBoxSize[ 0 ]!.inlineSize;\n blockSize = entry.borderBoxSize[ 0 ]!.blockSize;\n } else {\n // Fallback for older browsers or if borderBoxSize is missing\n inlineSize = target.offsetWidth;\n blockSize = target.offsetHeight;\n }\n\n updates.push({ index, inlineSize, blockSize, element: target });\n }\n }\n\n /* v8 ignore else -- @preserve */\n if (updates.length > 0) {\n updateItemSizes(updates);\n }\n });\n\n/* v8 ignore next 2 -- @preserve */\nconst extraResizeObserver = typeof window === 'undefined'\n ? null\n : new ResizeObserver(() => {\n measuredPaddingStart.value = headerRef.value?.offsetHeight || 0;\n measuredPaddingEnd.value = footerRef.value?.offsetHeight || 0;\n updateHostOffset();\n });\n\nwatch(headerRef, (newEl, oldEl) => {\n /* v8 ignore if -- @preserve */\n if (oldEl) {\n extraResizeObserver?.unobserve(oldEl);\n }\n if (newEl) {\n extraResizeObserver?.observe(newEl);\n }\n}, { immediate: true });\n\nwatch(footerRef, (newEl, oldEl) => {\n /* v8 ignore if -- @preserve */\n if (oldEl) {\n extraResizeObserver?.unobserve(oldEl);\n }\n if (newEl) {\n extraResizeObserver?.observe(newEl);\n }\n}, { immediate: true });\n\nconst firstRenderedIndex = computed(() => renderedItems.value[ 0 ]?.index);\nwatch(firstRenderedIndex, (newIdx, oldIdx) => {\n if (props.direction === 'both') {\n /* v8 ignore else -- @preserve */\n if (oldIdx !== undefined) {\n const oldEl = itemRefs.get(oldIdx);\n if (oldEl) {\n oldEl.querySelectorAll('[data-col-index]').forEach((c) => itemResizeObserver?.unobserve(c));\n }\n }\n if (newIdx !== undefined) {\n const newEl = itemRefs.get(newIdx);\n /* v8 ignore else -- @preserve */\n if (newEl) {\n newEl.querySelectorAll('[data-col-index]').forEach((c) => itemResizeObserver?.observe(c));\n }\n }\n }\n}, { flush: 'post' });\n\nonMounted(() => {\n /* v8 ignore else -- @preserve */\n if (hostRef.value) {\n hostResizeObserver?.observe(hostRef.value);\n }\n\n // Re-observe items that were set before observer was ready\n for (const el of itemRefs.values()) {\n itemResizeObserver?.observe(el);\n }\n\n // Observe cells of the first rendered item\n /* v8 ignore else -- @preserve */\n if (firstRenderedIndex.value !== undefined) {\n const el = itemRefs.get(firstRenderedIndex.value);\n /* v8 ignore else -- @preserve */\n if (el) {\n el.querySelectorAll('[data-col-index]').forEach((c) => itemResizeObserver?.observe(c));\n }\n }\n});\n\nwatch([ hostRef, wrapperRef ], ([ newHost ], [ oldHost ]) => {\n if (oldHost) {\n hostResizeObserver?.unobserve(oldHost);\n }\n if (newHost) {\n hostResizeObserver?.observe(newHost);\n }\n});\n\nfunction setItemRef(el: unknown, index: number) {\n if (el) {\n itemRefs.set(index, el as HTMLElement);\n itemResizeObserver?.observe(el as HTMLElement);\n } else {\n const oldEl = itemRefs.get(index);\n /* v8 ignore else -- @preserve */\n if (oldEl) {\n itemResizeObserver?.unobserve(oldEl);\n itemRefs.delete(index);\n }\n }\n}\n\nfunction handleKeyDown(event: KeyboardEvent) {\n stopProgrammaticScroll();\n const { viewportSize, scrollOffset } = scrollDetails.value;\n const isHorizontal = props.direction !== 'vertical';\n const isVertical = props.direction !== 'horizontal';\n\n if (event.key === 'Home') {\n event.preventDefault();\n scrollToIndex(0, 0, 'start');\n return;\n }\n if (event.key === 'End') {\n event.preventDefault();\n const lastItemIndex = props.items.length - 1;\n const lastColIndex = (props.columnCount || 0) > 0 ? props.columnCount - 1 : 0;\n\n if (isHorizontal) {\n if (isVertical) {\n scrollToIndex(lastItemIndex, lastColIndex, 'end');\n } else {\n scrollToIndex(0, lastItemIndex, 'end');\n }\n } else {\n scrollToIndex(lastItemIndex, 0, 'end');\n }\n return;\n }\n if (event.key === 'ArrowUp') {\n event.preventDefault();\n scrollToOffset(null, scrollOffset.y - DEFAULT_ITEM_SIZE);\n return;\n }\n if (event.key === 'ArrowDown') {\n event.preventDefault();\n scrollToOffset(null, scrollOffset.y + DEFAULT_ITEM_SIZE);\n return;\n }\n if (event.key === 'ArrowLeft') {\n event.preventDefault();\n scrollToOffset(scrollOffset.x - DEFAULT_ITEM_SIZE, null);\n return;\n }\n if (event.key === 'ArrowRight') {\n event.preventDefault();\n scrollToOffset(scrollOffset.x + DEFAULT_ITEM_SIZE, null);\n return;\n }\n if (event.key === 'PageUp') {\n event.preventDefault();\n scrollToOffset(\n !isVertical && isHorizontal ? scrollOffset.x - viewportSize.width : null,\n isVertical ? scrollOffset.y - viewportSize.height : null,\n );\n return;\n }\n if (event.key === 'PageDown') {\n event.preventDefault();\n scrollToOffset(\n !isVertical && isHorizontal ? scrollOffset.x + viewportSize.width : null,\n isVertical ? scrollOffset.y + viewportSize.height : null,\n );\n }\n}\n\nonUnmounted(() => {\n hostResizeObserver?.disconnect();\n itemResizeObserver?.disconnect();\n extraResizeObserver?.disconnect();\n});\n\nconst isWindowContainer = computed(() => {\n const c = props.container;\n if (\n c === null\n // window\n || (typeof window !== 'undefined' && c === window)\n ) {\n return true;\n }\n\n // body\n if (c && typeof c === 'object' && 'tagName' in c) {\n return (c as HTMLElement).tagName === 'BODY';\n }\n\n return false;\n});\n\nconst containerStyle = computed(() => {\n if (isWindowContainer.value) {\n return {\n ...(props.direction !== 'vertical' ? { whiteSpace: 'nowrap' as const } : {}),\n };\n }\n\n if (props.containerTag === 'table') {\n return {\n minInlineSize: props.direction === 'vertical' ? '100%' : 'auto',\n };\n }\n\n return {\n ...(props.direction !== 'vertical' ? { whiteSpace: 'nowrap' as const } : {}),\n };\n});\n\nconst wrapperStyle = computed(() => ({\n inlineSize: props.direction === 'vertical' ? '100%' : `${ totalWidth.value }px`,\n blockSize: props.direction === 'horizontal' ? '100%' : `${ totalHeight.value }px`,\n}));\n\nconst loadingStyle = computed(() => {\n const isHorizontal = props.direction === 'horizontal';\n\n return {\n display: isHorizontal ? 'inline-block' : 'block',\n ...(isHorizontal ? { blockSize: '100%', verticalAlign: 'top' } : { inlineSize: '100%' }),\n };\n});\n\nconst spacerStyle = computed(() => ({\n inlineSize: props.direction === 'vertical' ? '1px' : `${ totalWidth.value }px`,\n blockSize: props.direction === 'horizontal' ? '1px' : `${ totalHeight.value }px`,\n}));\n\nfunction getItemStyle(item: RenderedItem<T>) {\n const isVertical = props.direction === 'vertical';\n const isHorizontal = props.direction === 'horizontal';\n const isBoth = props.direction === 'both';\n const isDynamic = props.itemSize === undefined || props.itemSize === null || props.itemSize === 0;\n\n const style: Record<string, string | number | undefined> = {\n blockSize: isHorizontal ? '100%' : (!isDynamic ? `${ item.size.height }px` : 'auto'),\n };\n\n if (isVertical && props.containerTag === 'table') {\n style.minInlineSize = '100%';\n } else {\n style.inlineSize = isVertical ? '100%' : (!isDynamic ? `${ item.size.width }px` : 'auto');\n }\n\n if (isDynamic) {\n if (!isVertical) {\n style.minInlineSize = '1px';\n }\n if (!isHorizontal) {\n style.minBlockSize = '1px';\n }\n }\n\n if (isHydrated.value) {\n if (item.isStickyActive) {\n if (isVertical || isBoth) {\n style.insetBlockStart = `${ getPaddingY(props.scrollPaddingStart, props.direction) }px`;\n }\n\n if (isHorizontal || isBoth) {\n style.insetInlineStart = `${ getPaddingX(props.scrollPaddingStart, props.direction) }px`;\n }\n\n style.transform = `translate(${ item.stickyOffset.x }px, ${ item.stickyOffset.y }px)`;\n } else {\n style.transform = `translate(${ item.offset.x }px, ${ item.offset.y }px)`;\n }\n }\n\n return style;\n}\n\nconst isDebug = computed(() => props.debug);\nconst isTable = computed(() => props.containerTag === 'table');\nconst headerTag = computed(() => isTable.value ? 'thead' : 'div');\nconst footerTag = computed(() => isTable.value ? 'tfoot' : 'div');\n\ndefineExpose({\n scrollDetails,\n columnRange,\n getColumnWidth,\n scrollToIndex,\n scrollToOffset,\n refresh,\n stopProgrammaticScroll,\n});\n</script>\n\n<template>\n <component\n :is=\"containerTag\"\n ref=\"hostRef\"\n class=\"virtual-scroll-container\"\n :class=\"[\n `virtual-scroll--${ direction }`,\n {\n 'virtual-scroll--hydrated': isHydrated,\n 'virtual-scroll--window': isWindowContainer,\n 'virtual-scroll--table': isTable,\n },\n ]\"\n :style=\"containerStyle\"\n tabindex=\"0\"\n @keydown=\"handleKeyDown\"\n @wheel.passive=\"stopProgrammaticScroll\"\n @pointerdown.passive=\"stopProgrammaticScroll\"\n @touchstart.passive=\"stopProgrammaticScroll\"\n >\n <!-- v8 ignore start -->\n <component\n :is=\"headerTag\"\n v-if=\"slots.header\"\n ref=\"headerRef\"\n class=\"virtual-scroll-header\"\n :class=\"{ 'virtual-scroll--sticky': stickyHeader }\"\n >\n <slot name=\"header\" />\n </component>\n <!-- v8 ignore stop -->\n\n <component\n :is=\"wrapperTag\"\n ref=\"wrapperRef\"\n class=\"virtual-scroll-wrapper\"\n :style=\"wrapperStyle\"\n >\n <!-- Phantom element to push scroll height -->\n <!-- v8 ignore start -->\n <component\n :is=\"itemTag\"\n v-if=\"isTable\"\n class=\"virtual-scroll-spacer\"\n :style=\"spacerStyle\"\n >\n <td style=\"padding: 0; border: none; block-size: inherit;\" />\n </component>\n <!-- v8 ignore stop -->\n\n <component\n :is=\"itemTag\"\n v-for=\"renderedItem in renderedItems\"\n :key=\"renderedItem.index\"\n :ref=\"(el: unknown) => setItemRef(el, renderedItem.index)\"\n :data-index=\"renderedItem.index\"\n class=\"virtual-scroll-item\"\n :class=\"{\n 'virtual-scroll--sticky': renderedItem.isStickyActive,\n 'virtual-scroll--debug': isDebug,\n }\"\n :style=\"getItemStyle(renderedItem)\"\n >\n <slot\n name=\"item\"\n :item=\"renderedItem.item\"\n :index=\"renderedItem.index\"\n :column-range=\"columnRange\"\n :get-column-width=\"getColumnWidth\"\n :is-sticky=\"renderedItem.isSticky\"\n :is-sticky-active=\"renderedItem.isStickyActive\"\n />\n <div v-if=\"isDebug\" class=\"virtual-scroll-debug-info\">\n #{{ renderedItem.index }} ({{ Math.round(renderedItem.offset.x) }}, {{ Math.round(renderedItem.offset.y) }})\n </div>\n </component>\n </component>\n\n <!-- v8 ignore start -->\n <div\n v-if=\"loading && slots.loading\"\n class=\"virtual-scroll-loading\"\n :style=\"loadingStyle\"\n >\n <slot name=\"loading\" />\n </div>\n\n <component\n :is=\"footerTag\"\n v-if=\"slots.footer\"\n ref=\"footerRef\"\n class=\"virtual-scroll-footer\"\n :class=\"{ 'virtual-scroll--sticky': stickyFooter }\"\n >\n <slot name=\"footer\" />\n </component>\n <!-- v8 ignore stop -->\n </component>\n</template>\n\n<style scoped>\n.virtual-scroll-container {\n position: relative;\n block-size: 100%;\n inline-size: 100%;\n outline-offset: 1px;\n\n &:not(.virtual-scroll--window) {\n overflow: auto;\n overscroll-behavior: contain;\n }\n\n &.virtual-scroll--table {\n display: block;\n }\n}\n\n.virtual-scroll--horizontal {\n white-space: nowrap;\n}\n\n.virtual-scroll-wrapper {\n contain: layout;\n position: relative;\n\n :where(.virtual-scroll--hydrated > & > .virtual-scroll-item) {\n position: absolute;\n inset-block-start: 0;\n inset-inline-start: 0;\n }\n}\n\n.virtual-scroll-item {\n box-sizing: border-box;\n will-change: transform;\n\n &:where(.virtual-scroll--debug) {\n outline: 1px dashed rgba(255, 0, 0, 0.5);\n background-color: rgba(255, 0, 0, 0.05);\n\n &:where(:hover) {\n background-color: rgba(255, 0, 0, 0.1);\n z-index: 100;\n }\n }\n}\n\n.virtual-scroll-debug-info {\n position: absolute;\n inset-block-start: 2px;\n inset-inline-end: 2px;\n background: rgba(0, 0, 0, 0.7);\n color: white;\n font-size: 10px;\n padding: 2px 4px;\n border-radius: 4px;\n pointer-events: none;\n z-index: 100;\n font-family: monospace;\n}\n\n.virtual-scroll-spacer {\n pointer-events: none;\n}\n\n.virtual-scroll-header,\n.virtual-scroll-footer {\n position: relative;\n z-index: 20;\n}\n\n.virtual-scroll--sticky {\n position: sticky;\n\n &:where(.virtual-scroll-header) {\n inset-block-start: 0;\n inset-inline-start: 0;\n min-inline-size: 100%;\n box-sizing: border-box;\n }\n\n &:where(.virtual-scroll-footer) {\n inset-block-end: 0;\n inset-inline-start: 0;\n min-inline-size: 100%;\n box-sizing: border-box;\n }\n\n &:where(.virtual-scroll-item) {\n z-index: 10;\n }\n}\n\n:is(tbody.virtual-scroll-wrapper, thead.virtual-scroll-header, tfoot.virtual-scroll-footer) {\n display: inline-flex;\n min-inline-size: 100%;\n & > :deep(tr) {\n display: inline-flex;\n min-inline-size: 100%;\n\n & > :is(td, th) {\n display: inline-block;\n align-items: center;\n }\n }\n}\n</style>\n","<script setup lang=\"ts\" generic=\"T\">\nimport type {\n RenderedItem,\n ScrollAlignment,\n ScrollAlignmentOptions,\n ScrollDetails,\n VirtualScrollProps,\n} from '../composables/useVirtualScroll';\nimport type { VNodeChild } from 'vue';\n\nimport { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';\n\nimport {\n DEFAULT_ITEM_SIZE,\n useVirtualScroll,\n} from '../composables/useVirtualScroll';\nimport { getPaddingX, getPaddingY } from '../utils/scroll';\n\nexport interface Props<T = unknown> {\n /** Array of items to be virtualized. */\n items: T[];\n /** Fixed size of each item or a function that returns the size of an item. Pass 0, null or undefined for dynamic size detection. */\n itemSize?: number | ((item: T, index: number) => number) | null;\n /** Direction of the scroll: 'vertical', 'horizontal', or 'both' (grid). */\n direction?: 'vertical' | 'horizontal' | 'both';\n /** Number of items to render before the visible viewport. */\n bufferBefore?: number;\n /** Number of items to render after the visible viewport. */\n bufferAfter?: number;\n /** The scrollable container element or window. If not provided, the host element is used. */\n container?: HTMLElement | Window | null;\n /** Range of items to render for SSR. */\n ssrRange?: {\n start: number;\n end: number;\n colStart?: number;\n colEnd?: number;\n };\n /** Number of columns for bidirectional (grid) scroll. */\n columnCount?: number;\n /** Fixed width of columns or an array/function for column widths. Pass 0, null or undefined for dynamic width. */\n columnWidth?: number | number[] | ((index: number) => number) | null;\n /** The HTML tag to use for the container. */\n containerTag?: string;\n /** The HTML tag to use for the items wrapper. */\n wrapperTag?: string;\n /** The HTML tag to use for each item. */\n itemTag?: string;\n /** Padding at the start of the scroll container. */\n scrollPaddingStart?: number | { x?: number; y?: number; };\n /** Padding at the end of the scroll container. */\n scrollPaddingEnd?: number | { x?: number; y?: number; };\n /** Whether the header slot content is sticky and should be accounted for in scroll padding. If true, header size is automatically measured. */\n stickyHeader?: boolean;\n /** Whether the footer slot content is sticky and should be accounted for in scroll padding. If true, footer size is automatically measured. */\n stickyFooter?: boolean;\n /** Gap between items in pixels (vertical). */\n gap?: number;\n /** Gap between columns in pixels (horizontal/grid). */\n columnGap?: number;\n /** Indices of items that should stick to the top/start. Supports iOS-style pushing effect. */\n stickyIndices?: number[];\n /** Distance from the end of the scrollable area to trigger 'load' event in pixels. */\n loadDistance?: number;\n /** Whether items are currently being loaded. Prevents multiple 'load' events and shows 'loading' slot. */\n loading?: boolean;\n /** Whether to automatically restore scroll position when items are prepended to the list. */\n restoreScrollOnPrepend?: boolean;\n /** Initial scroll index to jump to on mount. */\n initialScrollIndex?: number;\n /** Alignment for the initial scroll index. */\n initialScrollAlign?: ScrollAlignment | ScrollAlignmentOptions;\n /** Default size for items before they are measured. */\n defaultItemSize?: number;\n /** Default width for columns before they are measured. */\n defaultColumnWidth?: number;\n /** Whether to show debug information (buffers and offsets). */\n debug?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props<T>>(), {\n direction: 'vertical',\n bufferBefore: 5,\n bufferAfter: 5,\n columnCount: 0,\n containerTag: 'div',\n wrapperTag: 'div',\n itemTag: 'div',\n scrollPaddingStart: 0,\n scrollPaddingEnd: 0,\n stickyHeader: false,\n stickyFooter: false,\n gap: 0,\n columnGap: 0,\n stickyIndices: () => [],\n loadDistance: 200,\n loading: false,\n restoreScrollOnPrepend: false,\n debug: false,\n});\n\nconst emit = defineEmits<{\n (e: 'scroll', details: ScrollDetails<T>): void;\n (e: 'load', direction: 'vertical' | 'horizontal'): void;\n (e: 'visibleRangeChange', range: { start: number; end: number; colStart: number; colEnd: number; }): void;\n}>();\n\nconst slots = defineSlots<{\n /** Content rendered at the top of the scrollable area. Can be made sticky. */\n header?: (props: Record<string, never>) => VNodeChild;\n /** Slot for rendering each individual item. */\n item?: (props: {\n /** The data item being rendered. */\n item: T;\n /** The index of the item in the items array. */\n index: number;\n /** The current visible range of columns (for grid mode). */\n columnRange: { start: number; end: number; padStart: number; padEnd: number; };\n /** Function to get the width of a specific column. */\n getColumnWidth: (index: number) => number;\n /** Whether this item is configured to be sticky. */\n isSticky?: boolean | undefined;\n /** Whether this item is currently in a sticky state. */\n isStickyActive?: boolean | undefined;\n }) => VNodeChild;\n /** Content shown when `loading` prop is true. */\n loading?: (props: Record<string, never>) => VNodeChild;\n /** Content rendered at the bottom of the scrollable area. Can be made sticky. */\n footer?: (props: Record<string, never>) => VNodeChild;\n}>();\n\nconst hostRef = ref<HTMLElement | null>(null);\nconst wrapperRef = ref<HTMLElement | null>(null);\nconst headerRef = ref<HTMLElement | null>(null);\nconst footerRef = ref<HTMLElement | null>(null);\nconst itemRefs = new Map<number, HTMLElement>();\n\nconst measuredPaddingStart = ref(0);\nconst measuredPaddingEnd = ref(0);\n\nconst isHeaderFooterInsideContainer = computed(() => {\n const container = props.container === undefined\n ? hostRef.value\n : props.container;\n\n return container === hostRef.value\n || (typeof window !== 'undefined' && (container === window || container === null));\n});\n\nconst virtualScrollProps = computed(() => {\n const pStart = props.scrollPaddingStart;\n const pEnd = props.scrollPaddingEnd;\n\n /* v8 ignore start -- @preserve */\n const startX = typeof pStart === 'object'\n ? (pStart.x || 0)\n : (props.direction === 'horizontal' ? (pStart || 0) : 0);\n const startY = typeof pStart === 'object'\n ? (pStart.y || 0)\n : (props.direction !== 'horizontal' ? (pStart || 0) : 0);\n\n const endX = typeof pEnd === 'object'\n ? (pEnd.x || 0)\n : (props.direction === 'horizontal' ? (pEnd || 0) : 0);\n const endY = typeof pEnd === 'object'\n ? (pEnd.y || 0)\n : (props.direction !== 'horizontal' ? (pEnd || 0) : 0);\n /* v8 ignore stop -- @preserve */\n\n return {\n items: props.items,\n itemSize: props.itemSize,\n direction: props.direction,\n bufferBefore: props.bufferBefore,\n bufferAfter: props.bufferAfter,\n container: props.container === undefined\n ? hostRef.value\n : props.container,\n hostElement: wrapperRef.value,\n ssrRange: props.ssrRange,\n columnCount: props.columnCount,\n columnWidth: props.columnWidth,\n scrollPaddingStart: {\n x: startX,\n y: startY + (props.stickyHeader && isHeaderFooterInsideContainer.value ? measuredPaddingStart.value : 0),\n },\n scrollPaddingEnd: {\n x: endX,\n y: endY + (props.stickyFooter && isHeaderFooterInsideContainer.value ? measuredPaddingEnd.value : 0),\n },\n gap: props.gap,\n columnGap: props.columnGap,\n stickyIndices: props.stickyIndices,\n loadDistance: props.loadDistance,\n loading: props.loading,\n restoreScrollOnPrepend: props.restoreScrollOnPrepend,\n initialScrollIndex: props.initialScrollIndex,\n initialScrollAlign: props.initialScrollAlign,\n defaultItemSize: props.defaultItemSize,\n defaultColumnWidth: props.defaultColumnWidth,\n debug: props.debug,\n } as VirtualScrollProps<T>;\n});\n\nconst {\n isHydrated,\n columnRange,\n renderedItems,\n scrollDetails,\n totalHeight,\n totalWidth,\n getColumnWidth,\n scrollToIndex,\n scrollToOffset,\n updateHostOffset,\n updateItemSizes,\n refresh: coreRefresh,\n stopProgrammaticScroll,\n} = useVirtualScroll(virtualScrollProps);\n\n/**\n * Resets all dynamic measurements and re-initializes from props.\n * Also triggers manual re-measurement of all currently rendered items.\n */\nfunction refresh() {\n coreRefresh();\n nextTick(() => {\n const updates: { index: number; inlineSize: number; blockSize: number; element?: HTMLElement; }[] = [];\n\n for (const [ index, el ] of itemRefs.entries()) {\n /* v8 ignore else -- @preserve */\n if (el) {\n updates.push({\n index,\n inlineSize: el.offsetWidth,\n blockSize: el.offsetHeight,\n element: el,\n });\n }\n }\n\n if (updates.length > 0) {\n updateItemSizes(updates);\n }\n });\n}\n\n// Watch for scroll details and emit event\nwatch(scrollDetails, (details, oldDetails) => {\n if (!isHydrated.value) {\n return;\n }\n emit('scroll', details);\n\n if (\n !oldDetails\n || details.range.start !== oldDetails.range.start\n || details.range.end !== oldDetails.range.end\n || details.columnRange.start !== oldDetails.columnRange.start\n || details.columnRange.end !== oldDetails.columnRange.end\n ) {\n emit('visibleRangeChange', {\n start: details.range.start,\n end: details.range.end,\n colStart: details.columnRange.start,\n colEnd: details.columnRange.end,\n });\n }\n\n if (props.loading) {\n return;\n }\n\n // vertical or both\n if (props.direction !== 'horizontal') {\n const remaining = details.totalSize.height - (details.scrollOffset.y + details.viewportSize.height);\n if (remaining <= props.loadDistance) {\n emit('load', 'vertical');\n }\n }\n // horizontal or both\n if (props.direction !== 'vertical') {\n const remaining = details.totalSize.width - (details.scrollOffset.x + details.viewportSize.width);\n if (remaining <= props.loadDistance) {\n emit('load', 'horizontal');\n }\n }\n});\n\nwatch(isHydrated, (hydrated) => {\n /* v8 ignore else -- @preserve */\n if (hydrated) {\n emit('visibleRangeChange', {\n start: scrollDetails.value.range.start,\n end: scrollDetails.value.range.end,\n colStart: scrollDetails.value.columnRange.start,\n colEnd: scrollDetails.value.columnRange.end,\n });\n }\n}, { once: true });\n\n/* v8 ignore next 2 -- @preserve */\nconst hostResizeObserver = typeof window === 'undefined'\n ? null\n : new ResizeObserver(updateHostOffset);\n\n/* v8 ignore next 2 -- @preserve */\nconst itemResizeObserver = typeof window === 'undefined'\n ? null\n : new ResizeObserver((entries) => {\n const updates: { index: number; inlineSize: number; blockSize: number; element?: HTMLElement; }[] = [];\n\n for (const entry of entries) {\n const target = entry.target as HTMLElement;\n const index = Number(target.dataset.index);\n const colIndex = target.dataset.colIndex;\n\n if (colIndex !== undefined) {\n // It's a cell measurement. row index is not strictly needed for column width.\n // We use -1 as a placeholder for row index if it's a cell measurement.\n updates.push({ index: -1, inlineSize: 0, blockSize: 0, element: target });\n } else if (!Number.isNaN(index)) {\n let inlineSize = entry.contentRect.width;\n let blockSize = entry.contentRect.height;\n\n if (entry.borderBoxSize && entry.borderBoxSize.length > 0) {\n inlineSize = entry.borderBoxSize[ 0 ]!.inlineSize;\n blockSize = entry.borderBoxSize[ 0 ]!.blockSize;\n } else {\n // Fallback for older browsers or if borderBoxSize is missing\n inlineSize = target.offsetWidth;\n blockSize = target.offsetHeight;\n }\n\n updates.push({ index, inlineSize, blockSize, element: target });\n }\n }\n\n /* v8 ignore else -- @preserve */\n if (updates.length > 0) {\n updateItemSizes(updates);\n }\n });\n\n/* v8 ignore next 2 -- @preserve */\nconst extraResizeObserver = typeof window === 'undefined'\n ? null\n : new ResizeObserver(() => {\n measuredPaddingStart.value = headerRef.value?.offsetHeight || 0;\n measuredPaddingEnd.value = footerRef.value?.offsetHeight || 0;\n updateHostOffset();\n });\n\nwatch(headerRef, (newEl, oldEl) => {\n /* v8 ignore if -- @preserve */\n if (oldEl) {\n extraResizeObserver?.unobserve(oldEl);\n }\n if (newEl) {\n extraResizeObserver?.observe(newEl);\n }\n}, { immediate: true });\n\nwatch(footerRef, (newEl, oldEl) => {\n /* v8 ignore if -- @preserve */\n if (oldEl) {\n extraResizeObserver?.unobserve(oldEl);\n }\n if (newEl) {\n extraResizeObserver?.observe(newEl);\n }\n}, { immediate: true });\n\nconst firstRenderedIndex = computed(() => renderedItems.value[ 0 ]?.index);\nwatch(firstRenderedIndex, (newIdx, oldIdx) => {\n if (props.direction === 'both') {\n /* v8 ignore else -- @preserve */\n if (oldIdx !== undefined) {\n const oldEl = itemRefs.get(oldIdx);\n if (oldEl) {\n oldEl.querySelectorAll('[data-col-index]').forEach((c) => itemResizeObserver?.unobserve(c));\n }\n }\n if (newIdx !== undefined) {\n const newEl = itemRefs.get(newIdx);\n /* v8 ignore else -- @preserve */\n if (newEl) {\n newEl.querySelectorAll('[data-col-index]').forEach((c) => itemResizeObserver?.observe(c));\n }\n }\n }\n}, { flush: 'post' });\n\nonMounted(() => {\n /* v8 ignore else -- @preserve */\n if (hostRef.value) {\n hostResizeObserver?.observe(hostRef.value);\n }\n\n // Re-observe items that were set before observer was ready\n for (const el of itemRefs.values()) {\n itemResizeObserver?.observe(el);\n }\n\n // Observe cells of the first rendered item\n /* v8 ignore else -- @preserve */\n if (firstRenderedIndex.value !== undefined) {\n const el = itemRefs.get(firstRenderedIndex.value);\n /* v8 ignore else -- @preserve */\n if (el) {\n el.querySelectorAll('[data-col-index]').forEach((c) => itemResizeObserver?.observe(c));\n }\n }\n});\n\nwatch([ hostRef, wrapperRef ], ([ newHost ], [ oldHost ]) => {\n if (oldHost) {\n hostResizeObserver?.unobserve(oldHost);\n }\n if (newHost) {\n hostResizeObserver?.observe(newHost);\n }\n});\n\nfunction setItemRef(el: unknown, index: number) {\n if (el) {\n itemRefs.set(index, el as HTMLElement);\n itemResizeObserver?.observe(el as HTMLElement);\n } else {\n const oldEl = itemRefs.get(index);\n /* v8 ignore else -- @preserve */\n if (oldEl) {\n itemResizeObserver?.unobserve(oldEl);\n itemRefs.delete(index);\n }\n }\n}\n\nfunction handleKeyDown(event: KeyboardEvent) {\n stopProgrammaticScroll();\n const { viewportSize, scrollOffset } = scrollDetails.value;\n const isHorizontal = props.direction !== 'vertical';\n const isVertical = props.direction !== 'horizontal';\n\n if (event.key === 'Home') {\n event.preventDefault();\n scrollToIndex(0, 0, 'start');\n return;\n }\n if (event.key === 'End') {\n event.preventDefault();\n const lastItemIndex = props.items.length - 1;\n const lastColIndex = (props.columnCount || 0) > 0 ? props.columnCount - 1 : 0;\n\n if (isHorizontal) {\n if (isVertical) {\n scrollToIndex(lastItemIndex, lastColIndex, 'end');\n } else {\n scrollToIndex(0, lastItemIndex, 'end');\n }\n } else {\n scrollToIndex(lastItemIndex, 0, 'end');\n }\n return;\n }\n if (event.key === 'ArrowUp') {\n event.preventDefault();\n scrollToOffset(null, scrollOffset.y - DEFAULT_ITEM_SIZE);\n return;\n }\n if (event.key === 'ArrowDown') {\n event.preventDefault();\n scrollToOffset(null, scrollOffset.y + DEFAULT_ITEM_SIZE);\n return;\n }\n if (event.key === 'ArrowLeft') {\n event.preventDefault();\n scrollToOffset(scrollOffset.x - DEFAULT_ITEM_SIZE, null);\n return;\n }\n if (event.key === 'ArrowRight') {\n event.preventDefault();\n scrollToOffset(scrollOffset.x + DEFAULT_ITEM_SIZE, null);\n return;\n }\n if (event.key === 'PageUp') {\n event.preventDefault();\n scrollToOffset(\n !isVertical && isHorizontal ? scrollOffset.x - viewportSize.width : null,\n isVertical ? scrollOffset.y - viewportSize.height : null,\n );\n return;\n }\n if (event.key === 'PageDown') {\n event.preventDefault();\n scrollToOffset(\n !isVertical && isHorizontal ? scrollOffset.x + viewportSize.width : null,\n isVertical ? scrollOffset.y + viewportSize.height : null,\n );\n }\n}\n\nonUnmounted(() => {\n hostResizeObserver?.disconnect();\n itemResizeObserver?.disconnect();\n extraResizeObserver?.disconnect();\n});\n\nconst isWindowContainer = computed(() => {\n const c = props.container;\n if (\n c === null\n // window\n || (typeof window !== 'undefined' && c === window)\n ) {\n return true;\n }\n\n // body\n if (c && typeof c === 'object' && 'tagName' in c) {\n return (c as HTMLElement).tagName === 'BODY';\n }\n\n return false;\n});\n\nconst containerStyle = computed(() => {\n if (isWindowContainer.value) {\n return {\n ...(props.direction !== 'vertical' ? { whiteSpace: 'nowrap' as const } : {}),\n };\n }\n\n if (props.containerTag === 'table') {\n return {\n minInlineSize: props.direction === 'vertical' ? '100%' : 'auto',\n };\n }\n\n return {\n ...(props.direction !== 'vertical' ? { whiteSpace: 'nowrap' as const } : {}),\n };\n});\n\nconst wrapperStyle = computed(() => ({\n inlineSize: props.direction === 'vertical' ? '100%' : `${ totalWidth.value }px`,\n blockSize: props.direction === 'horizontal' ? '100%' : `${ totalHeight.value }px`,\n}));\n\nconst loadingStyle = computed(() => {\n const isHorizontal = props.direction === 'horizontal';\n\n return {\n display: isHorizontal ? 'inline-block' : 'block',\n ...(isHorizontal ? { blockSize: '100%', verticalAlign: 'top' } : { inlineSize: '100%' }),\n };\n});\n\nconst spacerStyle = computed(() => ({\n inlineSize: props.direction === 'vertical' ? '1px' : `${ totalWidth.value }px`,\n blockSize: props.direction === 'horizontal' ? '1px' : `${ totalHeight.value }px`,\n}));\n\nfunction getItemStyle(item: RenderedItem<T>) {\n const isVertical = props.direction === 'vertical';\n const isHorizontal = props.direction === 'horizontal';\n const isBoth = props.direction === 'both';\n const isDynamic = props.itemSize === undefined || props.itemSize === null || props.itemSize === 0;\n\n const style: Record<string, string | number | undefined> = {\n blockSize: isHorizontal ? '100%' : (!isDynamic ? `${ item.size.height }px` : 'auto'),\n };\n\n if (isVertical && props.containerTag === 'table') {\n style.minInlineSize = '100%';\n } else {\n style.inlineSize = isVertical ? '100%' : (!isDynamic ? `${ item.size.width }px` : 'auto');\n }\n\n if (isDynamic) {\n if (!isVertical) {\n style.minInlineSize = '1px';\n }\n if (!isHorizontal) {\n style.minBlockSize = '1px';\n }\n }\n\n if (isHydrated.value) {\n if (item.isStickyActive) {\n if (isVertical || isBoth) {\n style.insetBlockStart = `${ getPaddingY(props.scrollPaddingStart, props.direction) }px`;\n }\n\n if (isHorizontal || isBoth) {\n style.insetInlineStart = `${ getPaddingX(props.scrollPaddingStart, props.direction) }px`;\n }\n\n style.transform = `translate(${ item.stickyOffset.x }px, ${ item.stickyOffset.y }px)`;\n } else {\n style.transform = `translate(${ item.offset.x }px, ${ item.offset.y }px)`;\n }\n }\n\n return style;\n}\n\nconst isDebug = computed(() => props.debug);\nconst isTable = computed(() => props.containerTag === 'table');\nconst headerTag = computed(() => isTable.value ? 'thead' : 'div');\nconst footerTag = computed(() => isTable.value ? 'tfoot' : 'div');\n\ndefineExpose({\n scrollDetails,\n columnRange,\n getColumnWidth,\n scrollToIndex,\n scrollToOffset,\n refresh,\n stopProgrammaticScroll,\n});\n</script>\n\n<template>\n <component\n :is=\"containerTag\"\n ref=\"hostRef\"\n class=\"virtual-scroll-container\"\n :class=\"[\n `virtual-scroll--${ direction }`,\n {\n 'virtual-scroll--hydrated': isHydrated,\n 'virtual-scroll--window': isWindowContainer,\n 'virtual-scroll--table': isTable,\n },\n ]\"\n :style=\"containerStyle\"\n tabindex=\"0\"\n @keydown=\"handleKeyDown\"\n @wheel.passive=\"stopProgrammaticScroll\"\n @pointerdown.passive=\"stopProgrammaticScroll\"\n @touchstart.passive=\"stopProgrammaticScroll\"\n >\n <!-- v8 ignore start -->\n <component\n :is=\"headerTag\"\n v-if=\"slots.header\"\n ref=\"headerRef\"\n class=\"virtual-scroll-header\"\n :class=\"{ 'virtual-scroll--sticky': stickyHeader }\"\n >\n <slot name=\"header\" />\n </component>\n <!-- v8 ignore stop -->\n\n <component\n :is=\"wrapperTag\"\n ref=\"wrapperRef\"\n class=\"virtual-scroll-wrapper\"\n :style=\"wrapperStyle\"\n >\n <!-- Phantom element to push scroll height -->\n <!-- v8 ignore start -->\n <component\n :is=\"itemTag\"\n v-if=\"isTable\"\n class=\"virtual-scroll-spacer\"\n :style=\"spacerStyle\"\n >\n <td style=\"padding: 0; border: none; block-size: inherit;\" />\n </component>\n <!-- v8 ignore stop -->\n\n <component\n :is=\"itemTag\"\n v-for=\"renderedItem in renderedItems\"\n :key=\"renderedItem.index\"\n :ref=\"(el: unknown) => setItemRef(el, renderedItem.index)\"\n :data-index=\"renderedItem.index\"\n class=\"virtual-scroll-item\"\n :class=\"{\n 'virtual-scroll--sticky': renderedItem.isStickyActive,\n 'virtual-scroll--debug': isDebug,\n }\"\n :style=\"getItemStyle(renderedItem)\"\n >\n <slot\n name=\"item\"\n :item=\"renderedItem.item\"\n :index=\"renderedItem.index\"\n :column-range=\"columnRange\"\n :get-column-width=\"getColumnWidth\"\n :is-sticky=\"renderedItem.isSticky\"\n :is-sticky-active=\"renderedItem.isStickyActive\"\n />\n <div v-if=\"isDebug\" class=\"virtual-scroll-debug-info\">\n #{{ renderedItem.index }} ({{ Math.round(renderedItem.offset.x) }}, {{ Math.round(renderedItem.offset.y) }})\n </div>\n </component>\n </component>\n\n <!-- v8 ignore start -->\n <div\n v-if=\"loading && slots.loading\"\n class=\"virtual-scroll-loading\"\n :style=\"loadingStyle\"\n >\n <slot name=\"loading\" />\n </div>\n\n <component\n :is=\"footerTag\"\n v-if=\"slots.footer\"\n ref=\"footerRef\"\n class=\"virtual-scroll-footer\"\n :class=\"{ 'virtual-scroll--sticky': stickyFooter }\"\n >\n <slot name=\"footer\" />\n </component>\n <!-- v8 ignore stop -->\n </component>\n</template>\n\n<style scoped>\n.virtual-scroll-container {\n position: relative;\n block-size: 100%;\n inline-size: 100%;\n outline-offset: 1px;\n\n &:not(.virtual-scroll--window) {\n overflow: auto;\n overscroll-behavior: contain;\n }\n\n &.virtual-scroll--table {\n display: block;\n }\n}\n\n.virtual-scroll--horizontal {\n white-space: nowrap;\n}\n\n.virtual-scroll-wrapper {\n contain: layout;\n position: relative;\n\n :where(.virtual-scroll--hydrated > & > .virtual-scroll-item) {\n position: absolute;\n inset-block-start: 0;\n inset-inline-start: 0;\n }\n}\n\n.virtual-scroll-item {\n box-sizing: border-box;\n will-change: transform;\n\n &:where(.virtual-scroll--debug) {\n outline: 1px dashed rgba(255, 0, 0, 0.5);\n background-color: rgba(255, 0, 0, 0.05);\n\n &:where(:hover) {\n background-color: rgba(255, 0, 0, 0.1);\n z-index: 100;\n }\n }\n}\n\n.virtual-scroll-debug-info {\n position: absolute;\n inset-block-start: 2px;\n inset-inline-end: 2px;\n background: rgba(0, 0, 0, 0.7);\n color: white;\n font-size: 10px;\n padding: 2px 4px;\n border-radius: 4px;\n pointer-events: none;\n z-index: 100;\n font-family: monospace;\n}\n\n.virtual-scroll-spacer {\n pointer-events: none;\n}\n\n.virtual-scroll-header,\n.virtual-scroll-footer {\n position: relative;\n z-index: 20;\n}\n\n.virtual-scroll--sticky {\n position: sticky;\n\n &:where(.virtual-scroll-header) {\n inset-block-start: 0;\n inset-inline-start: 0;\n min-inline-size: 100%;\n box-sizing: border-box;\n }\n\n &:where(.virtual-scroll-footer) {\n inset-block-end: 0;\n inset-inline-start: 0;\n min-inline-size: 100%;\n box-sizing: border-box;\n }\n\n &:where(.virtual-scroll-item) {\n z-index: 10;\n }\n}\n\n:is(tbody.virtual-scroll-wrapper, thead.virtual-scroll-header, tfoot.virtual-scroll-footer) {\n display: inline-flex;\n min-inline-size: 100%;\n & > :deep(tr) {\n display: inline-flex;\n min-inline-size: 100%;\n\n & > :is(td, th) {\n display: inline-block;\n align-items: center;\n }\n }\n}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["align: ScrollAlignment | ScrollAlignmentOptions | ScrollToIndexOptions | undefined","effectiveAlignX: ScrollAlignment","effectiveAlignY: ScrollAlignment","activeStickyIdx: number | undefined","nextStickyIdx: number | undefined","style: Record<string, string | number | undefined>","scrollTimeout: ReturnType<typeof setTimeout> | undefined","lastItems: T[]","behavior: 'auto' | 'smooth' | undefined","scrollOptions: ScrollToOptions","lastRenderedItems: RenderedItem<T>[]","items: RenderedItem<T>[]","prevStickyIdx: number | undefined","correctionOptions: ScrollToIndexOptions","resizeObserver: ResizeObserver | null","cleanup: (() => void) | undefined"],"sources":["../src/utils/fenwick-tree.ts","../src/utils/scroll.ts","../src/utils/virtual-scroll-logic.ts","../src/composables/useVirtualScroll.ts","../src/components/VirtualScroll.vue","../src/components/VirtualScroll.vue"],"sourcesContent":["/**\n * Fenwick Tree (Binary Indexed Tree) implementation for efficient\n * prefix sum calculations and updates.\n *\n * Provides O(log n) time complexity for both point updates and prefix sum queries.\n */\nexport class FenwickTree {\n private tree: Float64Array;\n private values: Float64Array;\n\n /**\n * Creates a new Fenwick Tree with the specified size.\n *\n * @param size - The number of elements in the tree.\n */\n constructor(size: number) {\n this.tree = new Float64Array(size + 1);\n this.values = new Float64Array(size);\n }\n\n /**\n * Update the value at a specific index and propagate changes throughout the tree.\n *\n * @param index - The 0-based index to update.\n * @param delta - The change in value (new value - old value).\n */\n update(index: number, delta: number): void {\n if (index < 0 || index >= this.values.length) {\n return;\n }\n this.values[ index ] = this.values[ index ]! + delta;\n\n index++; // 1-based index\n while (index < this.tree.length) {\n this.tree[ index ] = this.tree[ index ]! + delta;\n index += index & -index;\n }\n }\n\n /**\n * Get the prefix sum up to a specific index (exclusive).\n *\n * @param index - 0-based index. `query(n)` returns sum of values from index 0 to n-1.\n * @returns Sum of values in range [0, index).\n */\n query(index: number): number {\n let sum = 0;\n while (index > 0) {\n sum += this.tree[ index ] || 0;\n index -= index & -index;\n }\n return sum;\n }\n\n /**\n * Set the individual value at an index without updating the prefix sum tree.\n * Call `rebuild()` after multiple sets to update the tree efficiently in O(n).\n *\n * @param index - The 0-based index.\n * @param value - The new value.\n */\n set(index: number, value: number): void {\n if (index < 0 || index >= this.values.length) {\n return;\n }\n this.values[ index ] = value;\n }\n\n /**\n * Get the number of items in the tree.\n */\n get length(): number {\n return this.values.length;\n }\n\n /**\n * Get the individual value at a specific index.\n *\n * @param index - The 0-based index.\n * @returns The value at the specified index.\n */\n get(index: number): number {\n return this.values[ index ] || 0;\n }\n\n /**\n * Get the underlying values array as a read-only Float64Array.\n *\n * @returns The read-only values array.\n */\n getValues(): Readonly<Float64Array> {\n return this.values;\n }\n\n /**\n * Find the largest index such that the prefix sum is less than or equal to the given value.\n * Highly efficient search used to find which item is at a specific scroll offset.\n *\n * @param value - The prefix sum value to search for.\n * @returns The 0-based index.\n */\n findLowerBound(value: number): number {\n let index = 0;\n const len = this.tree.length;\n let power = 1 << Math.floor(Math.log2(len - 1));\n\n while (power > 0) {\n const nextIndex = index + power;\n if (nextIndex < len) {\n const treeVal = this.tree[ nextIndex ] || 0;\n if (treeVal <= value) {\n index = nextIndex;\n value -= treeVal;\n }\n }\n power >>= 1;\n }\n return index;\n }\n\n /**\n * Rebuild the entire prefix sum tree from the current values array.\n * Time complexity: O(n).\n */\n rebuild(): void {\n this.tree.fill(0);\n for (let i = 0; i < this.values.length; i++) {\n this.tree[ i + 1 ] = this.values[ i ] || 0;\n }\n for (let i = 1; i < this.tree.length; i++) {\n const j = i + (i & -i);\n if (j < this.tree.length) {\n this.tree[ j ] = this.tree[ j ]! + this.tree[ i ]!;\n }\n }\n }\n\n /**\n * Resize the tree while preserving existing values and rebuilding the prefix sums.\n *\n * @param size - The new size of the tree.\n */\n resize(size: number): void {\n if (size === this.values.length) {\n return;\n }\n const newValues = new Float64Array(size);\n newValues.set(this.values.subarray(0, Math.min(size, this.values.length)));\n\n this.values = newValues;\n this.tree = new Float64Array(size + 1);\n this.rebuild();\n }\n\n /**\n * Shift values by a given offset and rebuild the tree.\n * Useful when items are prepended to the list to maintain existing measurements.\n *\n * @param offset - Number of positions to shift. Positive for prepending (shifts right).\n */\n shift(offset: number): void {\n if (offset === 0) {\n return;\n }\n const size = this.values.length;\n const newValues = new Float64Array(size);\n if (offset > 0) {\n newValues.set(this.values.subarray(0, Math.min(size - offset, this.values.length)), offset);\n } else {\n newValues.set(this.values.subarray(-offset));\n }\n this.values = newValues;\n this.rebuild();\n }\n}\n","import type { ScrollDirection, ScrollToIndexOptions } from '../types';\n\n/**\n * Checks if the container is the window object.\n *\n * @param container - The container element or window to check.\n * @returns `true` if the container is the global window object.\n */\nexport function isWindow(container: HTMLElement | Window | null | undefined): container is Window {\n return container === null || (typeof window !== 'undefined' && container === window);\n}\n\n/**\n * Checks if the container is the document body element.\n *\n * @param container - The container element or window to check.\n * @returns `true` if the container is the `<body>` element.\n */\nexport function isBody(container: HTMLElement | Window | null | undefined): container is HTMLElement {\n return !!container && typeof container === 'object' && 'tagName' in container && container.tagName === 'BODY';\n}\n\n/**\n * Checks if the container is window-like (global window or document body).\n *\n * @param container - The container element or window to check.\n * @returns `true` if the container is window or body.\n */\nexport function isWindowLike(container: HTMLElement | Window | null | undefined): boolean {\n return isWindow(container) || isBody(container);\n}\n\n/**\n * Checks if the container is a valid HTML Element with bounding rect support.\n *\n * @param container - The container to check.\n * @returns `true` if the container is an `HTMLElement`.\n */\nexport function isElement(container: HTMLElement | Window | null | undefined): container is HTMLElement {\n return !!container && 'getBoundingClientRect' in container;\n}\n\n/**\n * Checks if the target is an element that supports scrolling.\n *\n * @param target - The event target to check.\n * @returns `true` if the target is an `HTMLElement` with scroll properties.\n */\nexport function isScrollableElement(target: EventTarget | null): target is HTMLElement {\n return !!target && 'scrollLeft' in target;\n}\n\n/**\n * Helper to determine if an options argument is a full `ScrollToIndexOptions` object.\n *\n * @param options - The options object to check.\n * @returns `true` if the options object contains scroll-to-index specific properties.\n */\nexport function isScrollToIndexOptions(options: unknown): options is ScrollToIndexOptions {\n return typeof options === 'object' && options !== null && ('align' in options || 'behavior' in options || 'isCorrection' in options);\n}\n\n/**\n * Extracts the horizontal padding from a padding configuration.\n *\n * @param p - The padding value (number or object with x/y).\n * @param direction - The current scroll direction.\n * @returns The horizontal padding in pixels.\n */\nexport function getPaddingX(p: number | { x?: number; y?: number; } | undefined, direction?: ScrollDirection) {\n if (typeof p === 'object' && p !== null) {\n return p.x || 0;\n }\n return (direction === 'horizontal' || direction === 'both') ? (p || 0) : 0;\n}\n\n/**\n * Extracts the vertical padding from a padding configuration.\n *\n * @param p - The padding value (number or object with x/y).\n * @param direction - The current scroll direction.\n * @returns The vertical padding in pixels.\n */\nexport function getPaddingY(p: number | { x?: number; y?: number; } | undefined, direction?: ScrollDirection) {\n if (typeof p === 'object' && p !== null) {\n return p.y || 0;\n }\n return (direction === 'vertical' || direction === 'both') ? (p || 0) : 0;\n}\n","import type {\n ColumnRangeParams,\n ItemPositionParams,\n ItemStyleParams,\n RangeParams,\n ScrollAlignment,\n ScrollAlignmentOptions,\n ScrollTargetParams,\n ScrollTargetResult,\n ScrollToIndexOptions,\n StickyParams,\n TotalSizeParams,\n} from '../types';\n\nimport { isScrollToIndexOptions } from './scroll';\n\n/**\n * Calculates the target scroll position (relative to content) for a given row/column index and alignment.\n *\n * @param params - The parameters for calculation.\n * @returns The target X and Y positions and item dimensions.\n * @see ScrollTargetParams\n * @see ScrollTargetResult\n */\nexport function calculateScrollTarget(params: ScrollTargetParams): ScrollTargetResult {\n const {\n rowIndex,\n colIndex,\n options,\n itemsLength,\n columnCount,\n direction,\n usableWidth,\n usableHeight,\n totalWidth,\n totalHeight,\n gap,\n columnGap,\n fixedSize,\n fixedWidth,\n relativeScrollX,\n relativeScrollY,\n getItemSizeY,\n getItemSizeX,\n getItemQueryY,\n getItemQueryX,\n getColumnSize,\n getColumnQuery,\n stickyIndices,\n } = params;\n\n let align: ScrollAlignment | ScrollAlignmentOptions | ScrollToIndexOptions | undefined;\n\n if (isScrollToIndexOptions(options)) {\n align = options.align;\n } else {\n align = options as ScrollAlignment | ScrollAlignmentOptions;\n }\n\n const alignX = (typeof align === 'object' ? align.x : align) || 'auto';\n const alignY = (typeof align === 'object' ? align.y : align) || 'auto';\n\n const isVertical = direction === 'vertical' || direction === 'both';\n const isHorizontal = direction === 'horizontal' || direction === 'both';\n\n let targetX = relativeScrollX;\n let targetY = relativeScrollY;\n let itemWidth = 0;\n let itemHeight = 0;\n let effectiveAlignX: ScrollAlignment = alignX === 'auto' ? 'auto' : alignX;\n let effectiveAlignY: ScrollAlignment = alignY === 'auto' ? 'auto' : alignY;\n\n // Y calculation\n if (rowIndex != null) {\n let stickyOffsetY = 0;\n if (isVertical && stickyIndices && stickyIndices.length > 0) {\n let activeStickyIdx: number | undefined;\n let low = 0;\n let high = stickyIndices.length - 1;\n while (low <= high) {\n const mid = (low + high) >>> 1;\n if (stickyIndices[ mid ]! < rowIndex) {\n activeStickyIdx = stickyIndices[ mid ];\n low = mid + 1;\n } else {\n high = mid - 1;\n }\n }\n\n if (activeStickyIdx !== undefined) {\n stickyOffsetY = fixedSize !== null ? fixedSize : getItemSizeY(activeStickyIdx) - gap;\n }\n }\n\n let itemY = 0;\n if (rowIndex >= itemsLength) {\n itemY = totalHeight;\n itemHeight = 0;\n } else {\n itemY = fixedSize !== null ? rowIndex * (fixedSize + gap) : getItemQueryY(rowIndex);\n itemHeight = fixedSize !== null ? fixedSize : getItemSizeY(rowIndex) - gap;\n }\n\n // Apply Y Alignment\n if (alignY === 'start') {\n targetY = itemY - stickyOffsetY;\n } else if (alignY === 'center') {\n targetY = itemY - (usableHeight - itemHeight) / 2;\n } else if (alignY === 'end') {\n targetY = itemY - (usableHeight - itemHeight);\n } else {\n // Auto alignment: stay if visible, otherwise align to nearest edge (minimal movement)\n const isVisibleY = itemHeight <= (usableHeight - stickyOffsetY)\n ? (itemY >= relativeScrollY + stickyOffsetY - 0.5 && (itemY + itemHeight) <= (relativeScrollY + usableHeight + 0.5))\n : (itemY <= relativeScrollY + stickyOffsetY + 0.5 && (itemY + itemHeight) >= (relativeScrollY + usableHeight - 0.5));\n\n if (!isVisibleY) {\n const targetStart = itemY - stickyOffsetY;\n const targetEnd = itemY - (usableHeight - itemHeight);\n\n if (itemHeight <= usableHeight - stickyOffsetY) {\n if (itemY < relativeScrollY + stickyOffsetY) {\n targetY = targetStart;\n effectiveAlignY = 'start';\n } else {\n targetY = targetEnd;\n effectiveAlignY = 'end';\n }\n } else {\n // Large item: minimal movement\n if (Math.abs(targetStart - relativeScrollY) < Math.abs(targetEnd - relativeScrollY)) {\n targetY = targetStart;\n effectiveAlignY = 'start';\n } else {\n targetY = targetEnd;\n effectiveAlignY = 'end';\n }\n }\n }\n }\n }\n\n // X calculation\n if (colIndex != null) {\n let stickyOffsetX = 0;\n if (isHorizontal && stickyIndices && stickyIndices.length > 0 && (direction === 'horizontal' || direction === 'both')) {\n let activeStickyIdx: number | undefined;\n let low = 0;\n let high = stickyIndices.length - 1;\n while (low <= high) {\n const mid = (low + high) >>> 1;\n if (stickyIndices[ mid ]! < colIndex) {\n activeStickyIdx = stickyIndices[ mid ];\n low = mid + 1;\n } else {\n high = mid - 1;\n }\n }\n\n if (activeStickyIdx !== undefined) {\n stickyOffsetX = direction === 'horizontal'\n ? (fixedSize !== null ? fixedSize : getItemSizeX(activeStickyIdx) - columnGap)\n : (fixedWidth !== null ? fixedWidth : getColumnSize(activeStickyIdx) - columnGap);\n }\n }\n\n let itemX = 0;\n if (colIndex >= columnCount && columnCount > 0) {\n itemX = totalWidth;\n itemWidth = 0;\n } else if (direction === 'horizontal') {\n itemX = fixedSize !== null ? colIndex * (fixedSize + columnGap) : getItemQueryX(colIndex);\n itemWidth = fixedSize !== null ? fixedSize : getItemSizeX(colIndex) - columnGap;\n } else {\n itemX = getColumnQuery(colIndex);\n itemWidth = getColumnSize(colIndex) - columnGap;\n }\n\n // Apply X Alignment\n if (alignX === 'start') {\n targetX = itemX - stickyOffsetX;\n } else if (alignX === 'center') {\n targetX = itemX - (usableWidth - itemWidth) / 2;\n } else if (alignX === 'end') {\n targetX = itemX - (usableWidth - itemWidth);\n } else {\n // Auto alignment: stay if visible, otherwise align to nearest edge (minimal movement)\n const isVisibleX = itemWidth <= (usableWidth - stickyOffsetX)\n ? (itemX >= relativeScrollX + stickyOffsetX - 0.5 && (itemX + itemWidth) <= (relativeScrollX + usableWidth + 0.5))\n : (itemX <= relativeScrollX + stickyOffsetX + 0.5 && (itemX + itemWidth) >= (relativeScrollX + usableWidth - 0.5));\n\n if (!isVisibleX) {\n const targetStart = itemX - stickyOffsetX;\n const targetEnd = itemX - (usableWidth - itemWidth);\n\n if (itemWidth <= usableWidth - stickyOffsetX) {\n if (itemX < relativeScrollX + stickyOffsetX) {\n targetX = targetStart;\n effectiveAlignX = 'start';\n } else {\n targetX = targetEnd;\n effectiveAlignX = 'end';\n }\n } else {\n // Large item: minimal movement\n if (Math.abs(targetStart - relativeScrollX) < Math.abs(targetEnd - relativeScrollX)) {\n targetX = targetStart;\n effectiveAlignX = 'start';\n } else {\n targetX = targetEnd;\n effectiveAlignX = 'end';\n }\n }\n }\n }\n }\n\n // Clamp to valid range\n targetX = Math.max(0, Math.min(targetX, Math.max(0, totalWidth - usableWidth)));\n targetY = Math.max(0, Math.min(targetY, Math.max(0, totalHeight - usableHeight)));\n\n return { targetX, targetY, itemWidth, itemHeight, effectiveAlignX, effectiveAlignY };\n}\n\n/**\n * Calculates the range of items to render based on scroll position and viewport size.\n *\n * @param params - The parameters for calculation.\n * @returns The start and end indices of the items to render.\n * @see RangeParams\n */\nexport function calculateRange(params: RangeParams) {\n const {\n direction,\n relativeScrollX,\n relativeScrollY,\n usableWidth,\n usableHeight,\n itemsLength,\n bufferBefore,\n bufferAfter,\n gap,\n columnGap,\n fixedSize,\n findLowerBoundY,\n findLowerBoundX,\n queryY,\n queryX,\n } = params;\n\n const isVertical = direction === 'vertical' || direction === 'both';\n\n let start = 0;\n let end = itemsLength;\n\n if (isVertical) {\n if (fixedSize !== null) {\n start = Math.floor(relativeScrollY / (fixedSize + gap));\n end = Math.ceil((relativeScrollY + usableHeight) / (fixedSize + gap));\n } else {\n start = findLowerBoundY(relativeScrollY);\n const targetY = relativeScrollY + usableHeight;\n end = findLowerBoundY(targetY);\n if (end < itemsLength && queryY(end) < targetY) {\n end++;\n }\n }\n } else {\n if (fixedSize !== null) {\n start = Math.floor(relativeScrollX / (fixedSize + columnGap));\n end = Math.ceil((relativeScrollX + usableWidth) / (fixedSize + columnGap));\n } else {\n start = findLowerBoundX(relativeScrollX);\n const targetX = relativeScrollX + usableWidth;\n end = findLowerBoundX(targetX);\n if (end < itemsLength && queryX(end) < targetX) {\n end++;\n }\n }\n }\n\n return {\n start: Math.max(0, start - bufferBefore),\n end: Math.min(itemsLength, end + bufferAfter),\n };\n}\n\n/**\n * Calculates the range of columns to render for bidirectional scroll.\n *\n * @param params - The parameters for calculation.\n * @returns The start and end indices and paddings for columns.\n * @see ColumnRangeParams\n * @see ColumnRange\n */\nexport function calculateColumnRange(params: ColumnRangeParams) {\n const {\n columnCount,\n relativeScrollX,\n usableWidth,\n colBuffer,\n fixedWidth,\n columnGap,\n findLowerBound,\n query,\n totalColsQuery,\n } = params;\n\n if (!columnCount) {\n return { start: 0, end: 0, padStart: 0, padEnd: 0 };\n }\n\n let start = 0;\n let end = columnCount;\n\n if (fixedWidth !== null) {\n start = Math.floor(relativeScrollX / (fixedWidth + columnGap));\n end = Math.ceil((relativeScrollX + usableWidth) / (fixedWidth + columnGap));\n } else {\n start = findLowerBound(relativeScrollX);\n let currentX = query(start);\n let i = start;\n while (i < columnCount && currentX < relativeScrollX + usableWidth) {\n currentX = query(++i);\n }\n end = i;\n }\n\n // Add buffer of columns\n const safeStart = Math.max(0, start - colBuffer);\n const safeEnd = Math.min(columnCount, end + colBuffer);\n\n const padStart = fixedWidth !== null ? safeStart * (fixedWidth + columnGap) : query(safeStart);\n const totalWidth = fixedWidth !== null ? columnCount * (fixedWidth + columnGap) - columnGap : Math.max(0, totalColsQuery() - columnGap);\n\n const renderedEnd = fixedWidth !== null\n ? (safeEnd * (fixedWidth + columnGap) - (safeEnd >= columnCount ? columnGap : 0))\n : (query(safeEnd) - (safeEnd >= columnCount ? columnGap : 0));\n\n return {\n start: safeStart,\n end: safeEnd,\n padStart,\n padEnd: Math.max(0, totalWidth - renderedEnd),\n };\n}\n\n/**\n * Calculates the sticky state and offset for a single item.\n *\n * @param params - The parameters for calculation.\n * @returns Sticky state and offset.\n * @see StickyParams\n */\nexport function calculateStickyItem(params: StickyParams) {\n const {\n index,\n isSticky,\n direction,\n relativeScrollX,\n relativeScrollY,\n originalX,\n originalY,\n width,\n height,\n stickyIndices,\n fixedSize,\n fixedWidth,\n gap,\n columnGap,\n getItemQueryY,\n getItemQueryX,\n } = params;\n\n let isStickyActive = false;\n const stickyOffset = { x: 0, y: 0 };\n\n if (!isSticky) {\n return { isStickyActive, stickyOffset };\n }\n\n if (direction === 'vertical' || direction === 'both') {\n if (relativeScrollY > originalY) {\n // Check if next sticky item pushes this one\n let nextStickyIdx: number | undefined;\n let low = 0;\n let high = stickyIndices.length - 1;\n while (low <= high) {\n const mid = (low + high) >>> 1;\n if (stickyIndices[ mid ]! > index) {\n nextStickyIdx = stickyIndices[ mid ];\n high = mid - 1;\n } else {\n low = mid + 1;\n }\n }\n\n if (nextStickyIdx !== undefined) {\n const nextStickyY = fixedSize !== null ? nextStickyIdx * (fixedSize + gap) : getItemQueryY(nextStickyIdx);\n if (relativeScrollY >= nextStickyY) {\n isStickyActive = false;\n } else {\n isStickyActive = true;\n stickyOffset.y = Math.max(0, Math.min(height, nextStickyY - relativeScrollY)) - height;\n }\n } else {\n isStickyActive = true;\n }\n }\n }\n\n if (direction === 'horizontal' || (direction === 'both' && !isStickyActive)) {\n if (relativeScrollX > originalX) {\n let nextStickyIdx: number | undefined;\n let low = 0;\n let high = stickyIndices.length - 1;\n while (low <= high) {\n const mid = (low + high) >>> 1;\n if (stickyIndices[ mid ]! > index) {\n nextStickyIdx = stickyIndices[ mid ];\n high = mid - 1;\n } else {\n low = mid + 1;\n }\n }\n\n if (nextStickyIdx !== undefined) {\n const nextStickyX = direction === 'horizontal'\n ? (fixedSize !== null ? nextStickyIdx * (fixedSize + columnGap) : getItemQueryX(nextStickyIdx))\n : (fixedWidth !== null ? nextStickyIdx * (fixedWidth + columnGap) : getItemQueryX(nextStickyIdx));\n\n if (relativeScrollX >= nextStickyX) {\n isStickyActive = false;\n } else {\n isStickyActive = true;\n stickyOffset.x = Math.max(0, Math.min(width, nextStickyX - relativeScrollX)) - width;\n }\n } else {\n isStickyActive = true;\n }\n }\n }\n\n return { isStickyActive, stickyOffset };\n}\n\n/**\n * Calculates the position and size of a single item.\n *\n * @param params - The parameters for calculation.\n * @returns Item position and size.\n * @see ItemPositionParams\n */\nexport function calculateItemPosition(params: ItemPositionParams) {\n const {\n index,\n direction,\n fixedSize,\n gap,\n columnGap,\n usableWidth,\n usableHeight,\n totalWidth,\n queryY,\n queryX,\n getSizeY,\n getSizeX,\n } = params;\n\n let x = 0;\n let y = 0;\n let width = 0;\n let height = 0;\n\n if (direction === 'horizontal') {\n x = fixedSize !== null ? index * (fixedSize + columnGap) : queryX(index);\n width = fixedSize !== null ? fixedSize : getSizeX(index) - columnGap;\n height = usableHeight;\n } else {\n // vertical or both\n y = (direction === 'vertical' || direction === 'both') && fixedSize !== null ? index * (fixedSize + gap) : queryY(index);\n height = fixedSize !== null ? fixedSize : getSizeY(index) - gap;\n width = direction === 'both' ? totalWidth : usableWidth;\n }\n\n return { height, width, x, y };\n}\n\n/**\n * Calculates the style object for a rendered item.\n *\n * @param params - The parameters for calculation.\n * @returns Style object.\n * @see ItemStyleParams\n */\nexport function calculateItemStyle<T = unknown>(params: ItemStyleParams<T>) {\n const {\n item,\n direction,\n itemSize,\n containerTag,\n paddingStartX,\n paddingStartY,\n isHydrated,\n } = params;\n\n const isVertical = direction === 'vertical';\n const isHorizontal = direction === 'horizontal';\n const isBoth = direction === 'both';\n const isDynamic = itemSize === undefined || itemSize === null || itemSize === 0;\n\n const style: Record<string, string | number | undefined> = {\n blockSize: isHorizontal ? '100%' : (!isDynamic ? `${ item.size.height }px` : 'auto'),\n };\n\n if (isVertical && containerTag === 'table') {\n style.minInlineSize = '100%';\n } else {\n style.inlineSize = isVertical ? '100%' : (!isDynamic ? `${ item.size.width }px` : 'auto');\n }\n\n if (isDynamic) {\n if (!isVertical) {\n style.minInlineSize = '1px';\n }\n if (!isHorizontal) {\n style.minBlockSize = '1px';\n }\n }\n\n if (isHydrated) {\n if (item.isStickyActive) {\n if (isVertical || isBoth) {\n style.insetBlockStart = `${ paddingStartY }px`;\n }\n\n if (isHorizontal || isBoth) {\n style.insetInlineStart = `${ paddingStartX }px`;\n }\n\n style.transform = `translate(${ item.stickyOffset.x }px, ${ item.stickyOffset.y }px)`;\n } else {\n style.transform = `translate(${ item.offset.x }px, ${ item.offset.y }px)`;\n }\n }\n\n return style;\n}\n\n/**\n * Calculates the total width and height of the virtualized content.\n *\n * @param params - The parameters for calculation.\n * @returns Total width and height.\n * @see TotalSizeParams\n */\nexport function calculateTotalSize(params: TotalSizeParams) {\n const {\n direction,\n itemsLength,\n columnCount,\n fixedSize,\n fixedWidth,\n gap,\n columnGap,\n usableWidth,\n usableHeight,\n queryY,\n queryX,\n queryColumn,\n } = params;\n\n let width = 0;\n let height = 0;\n\n if (direction === 'both') {\n if (columnCount > 0) {\n width = fixedWidth !== null ? columnCount * (fixedWidth + columnGap) - columnGap : Math.max(0, queryColumn(columnCount) - columnGap);\n }\n if (fixedSize !== null) {\n height = Math.max(0, itemsLength * (fixedSize + gap) - (itemsLength > 0 ? gap : 0));\n } else {\n height = Math.max(0, queryY(itemsLength) - (itemsLength > 0 ? gap : 0));\n }\n width = Math.max(width, usableWidth);\n height = Math.max(height, usableHeight);\n } else if (direction === 'horizontal') {\n if (fixedSize !== null) {\n width = Math.max(0, itemsLength * (fixedSize + columnGap) - (itemsLength > 0 ? columnGap : 0));\n } else {\n width = Math.max(0, queryX(itemsLength) - (itemsLength > 0 ? columnGap : 0));\n }\n height = usableHeight;\n } else {\n // vertical\n width = usableWidth;\n if (fixedSize !== null) {\n height = Math.max(0, itemsLength * (fixedSize + gap) - (itemsLength > 0 ? gap : 0));\n } else {\n height = Math.max(0, queryY(itemsLength) - (itemsLength > 0 ? gap : 0));\n }\n }\n\n return { width, height };\n}\n","import type {\n RenderedItem,\n ScrollAlignment,\n ScrollAlignmentOptions,\n ScrollDetails,\n ScrollDirection,\n ScrollToIndexOptions,\n VirtualScrollProps,\n} from '../types';\nimport type { Ref } from 'vue';\n\n/* global ScrollToOptions */\nimport { computed, getCurrentInstance, nextTick, onMounted, onUnmounted, reactive, ref, watch } from 'vue';\n\nimport { FenwickTree } from '../utils/fenwick-tree';\nimport { getPaddingX, getPaddingY, isElement, isScrollableElement, isScrollToIndexOptions } from '../utils/scroll';\nimport {\n calculateColumnRange,\n calculateItemPosition,\n calculateRange,\n calculateScrollTarget,\n calculateStickyItem,\n calculateTotalSize,\n} from '../utils/virtual-scroll-logic';\n\nexport {\n type RenderedItem,\n type ScrollAlignment,\n type ScrollAlignmentOptions,\n type ScrollDetails,\n type ScrollDirection,\n type ScrollToIndexOptions,\n type VirtualScrollProps,\n};\n\nexport const DEFAULT_ITEM_SIZE = 40;\nexport const DEFAULT_COLUMN_WIDTH = 100;\nexport const DEFAULT_BUFFER = 5;\n\n/**\n * Composable for virtual scrolling logic.\n * Handles calculation of visible items, scroll events, dynamic item sizes, and programmatic scrolling.\n *\n * @param props - A Ref to the configuration properties.\n * @see VirtualScrollProps\n */\nexport function useVirtualScroll<T = unknown>(props: Ref<VirtualScrollProps<T>>) {\n // --- State ---\n const scrollX = ref(0);\n const scrollY = ref(0);\n const isScrolling = ref(false);\n const isHydrated = ref(false);\n const isHydrating = ref(false);\n const isMounted = ref(false);\n const viewportWidth = ref(0);\n const viewportHeight = ref(0);\n const hostOffset = reactive({ x: 0, y: 0 });\n let scrollTimeout: ReturnType<typeof setTimeout> | undefined;\n\n const isProgrammaticScroll = ref(false);\n\n // --- Fenwick Trees for efficient size and offset management ---\n const itemSizesX = new FenwickTree(props.value.items?.length || 0);\n const itemSizesY = new FenwickTree(props.value.items?.length || 0);\n const columnSizes = new FenwickTree(props.value.columnCount || 0);\n\n const treeUpdateFlag = ref(0);\n\n let measuredColumns = new Uint8Array(0);\n let measuredItemsX = new Uint8Array(0);\n let measuredItemsY = new Uint8Array(0);\n\n // --- Scroll Queue / Correction ---\n const pendingScroll = ref<{\n rowIndex: number | null | undefined;\n colIndex: number | null | undefined;\n options: ScrollAlignment | ScrollAlignmentOptions | ScrollToIndexOptions | undefined;\n } | null>(null);\n\n // Track if sizes are initialized\n const sizesInitialized = ref(false);\n let lastItems: T[] = [];\n\n // --- Computed Config ---\n const isDynamicItemSize = computed(() =>\n props.value.itemSize === undefined || props.value.itemSize === null || props.value.itemSize === 0,\n );\n\n const isDynamicColumnWidth = computed(() =>\n props.value.columnWidth === undefined || props.value.columnWidth === null || props.value.columnWidth === 0,\n );\n\n const fixedItemSize = computed(() =>\n (typeof props.value.itemSize === 'number' && props.value.itemSize > 0) ? props.value.itemSize : null,\n );\n\n const fixedColumnWidth = computed(() =>\n (typeof props.value.columnWidth === 'number' && props.value.columnWidth > 0) ? props.value.columnWidth : null,\n );\n\n const defaultSize = computed(() => props.value.defaultItemSize || fixedItemSize.value || DEFAULT_ITEM_SIZE);\n\n const sortedStickyIndices = computed(() =>\n [ ...(props.value.stickyIndices || []) ].sort((a, b) => a - b),\n );\n\n const stickyIndicesSet = computed(() => new Set(sortedStickyIndices.value));\n\n const paddingStartX = computed(() => getPaddingX(props.value.scrollPaddingStart, props.value.direction));\n const paddingEndX = computed(() => getPaddingX(props.value.scrollPaddingEnd, props.value.direction));\n const paddingStartY = computed(() => getPaddingY(props.value.scrollPaddingStart, props.value.direction));\n const paddingEndY = computed(() => getPaddingY(props.value.scrollPaddingEnd, props.value.direction));\n\n const usableWidth = computed(() => {\n const isHorizontal = props.value.direction === 'horizontal' || props.value.direction === 'both';\n return viewportWidth.value - (isHorizontal ? (paddingStartX.value + paddingEndX.value) : 0);\n });\n\n const usableHeight = computed(() => {\n const isVertical = props.value.direction === 'vertical' || props.value.direction === 'both';\n return viewportHeight.value - (isVertical ? (paddingStartY.value + paddingEndY.value) : 0);\n });\n\n // --- Size Calculations ---\n /**\n * Total width of all items in the scrollable area.\n */\n const totalWidth = computed(() => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n if (!isHydrated.value && props.value.ssrRange && !isMounted.value) {\n const { start = 0, end = 0, colStart = 0, colEnd = 0 } = props.value.ssrRange;\n const colCount = props.value.columnCount || 0;\n if (props.value.direction === 'both') {\n if (colCount <= 0) {\n return 0;\n }\n const effectiveColEnd = colEnd || colCount;\n const total = columnSizes.query(effectiveColEnd) - columnSizes.query(colStart);\n return Math.max(0, total - (effectiveColEnd > colStart ? (props.value.columnGap || 0) : 0));\n }\n if (props.value.direction === 'horizontal') {\n if (fixedItemSize.value !== null) {\n const len = end - start;\n return Math.max(0, len * (fixedItemSize.value + (props.value.columnGap || 0)) - (len > 0 ? (props.value.columnGap || 0) : 0));\n }\n const total = itemSizesX.query(end) - itemSizesX.query(start);\n return Math.max(0, total - (end > start ? (props.value.columnGap || 0) : 0));\n }\n }\n\n return calculateTotalSize({\n direction: props.value.direction || 'vertical',\n itemsLength: props.value.items.length,\n columnCount: props.value.columnCount || 0,\n fixedSize: fixedItemSize.value,\n fixedWidth: fixedColumnWidth.value,\n gap: props.value.gap || 0,\n columnGap: props.value.columnGap || 0,\n usableWidth: usableWidth.value,\n usableHeight: usableHeight.value,\n queryY: (idx) => itemSizesY.query(idx),\n queryX: (idx) => itemSizesX.query(idx),\n queryColumn: (idx) => columnSizes.query(idx),\n }).width;\n });\n\n /**\n * Total height of all items in the scrollable area.\n */\n const totalHeight = computed(() => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n if (!isHydrated.value && props.value.ssrRange && !isMounted.value) {\n const { start, end } = props.value.ssrRange;\n if (props.value.direction === 'vertical' || props.value.direction === 'both') {\n if (fixedItemSize.value !== null) {\n const len = end - start;\n return Math.max(0, len * (fixedItemSize.value + (props.value.gap || 0)) - (len > 0 ? (props.value.gap || 0) : 0));\n }\n const total = itemSizesY.query(end) - itemSizesY.query(start);\n return Math.max(0, total - (end > start ? (props.value.gap || 0) : 0));\n }\n }\n\n return calculateTotalSize({\n direction: props.value.direction || 'vertical',\n itemsLength: props.value.items.length,\n columnCount: props.value.columnCount || 0,\n fixedSize: fixedItemSize.value,\n fixedWidth: fixedColumnWidth.value,\n gap: props.value.gap || 0,\n columnGap: props.value.columnGap || 0,\n usableWidth: usableWidth.value,\n usableHeight: usableHeight.value,\n queryY: (idx) => itemSizesY.query(idx),\n queryX: (idx) => itemSizesX.query(idx),\n queryColumn: (idx) => columnSizes.query(idx),\n }).height;\n });\n\n const relativeScrollX = computed(() => {\n const isHorizontal = props.value.direction === 'horizontal' || props.value.direction === 'both';\n const padding = isHorizontal ? getPaddingX(props.value.scrollPaddingStart, props.value.direction) : 0;\n return Math.max(0, scrollX.value + padding - hostOffset.x);\n });\n const relativeScrollY = computed(() => {\n const isVertical = props.value.direction === 'vertical' || props.value.direction === 'both';\n const padding = isVertical ? getPaddingY(props.value.scrollPaddingStart, props.value.direction) : 0;\n return Math.max(0, scrollY.value + padding - hostOffset.y);\n });\n\n // --- Scroll Helpers ---\n const getColumnWidth = (index: number) => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n const cw = props.value.columnWidth;\n if (typeof cw === 'number' && cw > 0) {\n return cw;\n }\n if (Array.isArray(cw) && cw.length > 0) {\n const val = cw[ index % cw.length ];\n return (val != null && val > 0) ? val : (props.value.defaultColumnWidth || DEFAULT_COLUMN_WIDTH);\n }\n if (typeof cw === 'function') {\n return cw(index);\n }\n return columnSizes.get(index) || props.value.defaultColumnWidth || DEFAULT_COLUMN_WIDTH;\n };\n\n // --- Public Scroll API ---\n /**\n * Scrolls to a specific row and column index.\n *\n * @param rowIndex - The row index to scroll to. Pass null to only scroll horizontally.\n * @param colIndex - The column index to scroll to. Pass null to only scroll vertically.\n * @param options - Scroll options including alignment ('start', 'center', 'end', 'auto') and behavior ('auto', 'smooth').\n * Defaults to { align: 'auto', behavior: 'auto' }.\n */\n const scrollToIndex = (\n rowIndex: number | null | undefined,\n colIndex: number | null | undefined,\n options?: ScrollAlignment | ScrollAlignmentOptions | ScrollToIndexOptions,\n ) => {\n const isCorrection = typeof options === 'object' && options !== null && 'isCorrection' in options\n ? options.isCorrection\n : false;\n\n const container = props.value.container || window;\n\n const isVertical = props.value.direction === 'vertical' || props.value.direction === 'both';\n const isHorizontal = props.value.direction === 'horizontal' || props.value.direction === 'both';\n\n const { targetX, targetY, effectiveAlignX, effectiveAlignY } = calculateScrollTarget({\n rowIndex,\n colIndex,\n options,\n itemsLength: props.value.items.length,\n columnCount: props.value.columnCount || 0,\n direction: props.value.direction || 'vertical',\n usableWidth: usableWidth.value,\n usableHeight: usableHeight.value,\n totalWidth: totalWidth.value,\n totalHeight: totalHeight.value,\n gap: props.value.gap || 0,\n columnGap: props.value.columnGap || 0,\n fixedSize: fixedItemSize.value,\n fixedWidth: fixedColumnWidth.value,\n relativeScrollX: relativeScrollX.value,\n relativeScrollY: relativeScrollY.value,\n getItemSizeY: (idx) => itemSizesY.get(idx),\n getItemSizeX: (idx) => itemSizesX.get(idx),\n getItemQueryY: (idx) => itemSizesY.query(idx),\n getItemQueryX: (idx) => itemSizesX.query(idx),\n getColumnSize: (idx) => columnSizes.get(idx),\n getColumnQuery: (idx) => columnSizes.query(idx),\n stickyIndices: sortedStickyIndices.value,\n });\n\n if (!isCorrection) {\n const behavior = isScrollToIndexOptions(options) ? options.behavior : undefined;\n pendingScroll.value = {\n rowIndex,\n colIndex,\n options: {\n align: { x: effectiveAlignX, y: effectiveAlignY },\n ...(behavior != null ? { behavior } : {}),\n },\n };\n }\n\n const finalX = targetX + hostOffset.x - (isHorizontal ? paddingStartX.value : 0);\n const finalY = targetY + hostOffset.y - (isVertical ? paddingStartY.value : 0);\n\n let behavior: 'auto' | 'smooth' | undefined;\n if (isScrollToIndexOptions(options)) {\n behavior = options.behavior;\n }\n\n const scrollBehavior = isCorrection ? 'auto' : (behavior || 'smooth');\n isProgrammaticScroll.value = true;\n\n if (typeof window !== 'undefined' && container === window) {\n window.scrollTo({\n left: (colIndex === null || colIndex === undefined) ? undefined : Math.max(0, finalX),\n top: (rowIndex === null || rowIndex === undefined) ? undefined : Math.max(0, finalY),\n behavior: scrollBehavior,\n } as ScrollToOptions);\n } else if (isScrollableElement(container)) {\n const scrollOptions: ScrollToOptions = {\n behavior: scrollBehavior,\n };\n\n if (colIndex !== null && colIndex !== undefined) {\n scrollOptions.left = Math.max(0, finalX);\n }\n if (rowIndex !== null && rowIndex !== undefined) {\n scrollOptions.top = Math.max(0, finalY);\n }\n\n if (typeof container.scrollTo === 'function') {\n container.scrollTo(scrollOptions);\n } else {\n if (scrollOptions.left !== undefined) {\n container.scrollLeft = scrollOptions.left;\n }\n if (scrollOptions.top !== undefined) {\n container.scrollTop = scrollOptions.top;\n }\n }\n }\n\n if (scrollBehavior === 'auto' || scrollBehavior === undefined) {\n if (colIndex !== null && colIndex !== undefined) {\n scrollX.value = Math.max(0, finalX);\n }\n if (rowIndex !== null && rowIndex !== undefined) {\n scrollY.value = Math.max(0, finalY);\n }\n }\n\n // We do NOT clear pendingScroll here anymore.\n // It will be cleared in checkPendingScroll when fully stable,\n // or in handleScroll if the user manually scrolls.\n };\n\n /**\n * Programmatically scroll to a specific pixel offset relative to the content start.\n *\n * @param x - The pixel offset to scroll to on the X axis. Pass null to keep current position.\n * @param y - The pixel offset to scroll to on the Y axis. Pass null to keep current position.\n * @param options - Scroll options (behavior)\n * @param options.behavior - The scroll behavior ('auto' | 'smooth'). Defaults to 'auto'.\n */\n const scrollToOffset = (x?: number | null, y?: number | null, options?: { behavior?: 'auto' | 'smooth'; }) => {\n const container = props.value.container || window;\n isProgrammaticScroll.value = true;\n\n const isVertical = props.value.direction === 'vertical' || props.value.direction === 'both';\n const isHorizontal = props.value.direction === 'horizontal' || props.value.direction === 'both';\n\n const clampedX = (x !== null && x !== undefined)\n ? (isHorizontal ? Math.max(0, Math.min(x, Math.max(0, totalWidth.value - usableWidth.value))) : Math.max(0, x))\n : null;\n const clampedY = (y !== null && y !== undefined)\n ? (isVertical ? Math.max(0, Math.min(y, Math.max(0, totalHeight.value - usableHeight.value))) : Math.max(0, y))\n : null;\n\n const currentX = (typeof window !== 'undefined' && container === window ? window.scrollX : (container as HTMLElement).scrollLeft);\n const currentY = (typeof window !== 'undefined' && container === window ? window.scrollY : (container as HTMLElement).scrollTop);\n\n const targetX = (clampedX !== null) ? clampedX + hostOffset.x - (isHorizontal ? paddingStartX.value : 0) : currentX;\n const targetY = (clampedY !== null) ? clampedY + hostOffset.y - (isVertical ? paddingStartY.value : 0) : currentY;\n\n if (typeof window !== 'undefined' && container === window) {\n window.scrollTo({\n left: (x !== null && x !== undefined) ? targetX : undefined,\n top: (y !== null && y !== undefined) ? targetY : undefined,\n behavior: options?.behavior || 'auto',\n } as ScrollToOptions);\n } else if (isScrollableElement(container)) {\n const scrollOptions: ScrollToOptions = {\n behavior: options?.behavior || 'auto',\n };\n\n if (x !== null && x !== undefined) {\n scrollOptions.left = targetX;\n }\n if (y !== null && y !== undefined) {\n scrollOptions.top = targetY;\n }\n\n if (typeof container.scrollTo === 'function') {\n container.scrollTo(scrollOptions);\n } else {\n if (scrollOptions.left !== undefined) {\n container.scrollLeft = scrollOptions.left;\n }\n if (scrollOptions.top !== undefined) {\n container.scrollTop = scrollOptions.top;\n }\n }\n }\n\n if (options?.behavior === 'auto' || options?.behavior === undefined) {\n if (x !== null && x !== undefined) {\n scrollX.value = targetX;\n }\n if (y !== null && y !== undefined) {\n scrollY.value = targetY;\n }\n }\n };\n\n // --- Measurement & Initialization ---\n const initializeSizes = () => {\n const newItems = props.value.items;\n const len = newItems.length;\n const colCount = props.value.columnCount || 0;\n\n itemSizesX.resize(len);\n itemSizesY.resize(len);\n columnSizes.resize(colCount);\n\n if (measuredItemsX.length !== len) {\n const newMeasuredX = new Uint8Array(len);\n newMeasuredX.set(measuredItemsX.subarray(0, Math.min(len, measuredItemsX.length)));\n measuredItemsX = newMeasuredX;\n }\n if (measuredItemsY.length !== len) {\n const newMeasuredY = new Uint8Array(len);\n newMeasuredY.set(measuredItemsY.subarray(0, Math.min(len, measuredItemsY.length)));\n measuredItemsY = newMeasuredY;\n }\n if (measuredColumns.length !== colCount) {\n const newMeasuredCols = new Uint8Array(colCount);\n newMeasuredCols.set(measuredColumns.subarray(0, Math.min(colCount, measuredColumns.length)));\n measuredColumns = newMeasuredCols;\n }\n\n let prependCount = 0;\n if (props.value.restoreScrollOnPrepend && lastItems.length > 0 && len > lastItems.length) {\n const oldFirstItem = lastItems[ 0 ];\n if (oldFirstItem !== undefined) {\n for (let i = 1; i <= len - lastItems.length; i++) {\n if (newItems[ i ] === oldFirstItem) {\n prependCount = i;\n break;\n }\n }\n }\n }\n\n if (prependCount > 0) {\n itemSizesX.shift(prependCount);\n itemSizesY.shift(prependCount);\n\n if (pendingScroll.value && pendingScroll.value.rowIndex !== null && pendingScroll.value.rowIndex !== undefined) {\n pendingScroll.value.rowIndex += prependCount;\n }\n\n const newMeasuredX = new Uint8Array(len);\n const newMeasuredY = new Uint8Array(len);\n newMeasuredX.set(measuredItemsX.subarray(0, Math.min(len - prependCount, measuredItemsX.length)), prependCount);\n newMeasuredY.set(measuredItemsY.subarray(0, Math.min(len - prependCount, measuredItemsY.length)), prependCount);\n measuredItemsX = newMeasuredX;\n measuredItemsY = newMeasuredY;\n\n // Calculate added size\n const gap = props.value.gap || 0;\n const columnGap = props.value.columnGap || 0;\n let addedX = 0;\n let addedY = 0;\n\n for (let i = 0; i < prependCount; i++) {\n const size = typeof props.value.itemSize === 'function'\n ? props.value.itemSize(newItems[ i ] as T, i)\n : defaultSize.value;\n\n if (props.value.direction === 'horizontal') {\n addedX += size + columnGap;\n } else {\n addedY += size + gap;\n }\n }\n\n if (addedX > 0 || addedY > 0) {\n nextTick(() => {\n scrollToOffset(\n addedX > 0 ? relativeScrollX.value + addedX : null,\n addedY > 0 ? relativeScrollY.value + addedY : null,\n { behavior: 'auto' },\n );\n });\n }\n }\n\n // Initialize columns\n if (colCount > 0) {\n const columnGap = props.value.columnGap || 0;\n let colNeedsRebuild = false;\n const cw = props.value.columnWidth;\n\n for (let i = 0; i < colCount; i++) {\n const currentW = columnSizes.get(i);\n const isMeasured = measuredColumns[ i ] === 1;\n\n if (!isDynamicColumnWidth.value || (!isMeasured && currentW === 0)) {\n let baseWidth = 0;\n if (typeof cw === 'number' && cw > 0) {\n baseWidth = cw;\n } else if (Array.isArray(cw) && cw.length > 0) {\n baseWidth = cw[ i % cw.length ] || props.value.defaultColumnWidth || DEFAULT_COLUMN_WIDTH;\n } else if (typeof cw === 'function') {\n baseWidth = cw(i);\n } else {\n baseWidth = props.value.defaultColumnWidth || DEFAULT_COLUMN_WIDTH;\n }\n\n const targetW = baseWidth + columnGap;\n if (Math.abs(currentW - targetW) > 0.5) {\n columnSizes.set(i, targetW);\n measuredColumns[ i ] = isDynamicColumnWidth.value ? 0 : 1;\n colNeedsRebuild = true;\n } else if (!isDynamicColumnWidth.value) {\n measuredColumns[ i ] = 1;\n }\n }\n }\n if (colNeedsRebuild) {\n columnSizes.rebuild();\n }\n }\n\n const gap = props.value.gap || 0;\n const columnGap = props.value.columnGap || 0;\n let itemsNeedRebuild = false;\n\n for (let i = 0; i < len; i++) {\n const item = props.value.items[ i ];\n const currentX = itemSizesX.get(i);\n const currentY = itemSizesY.get(i);\n\n const isVertical = props.value.direction === 'vertical';\n const isHorizontal = props.value.direction === 'horizontal';\n const isBoth = props.value.direction === 'both';\n\n const isMeasuredX = measuredItemsX[ i ] === 1;\n const isMeasuredY = measuredItemsY[ i ] === 1;\n\n // Logic for X\n if (isHorizontal) {\n if (!isDynamicItemSize.value || (!isMeasuredX && currentX === 0)) {\n const baseSize = typeof props.value.itemSize === 'function'\n ? props.value.itemSize(item as T, i)\n : defaultSize.value;\n const targetX = baseSize + columnGap;\n\n if (Math.abs(currentX - targetX) > 0.5) {\n itemSizesX.set(i, targetX);\n measuredItemsX[ i ] = isDynamicItemSize.value ? 0 : 1;\n itemsNeedRebuild = true;\n } else if (!isDynamicItemSize.value) {\n measuredItemsX[ i ] = 1;\n }\n }\n } else if (currentX !== 0) {\n itemSizesX.set(i, 0);\n measuredItemsX[ i ] = 0;\n itemsNeedRebuild = true;\n }\n\n // Logic for Y\n if (isVertical || isBoth) {\n if (!isDynamicItemSize.value || (!isMeasuredY && currentY === 0)) {\n const baseSize = typeof props.value.itemSize === 'function'\n ? props.value.itemSize(item as T, i)\n : defaultSize.value;\n const targetY = baseSize + gap;\n\n if (Math.abs(currentY - targetY) > 0.5) {\n itemSizesY.set(i, targetY);\n measuredItemsY[ i ] = isDynamicItemSize.value ? 0 : 1;\n itemsNeedRebuild = true;\n } else if (!isDynamicItemSize.value) {\n measuredItemsY[ i ] = 1;\n }\n }\n } else if (currentY !== 0) {\n itemSizesY.set(i, 0);\n measuredItemsY[ i ] = 0;\n itemsNeedRebuild = true;\n }\n }\n\n if (itemsNeedRebuild) {\n itemSizesX.rebuild();\n itemSizesY.rebuild();\n }\n\n lastItems = [ ...newItems ];\n sizesInitialized.value = true;\n treeUpdateFlag.value++;\n };\n\n /**\n * Updates the host element's offset relative to the scroll container.\n */\n const updateHostOffset = () => {\n if (props.value.hostElement && typeof window !== 'undefined') {\n const rect = props.value.hostElement.getBoundingClientRect();\n const container = props.value.container || window;\n\n let newX = 0;\n let newY = 0;\n\n if (container === window) {\n newX = rect.left + window.scrollX;\n newY = rect.top + window.scrollY;\n } else if (container === props.value.hostElement) {\n newX = 0;\n newY = 0;\n } else if (isElement(container)) {\n const containerRect = container.getBoundingClientRect();\n newX = rect.left - containerRect.left + container.scrollLeft;\n newY = rect.top - containerRect.top + container.scrollTop;\n }\n\n if (Math.abs(hostOffset.x - newX) > 0.1 || Math.abs(hostOffset.y - newY) > 0.1) {\n hostOffset.x = newX;\n hostOffset.y = newY;\n }\n }\n };\n\n watch([\n () => props.value.items,\n () => props.value.items.length,\n () => props.value.direction,\n () => props.value.columnCount,\n () => props.value.columnWidth,\n () => props.value.itemSize,\n () => props.value.gap,\n () => props.value.columnGap,\n () => props.value.defaultItemSize,\n () => props.value.defaultColumnWidth,\n ], initializeSizes, { immediate: true });\n\n watch(() => [ props.value.container, props.value.hostElement ], () => {\n updateHostOffset();\n });\n\n // --- Range & Visible Items ---\n /**\n * Current range of items that should be rendered.\n */\n const range = computed(() => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n if ((!isHydrated.value || isHydrating.value) && props.value.ssrRange) {\n return {\n start: props.value.ssrRange.start,\n end: props.value.ssrRange.end,\n };\n }\n\n const bufferBefore = (props.value.ssrRange && !isScrolling.value) ? 0 : (props.value.bufferBefore ?? DEFAULT_BUFFER);\n const bufferAfter = props.value.bufferAfter ?? DEFAULT_BUFFER;\n\n return calculateRange({\n direction: props.value.direction || 'vertical',\n relativeScrollX: relativeScrollX.value,\n relativeScrollY: relativeScrollY.value,\n usableWidth: usableWidth.value,\n usableHeight: usableHeight.value,\n itemsLength: props.value.items.length,\n bufferBefore,\n bufferAfter,\n gap: props.value.gap || 0,\n columnGap: props.value.columnGap || 0,\n fixedSize: fixedItemSize.value,\n findLowerBoundY: (offset) => itemSizesY.findLowerBound(offset),\n findLowerBoundX: (offset) => itemSizesX.findLowerBound(offset),\n queryY: (idx) => itemSizesY.query(idx),\n queryX: (idx) => itemSizesX.query(idx),\n });\n });\n\n /**\n * Index of the first visible item in the viewport.\n */\n const currentIndex = computed(() => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n const fixedSize = fixedItemSize.value;\n const gap = props.value.gap || 0;\n const columnGap = props.value.columnGap || 0;\n\n if (props.value.direction === 'horizontal') {\n if (fixedSize !== null) {\n return Math.floor(relativeScrollX.value / (fixedSize + columnGap));\n }\n return itemSizesX.findLowerBound(relativeScrollX.value);\n }\n if (fixedSize !== null) {\n return Math.floor(relativeScrollY.value / (fixedSize + gap));\n }\n return itemSizesY.findLowerBound(relativeScrollY.value);\n });\n\n /**\n * List of items to be rendered with their calculated offsets and sizes.\n */\n\n let lastRenderedItems: RenderedItem<T>[] = [];\n\n const renderedItems = computed<RenderedItem<T>[]>(() => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n const { start, end } = range.value;\n const items: RenderedItem<T>[] = [];\n const fixedSize = fixedItemSize.value;\n const gap = props.value.gap || 0;\n const columnGap = props.value.columnGap || 0;\n const stickyIndices = sortedStickyIndices.value;\n const stickySet = stickyIndicesSet.value;\n\n // Always include relevant sticky items\n const indicesToRender = new Set<number>();\n for (let i = start; i < end; i++) {\n indicesToRender.add(i);\n }\n\n if (isHydrated.value || !props.value.ssrRange) {\n const activeIdx = currentIndex.value;\n // find the largest index in stickyIndices that is < activeIdx\n let prevStickyIdx: number | undefined;\n let low = 0;\n let high = stickyIndices.length - 1;\n while (low <= high) {\n const mid = (low + high) >>> 1;\n if (stickyIndices[ mid ]! < activeIdx) {\n prevStickyIdx = stickyIndices[ mid ];\n low = mid + 1;\n } else {\n high = mid - 1;\n }\n }\n\n if (prevStickyIdx !== undefined) {\n indicesToRender.add(prevStickyIdx);\n }\n\n // Optimize: Use binary search to find the first sticky index in range\n let stickyLow = 0;\n let stickyHigh = stickyIndices.length - 1;\n let firstInRange = -1;\n\n while (stickyLow <= stickyHigh) {\n const mid = (stickyLow + stickyHigh) >>> 1;\n if (stickyIndices[ mid ]! >= start) {\n firstInRange = mid;\n stickyHigh = mid - 1;\n } else {\n stickyLow = mid + 1;\n }\n }\n\n if (firstInRange !== -1) {\n for (let i = firstInRange; i < stickyIndices.length; i++) {\n const idx = stickyIndices[ i ]!;\n if (idx >= end) {\n break;\n }\n indicesToRender.add(idx);\n }\n }\n }\n\n const sortedIndices = Array.from(indicesToRender).sort((a, b) => a - b);\n\n const ssrStartRow = props.value.ssrRange?.start || 0;\n const ssrStartCol = props.value.ssrRange?.colStart || 0;\n\n let ssrOffsetX = 0;\n let ssrOffsetY = 0;\n\n if (!isHydrated.value && props.value.ssrRange) {\n ssrOffsetY = (props.value.direction === 'vertical' || props.value.direction === 'both')\n ? (fixedSize !== null ? ssrStartRow * (fixedSize + gap) : itemSizesY.query(ssrStartRow))\n : 0;\n\n if (props.value.direction === 'horizontal') {\n ssrOffsetX = fixedSize !== null ? ssrStartCol * (fixedSize + columnGap) : itemSizesX.query(ssrStartCol);\n } else if (props.value.direction === 'both') {\n ssrOffsetX = columnSizes.query(ssrStartCol);\n }\n }\n\n const lastItemsMap = new Map(lastRenderedItems.map((it) => [ it.index, it ]));\n\n for (const i of sortedIndices) {\n const item = props.value.items[ i ];\n if (item === undefined) {\n continue;\n }\n\n const { x, y, width, height } = calculateItemPosition({\n index: i,\n direction: props.value.direction || 'vertical',\n fixedSize: fixedItemSize.value,\n gap: props.value.gap || 0,\n columnGap: props.value.columnGap || 0,\n usableWidth: usableWidth.value,\n usableHeight: usableHeight.value,\n totalWidth: totalWidth.value,\n queryY: (idx) => itemSizesY.query(idx),\n queryX: (idx) => itemSizesX.query(idx),\n getSizeY: (idx) => itemSizesY.get(idx),\n getSizeX: (idx) => itemSizesX.get(idx),\n });\n\n const isSticky = stickySet.has(i);\n const originalX = x;\n const originalY = y;\n\n const { isStickyActive, stickyOffset } = calculateStickyItem({\n index: i,\n isSticky,\n direction: props.value.direction || 'vertical',\n relativeScrollX: relativeScrollX.value,\n relativeScrollY: relativeScrollY.value,\n originalX,\n originalY,\n width,\n height,\n stickyIndices,\n fixedSize: fixedItemSize.value,\n fixedWidth: fixedColumnWidth.value,\n gap: props.value.gap || 0,\n columnGap: props.value.columnGap || 0,\n getItemQueryY: (idx) => itemSizesY.query(idx),\n getItemQueryX: (idx) => itemSizesX.query(idx),\n });\n\n const offsetX = originalX - ssrOffsetX;\n const offsetY = originalY - ssrOffsetY;\n const last = lastItemsMap.get(i);\n\n if (\n last\n && last.item === item\n && last.offset.x === offsetX\n && last.offset.y === offsetY\n && last.size.width === width\n && last.size.height === height\n && last.isSticky === isSticky\n && last.isStickyActive === isStickyActive\n && last.stickyOffset.x === stickyOffset.x\n && last.stickyOffset.y === stickyOffset.y\n ) {\n items.push(last);\n } else {\n items.push({\n item,\n index: i,\n offset: { x: offsetX, y: offsetY },\n size: { width, height },\n originalX,\n originalY,\n isSticky,\n isStickyActive,\n stickyOffset,\n });\n }\n }\n\n lastRenderedItems = items;\n\n return items;\n });\n\n const columnRange = computed(() => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n const totalCols = props.value.columnCount || 0;\n\n if (!totalCols) {\n return { start: 0, end: 0, padStart: 0, padEnd: 0 };\n }\n\n if ((!isHydrated.value || isHydrating.value) && props.value.ssrRange) {\n const { colStart = 0, colEnd = 0 } = props.value.ssrRange;\n const safeStart = Math.max(0, colStart);\n const safeEnd = Math.min(totalCols, colEnd || totalCols);\n return {\n start: safeStart,\n end: safeEnd,\n padStart: 0,\n padEnd: 0,\n };\n }\n\n const colBuffer = (props.value.ssrRange && !isScrolling.value) ? 0 : 2;\n\n return calculateColumnRange({\n columnCount: totalCols,\n relativeScrollX: relativeScrollX.value,\n usableWidth: usableWidth.value,\n colBuffer,\n fixedWidth: fixedColumnWidth.value,\n columnGap: props.value.columnGap || 0,\n findLowerBound: (offset) => columnSizes.findLowerBound(offset),\n query: (idx) => columnSizes.query(idx),\n totalColsQuery: () => columnSizes.query(totalCols),\n });\n });\n\n /**\n * Detailed information about the current scroll state.\n */\n const scrollDetails = computed<ScrollDetails<T>>(() => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n const fixedSize = fixedItemSize.value;\n const columnGap = props.value.columnGap || 0;\n\n let currentColIndex = 0;\n if (props.value.direction === 'horizontal') {\n if (fixedSize !== null) {\n currentColIndex = Math.floor(relativeScrollX.value / (fixedSize + columnGap));\n } else {\n currentColIndex = itemSizesX.findLowerBound(relativeScrollX.value);\n }\n } else if (props.value.direction === 'both') {\n currentColIndex = columnSizes.findLowerBound(relativeScrollX.value);\n }\n\n return {\n items: renderedItems.value,\n currentIndex: currentIndex.value,\n currentColIndex,\n scrollOffset: { x: relativeScrollX.value, y: relativeScrollY.value },\n viewportSize: { width: viewportWidth.value, height: viewportHeight.value },\n totalSize: { width: totalWidth.value, height: totalHeight.value },\n isScrolling: isScrolling.value,\n isProgrammaticScroll: isProgrammaticScroll.value,\n range: range.value,\n columnRange: columnRange.value,\n };\n });\n\n // --- Event Handlers & Lifecycle ---\n /**\n * Stops any currently active programmatic scroll and clears pending corrections.\n */\n const stopProgrammaticScroll = () => {\n isProgrammaticScroll.value = false;\n pendingScroll.value = null;\n };\n\n /**\n * Event handler for scroll events.\n */\n const handleScroll = (e: Event) => {\n const target = e.target;\n if (typeof window === 'undefined') {\n return;\n }\n\n if (target === window || target === document) {\n scrollX.value = window.scrollX;\n scrollY.value = window.scrollY;\n viewportWidth.value = document.documentElement.clientWidth;\n viewportHeight.value = document.documentElement.clientHeight;\n } else if (isScrollableElement(target)) {\n scrollX.value = target.scrollLeft;\n scrollY.value = target.scrollTop;\n viewportWidth.value = target.clientWidth;\n viewportHeight.value = target.clientHeight;\n }\n\n if (!isScrolling.value) {\n if (!isProgrammaticScroll.value) {\n pendingScroll.value = null;\n }\n isScrolling.value = true;\n }\n clearTimeout(scrollTimeout);\n scrollTimeout = setTimeout(() => {\n isScrolling.value = false;\n isProgrammaticScroll.value = false;\n }, 250);\n };\n\n /**\n * Updates the size of multiple items in the Fenwick tree.\n *\n * @param updates - Array of updates\n */\n const updateItemSizes = (updates: Array<{ index: number; inlineSize: number; blockSize: number; element?: HTMLElement | undefined; }>) => {\n let needUpdate = false;\n let deltaX = 0;\n let deltaY = 0;\n const gap = props.value.gap || 0;\n const columnGap = props.value.columnGap || 0;\n\n const currentRelX = relativeScrollX.value;\n const currentRelY = relativeScrollY.value;\n const firstRowIndex = props.value.direction === 'horizontal'\n ? (fixedItemSize.value !== null ? Math.floor(currentRelX / (fixedItemSize.value + columnGap)) : itemSizesX.findLowerBound(currentRelX))\n : (fixedItemSize.value !== null ? Math.floor(currentRelY / (fixedItemSize.value + gap)) : itemSizesY.findLowerBound(currentRelY));\n const firstColIndex = props.value.direction === 'both'\n ? columnSizes.findLowerBound(currentRelX)\n : (props.value.direction === 'horizontal' ? firstRowIndex : 0);\n\n const isHorizontalMode = props.value.direction === 'horizontal';\n const isVerticalMode = props.value.direction === 'vertical';\n const isBothMode = props.value.direction === 'both';\n\n const processedRows = new Set<number>();\n const processedCols = new Set<number>();\n\n for (const { index, inlineSize, blockSize, element } of updates) {\n // Ignore 0-size measurements as they usually indicate hidden/detached elements\n if (inlineSize <= 0 && blockSize <= 0) {\n continue;\n }\n\n const isMeasurable = isDynamicItemSize.value || typeof props.value.itemSize === 'function';\n if (index >= 0 && !processedRows.has(index) && isMeasurable && blockSize > 0) {\n processedRows.add(index);\n if (isHorizontalMode && inlineSize > 0) {\n const oldWidth = itemSizesX.get(index);\n const targetWidth = inlineSize + columnGap;\n if (!measuredItemsX[ index ] || Math.abs(targetWidth - oldWidth) > 0.1) {\n const d = targetWidth - oldWidth;\n itemSizesX.update(index, d);\n measuredItemsX[ index ] = 1;\n needUpdate = true;\n if (index < firstRowIndex) {\n deltaX += d;\n }\n }\n }\n if (isVerticalMode || isBothMode) {\n const oldHeight = itemSizesY.get(index);\n const targetHeight = blockSize + gap;\n\n if (!measuredItemsY[ index ] || Math.abs(targetHeight - oldHeight) > 0.1) {\n const d = targetHeight - oldHeight;\n itemSizesY.update(index, d);\n measuredItemsY[ index ] = 1;\n needUpdate = true;\n if (index < firstRowIndex) {\n deltaY += d;\n }\n }\n }\n }\n\n // Dynamic column width measurement\n const isColMeasurable = isDynamicColumnWidth.value || typeof props.value.columnWidth === 'function';\n if (\n isBothMode\n && element\n && props.value.columnCount\n && isColMeasurable\n && (inlineSize > 0 || element.dataset.colIndex === undefined)\n ) {\n const colIndexAttr = element.dataset.colIndex;\n if (colIndexAttr != null) {\n const colIndex = Number.parseInt(colIndexAttr, 10);\n if (colIndex >= 0 && colIndex < (props.value.columnCount || 0) && !processedCols.has(colIndex)) {\n processedCols.add(colIndex);\n const oldW = columnSizes.get(colIndex);\n const targetW = inlineSize + columnGap;\n\n if (!measuredColumns[ colIndex ] || Math.abs(oldW - targetW) > 0.1) {\n const d = targetW - oldW;\n if (Math.abs(d) > 0.1) {\n columnSizes.update(colIndex, d);\n needUpdate = true;\n if (colIndex < firstColIndex) {\n deltaX += d;\n }\n }\n measuredColumns[ colIndex ] = 1;\n }\n }\n } else {\n // If the element is a row, try to find cells with data-col-index\n const cells = element.dataset.colIndex !== undefined\n ? [ element ]\n : Array.from(element.querySelectorAll('[data-col-index]')) as HTMLElement[];\n\n for (const child of cells) {\n const colIndex = Number.parseInt(child.dataset.colIndex!, 10);\n\n if (colIndex >= 0 && colIndex < (props.value.columnCount || 0) && !processedCols.has(colIndex)) {\n processedCols.add(colIndex);\n const rect = child.getBoundingClientRect();\n const w = rect.width;\n const oldW = columnSizes.get(colIndex);\n const targetW = w + columnGap;\n if (!measuredColumns[ colIndex ] || Math.abs(oldW - targetW) > 0.1) {\n const d = targetW - oldW;\n if (Math.abs(d) > 0.1) {\n columnSizes.update(colIndex, d);\n needUpdate = true;\n if (colIndex < firstColIndex) {\n deltaX += d;\n }\n }\n measuredColumns[ colIndex ] = 1;\n }\n }\n }\n }\n }\n }\n\n if (needUpdate) {\n treeUpdateFlag.value++;\n // Only compensate if not in a programmatic scroll,\n // as it would interrupt the browser animation or explicit alignment.\n const hasPendingScroll = pendingScroll.value !== null || isProgrammaticScroll.value;\n\n if (!hasPendingScroll && (deltaX !== 0 || deltaY !== 0)) {\n scrollToOffset(\n deltaX !== 0 ? currentRelX + deltaX : null,\n deltaY !== 0 ? currentRelY + deltaY : null,\n { behavior: 'auto' },\n );\n }\n }\n };\n\n /**\n * Updates the size of a specific item in the Fenwick tree.\n *\n * @param index - Index of the item\n * @param inlineSize - New inlineSize\n * @param blockSize - New blockSize\n * @param element - The element that was measured (optional)\n */\n const updateItemSize = (index: number, inlineSize: number, blockSize: number, element?: HTMLElement) => {\n updateItemSizes([ { index, inlineSize, blockSize, element } ]);\n };\n\n // --- Scroll Queue / Correction Watchers ---\n const checkPendingScroll = () => {\n if (pendingScroll.value && !isHydrating.value) {\n const { rowIndex, colIndex, options } = pendingScroll.value;\n\n const isSmooth = isScrollToIndexOptions(options) && options.behavior === 'smooth';\n\n // If it's a smooth scroll, we wait until it's finished before correcting.\n if (isSmooth && isScrolling.value) {\n return;\n }\n\n const { targetX, targetY } = calculateScrollTarget({\n rowIndex,\n colIndex,\n options,\n itemsLength: props.value.items.length,\n columnCount: props.value.columnCount || 0,\n direction: props.value.direction || 'vertical',\n usableWidth: usableWidth.value,\n usableHeight: usableHeight.value,\n totalWidth: totalWidth.value,\n totalHeight: totalHeight.value,\n gap: props.value.gap || 0,\n columnGap: props.value.columnGap || 0,\n fixedSize: fixedItemSize.value,\n fixedWidth: fixedColumnWidth.value,\n relativeScrollX: relativeScrollX.value,\n relativeScrollY: relativeScrollY.value,\n getItemSizeY: (idx) => itemSizesY.get(idx),\n getItemSizeX: (idx) => itemSizesX.get(idx),\n getItemQueryY: (idx) => itemSizesY.query(idx),\n getItemQueryX: (idx) => itemSizesX.query(idx),\n getColumnSize: (idx) => columnSizes.get(idx),\n getColumnQuery: (idx) => columnSizes.query(idx),\n stickyIndices: sortedStickyIndices.value,\n });\n\n const tolerance = 1;\n const reachedX = (colIndex === null || colIndex === undefined) || Math.abs(relativeScrollX.value - targetX) < tolerance;\n const reachedY = (rowIndex === null || rowIndex === undefined) || Math.abs(relativeScrollY.value - targetY) < tolerance;\n\n const isMeasuredX = colIndex == null || colIndex === undefined || measuredColumns[ colIndex ] === 1;\n const isMeasuredY = rowIndex == null || rowIndex === undefined || measuredItemsY[ rowIndex ] === 1;\n\n if (reachedX && reachedY) {\n if (isMeasuredX && isMeasuredY) {\n pendingScroll.value = null;\n }\n } else {\n const correctionOptions: ScrollToIndexOptions = isScrollToIndexOptions(options)\n ? { ...options, isCorrection: true }\n : { align: options as ScrollAlignment | ScrollAlignmentOptions, isCorrection: true };\n scrollToIndex(rowIndex, colIndex, correctionOptions);\n }\n }\n };\n\n watch([ treeUpdateFlag, viewportWidth, viewportHeight ], checkPendingScroll);\n\n watch(isScrolling, (scrolling) => {\n if (!scrolling) {\n checkPendingScroll();\n }\n });\n\n let resizeObserver: ResizeObserver | null = null;\n\n const attachEvents = (container: HTMLElement | Window | null) => {\n if (!container || typeof window === 'undefined') {\n return;\n }\n const scrollTarget = container === window ? document : container;\n scrollTarget.addEventListener('scroll', handleScroll, { passive: true });\n\n if (container === window) {\n viewportWidth.value = document.documentElement.clientWidth;\n viewportHeight.value = document.documentElement.clientHeight;\n scrollX.value = window.scrollX;\n scrollY.value = window.scrollY;\n\n const onResize = () => {\n viewportWidth.value = document.documentElement.clientWidth;\n viewportHeight.value = document.documentElement.clientHeight;\n updateHostOffset();\n };\n window.addEventListener('resize', onResize);\n return () => {\n scrollTarget.removeEventListener('scroll', handleScroll);\n window.removeEventListener('resize', onResize);\n };\n } else {\n viewportWidth.value = (container as HTMLElement).clientWidth;\n viewportHeight.value = (container as HTMLElement).clientHeight;\n scrollX.value = (container as HTMLElement).scrollLeft;\n scrollY.value = (container as HTMLElement).scrollTop;\n\n resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n if (entry.target === container) {\n viewportWidth.value = (container as HTMLElement).clientWidth;\n viewportHeight.value = (container as HTMLElement).clientHeight;\n updateHostOffset();\n }\n }\n });\n resizeObserver.observe(container as HTMLElement);\n return () => {\n scrollTarget.removeEventListener('scroll', handleScroll);\n resizeObserver?.disconnect();\n };\n }\n };\n\n let cleanup: (() => void) | undefined;\n\n if (getCurrentInstance()) {\n onMounted(() => {\n isMounted.value = true;\n\n watch(() => props.value.container, (newContainer) => {\n cleanup?.();\n cleanup = attachEvents(newContainer || null);\n }, { immediate: true });\n\n updateHostOffset();\n\n if (props.value.ssrRange || props.value.initialScrollIndex !== undefined) {\n nextTick(() => {\n updateHostOffset();\n const initialIndex = props.value.initialScrollIndex !== undefined\n ? props.value.initialScrollIndex\n : props.value.ssrRange?.start;\n const initialAlign = props.value.initialScrollAlign || 'start';\n\n if (initialIndex !== undefined && initialIndex !== null) {\n scrollToIndex(initialIndex, props.value.ssrRange?.colStart, { align: initialAlign, behavior: 'auto' });\n }\n\n isHydrated.value = true;\n isHydrating.value = true;\n nextTick(() => {\n isHydrating.value = false;\n });\n });\n } else {\n isHydrated.value = true;\n }\n });\n\n onUnmounted(() => {\n cleanup?.();\n });\n }\n\n /**\n * The list of items currently rendered in the DOM.\n */\n const refresh = () => {\n itemSizesX.resize(0);\n itemSizesY.resize(0);\n columnSizes.resize(0);\n measuredColumns.fill(0);\n measuredItemsX.fill(0);\n measuredItemsY.fill(0);\n initializeSizes();\n };\n\n return {\n /**\n * Array of items currently rendered in the DOM with their calculated offsets and sizes.\n * @see RenderedItem\n */\n renderedItems,\n\n /**\n * Total calculated width of all items including gaps (in pixels).\n */\n totalWidth,\n\n /**\n * Total calculated height of all items including gaps (in pixels).\n */\n totalHeight,\n\n /**\n * Detailed information about the current scroll state.\n * Includes currentIndex, scrollOffset, viewportSize, totalSize, and scrolling status.\n * @see ScrollDetails\n */\n scrollDetails,\n\n /**\n * Programmatically scroll to a specific row and/or column.\n *\n * @param rowIndex - The row index to scroll to. Pass null to only scroll horizontally.\n * @param colIndex - The column index to scroll to. Pass null to only scroll vertically.\n * @param options - Alignment and behavior options.\n * @see ScrollAlignment\n * @see ScrollToIndexOptions\n */\n scrollToIndex,\n\n /**\n * Programmatically scroll to a specific pixel offset relative to the content start.\n *\n * @param x - The pixel offset to scroll to on the X axis. Pass null to keep current position.\n * @param y - The pixel offset to scroll to on the Y axis. Pass null to keep current position.\n * @param options - Scroll options (behavior).\n */\n scrollToOffset,\n\n /**\n * Stops any currently active smooth scroll animation and clears pending corrections.\n */\n stopProgrammaticScroll,\n\n /**\n * Updates the stored size of an item. Should be called when an item is measured (e.g., via ResizeObserver).\n *\n * @param index - The item index.\n * @param width - The measured inlineSize (width).\n * @param height - The measured blockSize (height).\n * @param element - The measured element (optional, used for robust grid column detection).\n */\n updateItemSize,\n\n /**\n * Updates the stored size of multiple items simultaneously.\n *\n * @param updates - Array of measurement updates.\n */\n updateItemSizes,\n\n /**\n * Recalculates the host element's offset relative to the scroll container.\n * Useful if the container or host moves without a resize event.\n */\n updateHostOffset,\n\n /**\n * Information about the current visible range of columns and their paddings.\n * @see ColumnRange\n */\n columnRange,\n\n /**\n * Helper to get the width of a specific column based on current configuration and measurements.\n *\n * @param index - The column index.\n */\n getColumnWidth,\n\n /**\n * Resets all dynamic measurements and re-initializes from props.\n * Useful if item sizes have changed externally.\n */\n refresh,\n\n /**\n * Whether the component has finished its first client-side mount and hydration.\n */\n isHydrated,\n };\n}\n","<script setup lang=\"ts\" generic=\"T\">\nimport type {\n RenderedItem,\n ScrollAlignment,\n ScrollAlignmentOptions,\n ScrollDetails,\n VirtualScrollProps,\n} from '../types';\nimport type { VNodeChild } from 'vue';\n\nimport { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';\n\nimport {\n DEFAULT_ITEM_SIZE,\n useVirtualScroll,\n} from '../composables/useVirtualScroll';\nimport { isWindowLike } from '../utils/scroll';\nimport { calculateItemStyle } from '../utils/virtual-scroll-logic';\n\nexport interface Props<T = unknown> {\n /**\n * Array of items to be virtualized.\n * Required.\n */\n items: T[];\n\n /**\n * Fixed size of each item (in pixels) or a function that returns the size of an item.\n * Pass 0, null or undefined for dynamic size detection via ResizeObserver.\n * @default 40\n */\n itemSize?: number | ((item: T, index: number) => number) | null;\n\n /**\n * Direction of the scroll.\n * - 'vertical': Standard vertical list.\n * - 'horizontal': Standard horizontal list.\n * - 'both': Grid mode virtualizing both rows and columns.\n * @default 'vertical'\n */\n direction?: 'vertical' | 'horizontal' | 'both';\n\n /**\n * Number of items to render before the visible viewport.\n * Useful for smoother scrolling and keyboard navigation.\n * @default 5\n */\n bufferBefore?: number;\n\n /**\n * Number of items to render after the visible viewport.\n * @default 5\n */\n bufferAfter?: number;\n\n /**\n * The scrollable container element or window.\n * If not provided, the host element (root of VirtualScroll) is used.\n * @default hostRef\n */\n container?: HTMLElement | Window | null;\n\n /**\n * Range of items to render during Server-Side Rendering.\n * When provided, these items will be rendered in-flow before hydration.\n * @see SSRRange\n */\n ssrRange?: {\n /** First row index to render. */\n start: number;\n /** Last row index to render (exclusive). */\n end: number;\n /** First column index to render (for grid mode). */\n colStart?: number;\n /** Last column index to render (exclusive, for grid mode). */\n colEnd?: number;\n };\n\n /**\n * Number of columns for bidirectional (grid) scroll.\n * Only applicable when direction=\"both\".\n * @default 0\n */\n columnCount?: number;\n\n /**\n * Fixed width of columns (in pixels), an array of widths, or a function for column widths.\n * Pass 0, null or undefined for dynamic width detection via ResizeObserver.\n * Only applicable when direction=\"both\".\n * @default 100\n */\n columnWidth?: number | number[] | ((index: number) => number) | null;\n\n /**\n * The HTML tag to use for the root container.\n * @default 'div'\n */\n containerTag?: string;\n\n /**\n * The HTML tag to use for the items wrapper.\n * Useful for <table> integration (e.g. 'tbody').\n * @default 'div'\n */\n wrapperTag?: string;\n\n /**\n * The HTML tag to use for each item.\n * Useful for <table> integration (e.g. 'tr').\n * @default 'div'\n */\n itemTag?: string;\n\n /**\n * Additional padding at the start of the scroll container (top or left).\n * Can be a number (applied to current direction) or an object with x/y.\n * @default 0\n */\n scrollPaddingStart?: number | { x?: number; y?: number; };\n\n /**\n * Additional padding at the end of the scroll container (bottom or right).\n * @default 0\n */\n scrollPaddingEnd?: number | { x?: number; y?: number; };\n\n /**\n * Whether the content in the 'header' slot is sticky.\n * If true, the header size is measured and accounted for in scroll padding.\n * @default false\n */\n stickyHeader?: boolean;\n\n /**\n * Whether the content in the 'footer' slot is sticky.\n * @default false\n */\n stickyFooter?: boolean;\n\n /**\n * Gap between items in pixels (vertical gap in vertical/grid mode, horizontal gap in horizontal mode).\n * @default 0\n */\n gap?: number;\n\n /**\n * Gap between columns in pixels. Only applicable when direction=\"both\" or \"horizontal\".\n * @default 0\n */\n columnGap?: number;\n\n /**\n * Indices of items that should stick to the top/start of the viewport.\n * Supports iOS-style pushing effect where the next sticky item pushes the previous one.\n * @default []\n */\n stickyIndices?: number[];\n\n /**\n * Distance from the end of the scrollable area (in pixels) to trigger the 'load' event.\n * @default 200\n */\n loadDistance?: number;\n\n /**\n * Whether items are currently being loaded.\n * Prevents multiple 'load' events from triggering and shows the 'loading' slot.\n * @default false\n */\n loading?: boolean;\n\n /**\n * Whether to automatically restore and maintain scroll position when items are prepended to the list.\n * Perfect for chat applications.\n * @default false\n */\n restoreScrollOnPrepend?: boolean;\n\n /**\n * Initial scroll index to jump to immediately after mount.\n */\n initialScrollIndex?: number;\n\n /**\n * Alignment for the initial scroll index.\n * @default 'start'\n * @see ScrollAlignment\n */\n initialScrollAlign?: ScrollAlignment | ScrollAlignmentOptions;\n\n /**\n * Default size for items before they are measured by ResizeObserver.\n * Only used when itemSize is dynamic.\n * @default 40\n */\n defaultItemSize?: number;\n\n /**\n * Default width for columns before they are measured by ResizeObserver.\n * Only used when columnWidth is dynamic.\n * @default 100\n */\n defaultColumnWidth?: number;\n\n /**\n * Whether to show debug information (visible offsets and indices) over items.\n * @default false\n */\n debug?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props<T>>(), {\n direction: 'vertical',\n bufferBefore: 5,\n bufferAfter: 5,\n columnCount: 0,\n containerTag: 'div',\n wrapperTag: 'div',\n itemTag: 'div',\n scrollPaddingStart: 0,\n scrollPaddingEnd: 0,\n stickyHeader: false,\n stickyFooter: false,\n gap: 0,\n columnGap: 0,\n stickyIndices: () => [],\n loadDistance: 200,\n loading: false,\n restoreScrollOnPrepend: false,\n debug: false,\n});\n\nconst emit = defineEmits<{\n (e: 'scroll', details: ScrollDetails<T>): void;\n (e: 'load', direction: 'vertical' | 'horizontal'): void;\n (e: 'visibleRangeChange', range: { start: number; end: number; colStart: number; colEnd: number; }): void;\n}>();\n\nconst slots = defineSlots<{\n /**\n * Content rendered at the top of the scrollable area.\n * Can be made sticky using the `stickyHeader` prop.\n */\n header?: (props: Record<string, never>) => VNodeChild;\n\n /**\n * Scoped slot for rendering each individual item.\n */\n item?: (props: {\n /** The original data item from the `items` array. */\n item: T;\n /** The original index of the item in the `items` array. */\n index: number;\n /**\n * Information about the current visible range of columns (for grid mode).\n * @see ColumnRange\n */\n columnRange: {\n /** Index of the first rendered column. */\n start: number;\n /** Index of the last rendered column (exclusive). */\n end: number;\n /** Pixel offset from the start of the row to the first rendered cell. */\n padStart: number;\n /** Pixel offset from the last rendered cell to the end of the row. */\n padEnd: number;\n };\n /**\n * Helper function to get the width of a specific column.\n * Useful for setting consistent widths in grid mode.\n */\n getColumnWidth: (index: number) => number;\n /** Whether this item is configured to be sticky via `stickyIndices`. */\n isSticky?: boolean | undefined;\n /** Whether this item is currently in a sticky state (stuck at the top/start). */\n isStickyActive?: boolean | undefined;\n }) => VNodeChild;\n\n /**\n * Content shown at the end of the list when the `loading` prop is true.\n * Also prevents additional 'load' events from triggering while visible.\n */\n loading?: (props: Record<string, never>) => VNodeChild;\n\n /**\n * Content rendered at the bottom of the scrollable area.\n * Can be made sticky using the `stickyFooter` prop.\n */\n footer?: (props: Record<string, never>) => VNodeChild;\n}>();\n\nconst hostRef = ref<HTMLElement | null>(null);\nconst wrapperRef = ref<HTMLElement | null>(null);\nconst headerRef = ref<HTMLElement | null>(null);\nconst footerRef = ref<HTMLElement | null>(null);\nconst itemRefs = new Map<number, HTMLElement>();\n\nconst measuredPaddingStart = ref(0);\nconst measuredPaddingEnd = ref(0);\n\nconst isHeaderFooterInsideContainer = computed(() => {\n const container = props.container === undefined\n ? hostRef.value\n : props.container;\n\n return container === hostRef.value\n || (typeof window !== 'undefined' && (container === window || container === null));\n});\n\nconst virtualScrollProps = computed(() => {\n const pStart = props.scrollPaddingStart;\n const pEnd = props.scrollPaddingEnd;\n\n /* Trigger re-evaluation on items array mutations */\n // eslint-disable-next-line ts/no-unused-expressions\n props.items.length;\n\n const startX = typeof pStart === 'object'\n ? (pStart.x || 0)\n : ((props.direction === 'horizontal' || props.direction === 'both') ? (pStart || 0) : 0);\n const startY = typeof pStart === 'object'\n ? (pStart.y || 0)\n : ((props.direction === 'vertical' || props.direction === 'both') ? (pStart || 0) : 0);\n\n const endX = typeof pEnd === 'object'\n ? (pEnd.x || 0)\n : ((props.direction === 'horizontal' || props.direction === 'both') ? (pEnd || 0) : 0);\n const endY = typeof pEnd === 'object'\n ? (pEnd.y || 0)\n : ((props.direction === 'vertical' || props.direction === 'both') ? (pEnd || 0) : 0);\n\n return {\n items: props.items,\n itemSize: props.itemSize,\n direction: props.direction,\n bufferBefore: props.bufferBefore,\n bufferAfter: props.bufferAfter,\n container: props.container === undefined\n ? hostRef.value\n : props.container,\n hostElement: wrapperRef.value,\n ssrRange: props.ssrRange,\n columnCount: props.columnCount,\n columnWidth: props.columnWidth,\n scrollPaddingStart: {\n x: startX,\n y: startY + (props.stickyHeader && isHeaderFooterInsideContainer.value ? measuredPaddingStart.value : 0),\n },\n scrollPaddingEnd: {\n x: endX,\n y: endY + (props.stickyFooter && isHeaderFooterInsideContainer.value ? measuredPaddingEnd.value : 0),\n },\n gap: props.gap,\n columnGap: props.columnGap,\n stickyIndices: props.stickyIndices,\n loadDistance: props.loadDistance,\n loading: props.loading,\n restoreScrollOnPrepend: props.restoreScrollOnPrepend,\n initialScrollIndex: props.initialScrollIndex,\n initialScrollAlign: props.initialScrollAlign,\n defaultItemSize: props.defaultItemSize,\n defaultColumnWidth: props.defaultColumnWidth,\n debug: props.debug,\n } as VirtualScrollProps<T>;\n});\n\nconst {\n isHydrated,\n columnRange,\n renderedItems,\n scrollDetails,\n totalHeight,\n totalWidth,\n getColumnWidth,\n scrollToIndex,\n scrollToOffset,\n updateHostOffset,\n updateItemSizes,\n refresh: coreRefresh,\n stopProgrammaticScroll,\n} = useVirtualScroll(virtualScrollProps);\n\n/**\n * Resets all dynamic measurements and re-initializes from props.\n * Also triggers manual re-measurement of all currently rendered items.\n */\nfunction refresh() {\n coreRefresh();\n nextTick(() => {\n const updates: { index: number; inlineSize: number; blockSize: number; element?: HTMLElement; }[] = [];\n\n for (const [ index, el ] of itemRefs.entries()) {\n if (el) {\n updates.push({\n index,\n inlineSize: el.offsetWidth,\n blockSize: el.offsetHeight,\n element: el,\n });\n }\n }\n\n if (updates.length > 0) {\n updateItemSizes(updates);\n }\n });\n}\n\n// Watch for scroll details and emit event\nwatch(scrollDetails, (details, oldDetails) => {\n if (!isHydrated.value) {\n return;\n }\n emit('scroll', details);\n\n if (\n !oldDetails\n || details.range.start !== oldDetails.range.start\n || details.range.end !== oldDetails.range.end\n || details.columnRange.start !== oldDetails.columnRange.start\n || details.columnRange.end !== oldDetails.columnRange.end\n ) {\n emit('visibleRangeChange', {\n start: details.range.start,\n end: details.range.end,\n colStart: details.columnRange.start,\n colEnd: details.columnRange.end,\n });\n }\n\n if (props.loading) {\n return;\n }\n\n // vertical or both\n if (props.direction !== 'horizontal') {\n const remaining = details.totalSize.height - (details.scrollOffset.y + details.viewportSize.height);\n if (remaining <= props.loadDistance) {\n emit('load', 'vertical');\n }\n }\n // horizontal or both\n if (props.direction !== 'vertical') {\n const remaining = details.totalSize.width - (details.scrollOffset.x + details.viewportSize.width);\n if (remaining <= props.loadDistance) {\n emit('load', 'horizontal');\n }\n }\n});\n\nwatch(isHydrated, (hydrated) => {\n if (hydrated) {\n emit('visibleRangeChange', {\n start: scrollDetails.value.range.start,\n end: scrollDetails.value.range.end,\n colStart: scrollDetails.value.columnRange.start,\n colEnd: scrollDetails.value.columnRange.end,\n });\n }\n}, { once: true });\n\nconst hostResizeObserver = typeof window === 'undefined'\n ? null\n : new ResizeObserver(updateHostOffset);\n\nconst itemResizeObserver = typeof window === 'undefined'\n ? null\n : new ResizeObserver((entries) => {\n const updates: { index: number; inlineSize: number; blockSize: number; element?: HTMLElement; }[] = [];\n\n for (const entry of entries) {\n const target = entry.target as HTMLElement;\n const index = Number(target.dataset.index);\n const colIndex = target.dataset.colIndex;\n\n let inlineSize = entry.contentRect.width;\n let blockSize = entry.contentRect.height;\n\n if (entry.borderBoxSize && entry.borderBoxSize.length > 0) {\n inlineSize = entry.borderBoxSize[ 0 ]!.inlineSize;\n blockSize = entry.borderBoxSize[ 0 ]!.blockSize;\n } else {\n // Fallback for older browsers or if borderBoxSize is missing\n inlineSize = target.offsetWidth;\n blockSize = target.offsetHeight;\n }\n\n if (colIndex !== undefined) {\n // It's a cell measurement. row index is not strictly needed for column width.\n // We use -1 as a placeholder for row index if it's a cell measurement.\n updates.push({ index: -1, inlineSize, blockSize, element: target });\n } else if (!Number.isNaN(index)) {\n updates.push({ index, inlineSize, blockSize, element: target });\n }\n }\n\n if (updates.length > 0) {\n updateItemSizes(updates);\n }\n });\n\nconst extraResizeObserver = typeof window === 'undefined'\n ? null\n : new ResizeObserver(() => {\n measuredPaddingStart.value = headerRef.value?.offsetHeight || 0;\n measuredPaddingEnd.value = footerRef.value?.offsetHeight || 0;\n updateHostOffset();\n });\n\nwatch(headerRef, (newEl, oldEl) => {\n if (oldEl) {\n extraResizeObserver?.unobserve(oldEl);\n }\n if (newEl) {\n extraResizeObserver?.observe(newEl);\n }\n}, { immediate: true });\n\nwatch(footerRef, (newEl, oldEl) => {\n if (oldEl) {\n extraResizeObserver?.unobserve(oldEl);\n }\n if (newEl) {\n extraResizeObserver?.observe(newEl);\n }\n}, { immediate: true });\n\nonMounted(() => {\n if (hostRef.value) {\n hostResizeObserver?.observe(hostRef.value);\n }\n\n // Re-observe items that were set before observer was ready\n for (const el of itemRefs.values()) {\n itemResizeObserver?.observe(el);\n if (props.direction === 'both') {\n el.querySelectorAll('[data-col-index]').forEach((c) => itemResizeObserver?.observe(c));\n }\n }\n});\n\nwatch([ hostRef, wrapperRef ], ([ newHost ], [ oldHost ]) => {\n if (oldHost) {\n hostResizeObserver?.unobserve(oldHost);\n }\n if (newHost) {\n hostResizeObserver?.observe(newHost);\n }\n});\n\nfunction setItemRef(el: unknown, index: number) {\n if (el) {\n itemRefs.set(index, el as HTMLElement);\n itemResizeObserver?.observe(el as HTMLElement);\n\n if (props.direction === 'both') {\n (el as HTMLElement).querySelectorAll('[data-col-index]').forEach((c) => itemResizeObserver?.observe(c));\n }\n } else {\n const oldEl = itemRefs.get(index);\n if (oldEl) {\n itemResizeObserver?.unobserve(oldEl);\n if (props.direction === 'both') {\n oldEl.querySelectorAll('[data-col-index]').forEach((c) => itemResizeObserver?.unobserve(c));\n }\n itemRefs.delete(index);\n }\n }\n}\n\nfunction handleKeyDown(event: KeyboardEvent) {\n const { viewportSize, scrollOffset } = scrollDetails.value;\n const isHorizontal = props.direction !== 'vertical';\n const isVertical = props.direction !== 'horizontal';\n\n switch (event.key) {\n case 'Home':\n event.preventDefault();\n stopProgrammaticScroll();\n scrollToIndex(0, 0, 'start');\n break;\n case 'End': {\n event.preventDefault();\n stopProgrammaticScroll();\n const lastItemIndex = props.items.length - 1;\n const lastColIndex = (props.columnCount || 0) > 0 ? props.columnCount - 1 : 0;\n\n if (isHorizontal) {\n if (isVertical) {\n scrollToIndex(lastItemIndex, lastColIndex, 'end');\n } else {\n scrollToIndex(0, lastItemIndex, 'end');\n }\n } else {\n scrollToIndex(lastItemIndex, 0, 'end');\n }\n break;\n }\n case 'ArrowUp':\n event.preventDefault();\n stopProgrammaticScroll();\n scrollToOffset(null, scrollOffset.y - DEFAULT_ITEM_SIZE);\n break;\n case 'ArrowDown':\n event.preventDefault();\n stopProgrammaticScroll();\n scrollToOffset(null, scrollOffset.y + DEFAULT_ITEM_SIZE);\n break;\n case 'ArrowLeft':\n event.preventDefault();\n stopProgrammaticScroll();\n scrollToOffset(scrollOffset.x - DEFAULT_ITEM_SIZE, null);\n break;\n case 'ArrowRight':\n event.preventDefault();\n stopProgrammaticScroll();\n scrollToOffset(scrollOffset.x + DEFAULT_ITEM_SIZE, null);\n break;\n case 'PageUp':\n event.preventDefault();\n stopProgrammaticScroll();\n scrollToOffset(\n !isVertical && isHorizontal ? scrollOffset.x - viewportSize.width : null,\n isVertical ? scrollOffset.y - viewportSize.height : null,\n );\n break;\n case 'PageDown':\n event.preventDefault();\n stopProgrammaticScroll();\n scrollToOffset(\n !isVertical && isHorizontal ? scrollOffset.x + viewportSize.width : null,\n isVertical ? scrollOffset.y + viewportSize.height : null,\n );\n break;\n }\n}\n\nonUnmounted(() => {\n hostResizeObserver?.disconnect();\n itemResizeObserver?.disconnect();\n extraResizeObserver?.disconnect();\n});\n\nconst isWindowContainer = computed(() => isWindowLike(props.container));\n\nconst containerStyle = computed(() => {\n if (isWindowContainer.value) {\n return {\n ...(props.direction !== 'vertical' ? { whiteSpace: 'nowrap' as const } : {}),\n };\n }\n\n if (props.containerTag === 'table') {\n return {\n minInlineSize: props.direction === 'vertical' ? '100%' : 'auto',\n };\n }\n\n return {\n ...(props.direction !== 'vertical' ? { whiteSpace: 'nowrap' as const } : {}),\n };\n});\n\nconst wrapperStyle = computed(() => ({\n inlineSize: props.direction === 'vertical' ? '100%' : `${ totalWidth.value }px`,\n blockSize: props.direction === 'horizontal' ? '100%' : `${ totalHeight.value }px`,\n}));\n\nconst loadingStyle = computed(() => {\n const isHorizontal = props.direction === 'horizontal';\n\n return {\n display: isHorizontal ? 'inline-block' : 'block',\n ...(isHorizontal ? { blockSize: '100%', verticalAlign: 'top' } : { inlineSize: '100%' }),\n };\n});\n\nconst spacerStyle = computed(() => ({\n inlineSize: props.direction === 'vertical' ? '1px' : `${ totalWidth.value }px`,\n blockSize: props.direction === 'horizontal' ? '1px' : `${ totalHeight.value }px`,\n}));\n\nfunction getItemStyle(item: RenderedItem<T>) {\n return calculateItemStyle({\n containerTag: props.containerTag,\n direction: props.direction,\n isHydrated: isHydrated.value,\n item,\n itemSize: props.itemSize,\n paddingStartX: (virtualScrollProps.value.scrollPaddingStart as { x: number; y: number; }).x,\n paddingStartY: (virtualScrollProps.value.scrollPaddingStart as { x: number; y: number; }).y,\n });\n}\n\nconst isDebug = computed(() => props.debug);\nconst isTable = computed(() => props.containerTag === 'table');\nconst headerTag = computed(() => isTable.value ? 'thead' : 'div');\nconst footerTag = computed(() => isTable.value ? 'tfoot' : 'div');\n\ndefineExpose({\n /**\n * Detailed information about the current scroll state.\n * @see ScrollDetails\n * @see useVirtualScroll\n */\n scrollDetails,\n\n /**\n * Information about the current visible range of columns.\n * @see ColumnRange\n * @see useVirtualScroll\n */\n columnRange,\n\n /**\n * Helper to get the width of a specific column.\n * @param index - The column index.\n * @see useVirtualScroll\n */\n getColumnWidth,\n\n /**\n * Programmatically scroll to a specific row and/or column.\n *\n * @param rowIndex - The row index to scroll to. Pass null to only scroll horizontally.\n * @param colIndex - The column index to scroll to. Pass null to only scroll vertically.\n * @param options - Alignment and behavior options. Defaults to { align: 'auto', behavior: 'auto' }.\n * @see ScrollAlignment\n * @see ScrollToIndexOptions\n * @see useVirtualScroll\n */\n scrollToIndex,\n\n /**\n * Programmatically scroll to a specific pixel offset.\n *\n * @param x - The pixel offset to scroll to on the X axis. Pass null to keep current position.\n * @param y - The pixel offset to scroll to on the Y axis. Pass null to keep current position.\n * @param options - Scroll options (behavior). Defaults to { behavior: 'auto' }.\n * @see useVirtualScroll\n */\n scrollToOffset,\n\n /**\n * Resets all dynamic measurements and re-initializes from props.\n * @see useVirtualScroll\n */\n refresh,\n\n /**\n * Immediately stops any currently active smooth scroll animation and clears pending corrections.\n * @see useVirtualScroll\n */\n stopProgrammaticScroll,\n});\n</script>\n\n<template>\n <component\n :is=\"containerTag\"\n ref=\"hostRef\"\n class=\"virtual-scroll-container\"\n :class=\"[\n `virtual-scroll--${ direction }`,\n {\n 'virtual-scroll--hydrated': isHydrated,\n 'virtual-scroll--window': isWindowContainer,\n 'virtual-scroll--table': isTable,\n },\n ]\"\n :style=\"containerStyle\"\n tabindex=\"0\"\n @keydown=\"handleKeyDown\"\n @wheel.passive=\"stopProgrammaticScroll\"\n @pointerdown.passive=\"stopProgrammaticScroll\"\n @touchstart.passive=\"stopProgrammaticScroll\"\n >\n <component\n :is=\"headerTag\"\n v-if=\"slots.header\"\n ref=\"headerRef\"\n class=\"virtual-scroll-header\"\n :class=\"{ 'virtual-scroll--sticky': stickyHeader }\"\n >\n <slot name=\"header\" />\n </component>\n\n <component\n :is=\"wrapperTag\"\n ref=\"wrapperRef\"\n class=\"virtual-scroll-wrapper\"\n :style=\"wrapperStyle\"\n >\n <!-- Phantom element to push scroll height -->\n <component\n :is=\"itemTag\"\n v-if=\"isTable\"\n class=\"virtual-scroll-spacer\"\n :style=\"spacerStyle\"\n >\n <td style=\"padding: 0; border: none; block-size: inherit;\" />\n </component>\n\n <component\n :is=\"itemTag\"\n v-for=\"renderedItem in renderedItems\"\n :key=\"renderedItem.index\"\n :ref=\"(el: unknown) => setItemRef(el, renderedItem.index)\"\n :data-index=\"renderedItem.index\"\n class=\"virtual-scroll-item\"\n :class=\"{\n 'virtual-scroll--sticky': renderedItem.isStickyActive,\n 'virtual-scroll--debug': isDebug,\n }\"\n :style=\"getItemStyle(renderedItem)\"\n >\n <slot\n name=\"item\"\n :item=\"renderedItem.item\"\n :index=\"renderedItem.index\"\n :column-range=\"columnRange\"\n :get-column-width=\"getColumnWidth\"\n :is-sticky=\"renderedItem.isSticky\"\n :is-sticky-active=\"renderedItem.isStickyActive\"\n />\n <div v-if=\"isDebug\" class=\"virtual-scroll-debug-info\">\n #{{ renderedItem.index }} ({{ Math.round(renderedItem.offset.x) }}, {{ Math.round(renderedItem.offset.y) }})\n </div>\n </component>\n </component>\n\n <div\n v-if=\"loading && slots.loading\"\n class=\"virtual-scroll-loading\"\n :style=\"loadingStyle\"\n >\n <slot name=\"loading\" />\n </div>\n\n <component\n :is=\"footerTag\"\n v-if=\"slots.footer\"\n ref=\"footerRef\"\n class=\"virtual-scroll-footer\"\n :class=\"{ 'virtual-scroll--sticky': stickyFooter }\"\n >\n <slot name=\"footer\" />\n </component>\n </component>\n</template>\n\n<style scoped>\n.virtual-scroll-container {\n position: relative;\n block-size: 100%;\n inline-size: 100%;\n outline-offset: 1px;\n\n &:not(.virtual-scroll--window) {\n overflow: auto;\n overscroll-behavior: contain;\n }\n\n &.virtual-scroll--table {\n display: block;\n }\n}\n\n.virtual-scroll--horizontal {\n white-space: nowrap;\n}\n\n.virtual-scroll-wrapper {\n contain: layout;\n position: relative;\n\n :where(.virtual-scroll--hydrated > & > .virtual-scroll-item) {\n position: absolute;\n inset-block-start: 0;\n inset-inline-start: 0;\n }\n}\n\n.virtual-scroll-item {\n display: grid;\n box-sizing: border-box;\n will-change: transform;\n\n &:where(.virtual-scroll--debug) {\n outline: 1px dashed rgba(255, 0, 0, 0.5);\n background-color: rgba(255, 0, 0, 0.05);\n\n &:where(:hover) {\n background-color: rgba(255, 0, 0, 0.1);\n z-index: 100;\n }\n }\n}\n\n.virtual-scroll-debug-info {\n position: absolute;\n inset-block-start: 2px;\n inset-inline-end: 2px;\n background: rgba(0, 0, 0, 0.7);\n color: white;\n font-size: 10px;\n padding: 2px 4px;\n border-radius: 4px;\n pointer-events: none;\n z-index: 100;\n font-family: monospace;\n}\n\n.virtual-scroll-spacer {\n pointer-events: none;\n}\n\n.virtual-scroll-header,\n.virtual-scroll-footer {\n position: relative;\n z-index: 20;\n}\n\n.virtual-scroll--sticky {\n position: sticky;\n\n &:where(.virtual-scroll-header) {\n inset-block-start: 0;\n inset-inline-start: 0;\n min-inline-size: 100%;\n box-sizing: border-box;\n }\n\n &:where(.virtual-scroll-footer) {\n inset-block-end: 0;\n inset-inline-start: 0;\n min-inline-size: 100%;\n box-sizing: border-box;\n }\n\n &:where(.virtual-scroll-item) {\n z-index: 10;\n }\n}\n\n:is(tbody.virtual-scroll-wrapper, thead.virtual-scroll-header, tfoot.virtual-scroll-footer) {\n display: inline-flex;\n min-inline-size: 100%;\n & > :deep(tr) {\n display: inline-flex;\n min-inline-size: 100%;\n\n & > :is(td, th) {\n display: inline-block;\n align-items: center;\n }\n }\n}\n</style>\n","<script setup lang=\"ts\" generic=\"T\">\nimport type {\n RenderedItem,\n ScrollAlignment,\n ScrollAlignmentOptions,\n ScrollDetails,\n VirtualScrollProps,\n} from '../types';\nimport type { VNodeChild } from 'vue';\n\nimport { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';\n\nimport {\n DEFAULT_ITEM_SIZE,\n useVirtualScroll,\n} from '../composables/useVirtualScroll';\nimport { isWindowLike } from '../utils/scroll';\nimport { calculateItemStyle } from '../utils/virtual-scroll-logic';\n\nexport interface Props<T = unknown> {\n /**\n * Array of items to be virtualized.\n * Required.\n */\n items: T[];\n\n /**\n * Fixed size of each item (in pixels) or a function that returns the size of an item.\n * Pass 0, null or undefined for dynamic size detection via ResizeObserver.\n * @default 40\n */\n itemSize?: number | ((item: T, index: number) => number) | null;\n\n /**\n * Direction of the scroll.\n * - 'vertical': Standard vertical list.\n * - 'horizontal': Standard horizontal list.\n * - 'both': Grid mode virtualizing both rows and columns.\n * @default 'vertical'\n */\n direction?: 'vertical' | 'horizontal' | 'both';\n\n /**\n * Number of items to render before the visible viewport.\n * Useful for smoother scrolling and keyboard navigation.\n * @default 5\n */\n bufferBefore?: number;\n\n /**\n * Number of items to render after the visible viewport.\n * @default 5\n */\n bufferAfter?: number;\n\n /**\n * The scrollable container element or window.\n * If not provided, the host element (root of VirtualScroll) is used.\n * @default hostRef\n */\n container?: HTMLElement | Window | null;\n\n /**\n * Range of items to render during Server-Side Rendering.\n * When provided, these items will be rendered in-flow before hydration.\n * @see SSRRange\n */\n ssrRange?: {\n /** First row index to render. */\n start: number;\n /** Last row index to render (exclusive). */\n end: number;\n /** First column index to render (for grid mode). */\n colStart?: number;\n /** Last column index to render (exclusive, for grid mode). */\n colEnd?: number;\n };\n\n /**\n * Number of columns for bidirectional (grid) scroll.\n * Only applicable when direction=\"both\".\n * @default 0\n */\n columnCount?: number;\n\n /**\n * Fixed width of columns (in pixels), an array of widths, or a function for column widths.\n * Pass 0, null or undefined for dynamic width detection via ResizeObserver.\n * Only applicable when direction=\"both\".\n * @default 100\n */\n columnWidth?: number | number[] | ((index: number) => number) | null;\n\n /**\n * The HTML tag to use for the root container.\n * @default 'div'\n */\n containerTag?: string;\n\n /**\n * The HTML tag to use for the items wrapper.\n * Useful for <table> integration (e.g. 'tbody').\n * @default 'div'\n */\n wrapperTag?: string;\n\n /**\n * The HTML tag to use for each item.\n * Useful for <table> integration (e.g. 'tr').\n * @default 'div'\n */\n itemTag?: string;\n\n /**\n * Additional padding at the start of the scroll container (top or left).\n * Can be a number (applied to current direction) or an object with x/y.\n * @default 0\n */\n scrollPaddingStart?: number | { x?: number; y?: number; };\n\n /**\n * Additional padding at the end of the scroll container (bottom or right).\n * @default 0\n */\n scrollPaddingEnd?: number | { x?: number; y?: number; };\n\n /**\n * Whether the content in the 'header' slot is sticky.\n * If true, the header size is measured and accounted for in scroll padding.\n * @default false\n */\n stickyHeader?: boolean;\n\n /**\n * Whether the content in the 'footer' slot is sticky.\n * @default false\n */\n stickyFooter?: boolean;\n\n /**\n * Gap between items in pixels (vertical gap in vertical/grid mode, horizontal gap in horizontal mode).\n * @default 0\n */\n gap?: number;\n\n /**\n * Gap between columns in pixels. Only applicable when direction=\"both\" or \"horizontal\".\n * @default 0\n */\n columnGap?: number;\n\n /**\n * Indices of items that should stick to the top/start of the viewport.\n * Supports iOS-style pushing effect where the next sticky item pushes the previous one.\n * @default []\n */\n stickyIndices?: number[];\n\n /**\n * Distance from the end of the scrollable area (in pixels) to trigger the 'load' event.\n * @default 200\n */\n loadDistance?: number;\n\n /**\n * Whether items are currently being loaded.\n * Prevents multiple 'load' events from triggering and shows the 'loading' slot.\n * @default false\n */\n loading?: boolean;\n\n /**\n * Whether to automatically restore and maintain scroll position when items are prepended to the list.\n * Perfect for chat applications.\n * @default false\n */\n restoreScrollOnPrepend?: boolean;\n\n /**\n * Initial scroll index to jump to immediately after mount.\n */\n initialScrollIndex?: number;\n\n /**\n * Alignment for the initial scroll index.\n * @default 'start'\n * @see ScrollAlignment\n */\n initialScrollAlign?: ScrollAlignment | ScrollAlignmentOptions;\n\n /**\n * Default size for items before they are measured by ResizeObserver.\n * Only used when itemSize is dynamic.\n * @default 40\n */\n defaultItemSize?: number;\n\n /**\n * Default width for columns before they are measured by ResizeObserver.\n * Only used when columnWidth is dynamic.\n * @default 100\n */\n defaultColumnWidth?: number;\n\n /**\n * Whether to show debug information (visible offsets and indices) over items.\n * @default false\n */\n debug?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props<T>>(), {\n direction: 'vertical',\n bufferBefore: 5,\n bufferAfter: 5,\n columnCount: 0,\n containerTag: 'div',\n wrapperTag: 'div',\n itemTag: 'div',\n scrollPaddingStart: 0,\n scrollPaddingEnd: 0,\n stickyHeader: false,\n stickyFooter: false,\n gap: 0,\n columnGap: 0,\n stickyIndices: () => [],\n loadDistance: 200,\n loading: false,\n restoreScrollOnPrepend: false,\n debug: false,\n});\n\nconst emit = defineEmits<{\n (e: 'scroll', details: ScrollDetails<T>): void;\n (e: 'load', direction: 'vertical' | 'horizontal'): void;\n (e: 'visibleRangeChange', range: { start: number; end: number; colStart: number; colEnd: number; }): void;\n}>();\n\nconst slots = defineSlots<{\n /**\n * Content rendered at the top of the scrollable area.\n * Can be made sticky using the `stickyHeader` prop.\n */\n header?: (props: Record<string, never>) => VNodeChild;\n\n /**\n * Scoped slot for rendering each individual item.\n */\n item?: (props: {\n /** The original data item from the `items` array. */\n item: T;\n /** The original index of the item in the `items` array. */\n index: number;\n /**\n * Information about the current visible range of columns (for grid mode).\n * @see ColumnRange\n */\n columnRange: {\n /** Index of the first rendered column. */\n start: number;\n /** Index of the last rendered column (exclusive). */\n end: number;\n /** Pixel offset from the start of the row to the first rendered cell. */\n padStart: number;\n /** Pixel offset from the last rendered cell to the end of the row. */\n padEnd: number;\n };\n /**\n * Helper function to get the width of a specific column.\n * Useful for setting consistent widths in grid mode.\n */\n getColumnWidth: (index: number) => number;\n /** Whether this item is configured to be sticky via `stickyIndices`. */\n isSticky?: boolean | undefined;\n /** Whether this item is currently in a sticky state (stuck at the top/start). */\n isStickyActive?: boolean | undefined;\n }) => VNodeChild;\n\n /**\n * Content shown at the end of the list when the `loading` prop is true.\n * Also prevents additional 'load' events from triggering while visible.\n */\n loading?: (props: Record<string, never>) => VNodeChild;\n\n /**\n * Content rendered at the bottom of the scrollable area.\n * Can be made sticky using the `stickyFooter` prop.\n */\n footer?: (props: Record<string, never>) => VNodeChild;\n}>();\n\nconst hostRef = ref<HTMLElement | null>(null);\nconst wrapperRef = ref<HTMLElement | null>(null);\nconst headerRef = ref<HTMLElement | null>(null);\nconst footerRef = ref<HTMLElement | null>(null);\nconst itemRefs = new Map<number, HTMLElement>();\n\nconst measuredPaddingStart = ref(0);\nconst measuredPaddingEnd = ref(0);\n\nconst isHeaderFooterInsideContainer = computed(() => {\n const container = props.container === undefined\n ? hostRef.value\n : props.container;\n\n return container === hostRef.value\n || (typeof window !== 'undefined' && (container === window || container === null));\n});\n\nconst virtualScrollProps = computed(() => {\n const pStart = props.scrollPaddingStart;\n const pEnd = props.scrollPaddingEnd;\n\n /* Trigger re-evaluation on items array mutations */\n // eslint-disable-next-line ts/no-unused-expressions\n props.items.length;\n\n const startX = typeof pStart === 'object'\n ? (pStart.x || 0)\n : ((props.direction === 'horizontal' || props.direction === 'both') ? (pStart || 0) : 0);\n const startY = typeof pStart === 'object'\n ? (pStart.y || 0)\n : ((props.direction === 'vertical' || props.direction === 'both') ? (pStart || 0) : 0);\n\n const endX = typeof pEnd === 'object'\n ? (pEnd.x || 0)\n : ((props.direction === 'horizontal' || props.direction === 'both') ? (pEnd || 0) : 0);\n const endY = typeof pEnd === 'object'\n ? (pEnd.y || 0)\n : ((props.direction === 'vertical' || props.direction === 'both') ? (pEnd || 0) : 0);\n\n return {\n items: props.items,\n itemSize: props.itemSize,\n direction: props.direction,\n bufferBefore: props.bufferBefore,\n bufferAfter: props.bufferAfter,\n container: props.container === undefined\n ? hostRef.value\n : props.container,\n hostElement: wrapperRef.value,\n ssrRange: props.ssrRange,\n columnCount: props.columnCount,\n columnWidth: props.columnWidth,\n scrollPaddingStart: {\n x: startX,\n y: startY + (props.stickyHeader && isHeaderFooterInsideContainer.value ? measuredPaddingStart.value : 0),\n },\n scrollPaddingEnd: {\n x: endX,\n y: endY + (props.stickyFooter && isHeaderFooterInsideContainer.value ? measuredPaddingEnd.value : 0),\n },\n gap: props.gap,\n columnGap: props.columnGap,\n stickyIndices: props.stickyIndices,\n loadDistance: props.loadDistance,\n loading: props.loading,\n restoreScrollOnPrepend: props.restoreScrollOnPrepend,\n initialScrollIndex: props.initialScrollIndex,\n initialScrollAlign: props.initialScrollAlign,\n defaultItemSize: props.defaultItemSize,\n defaultColumnWidth: props.defaultColumnWidth,\n debug: props.debug,\n } as VirtualScrollProps<T>;\n});\n\nconst {\n isHydrated,\n columnRange,\n renderedItems,\n scrollDetails,\n totalHeight,\n totalWidth,\n getColumnWidth,\n scrollToIndex,\n scrollToOffset,\n updateHostOffset,\n updateItemSizes,\n refresh: coreRefresh,\n stopProgrammaticScroll,\n} = useVirtualScroll(virtualScrollProps);\n\n/**\n * Resets all dynamic measurements and re-initializes from props.\n * Also triggers manual re-measurement of all currently rendered items.\n */\nfunction refresh() {\n coreRefresh();\n nextTick(() => {\n const updates: { index: number; inlineSize: number; blockSize: number; element?: HTMLElement; }[] = [];\n\n for (const [ index, el ] of itemRefs.entries()) {\n if (el) {\n updates.push({\n index,\n inlineSize: el.offsetWidth,\n blockSize: el.offsetHeight,\n element: el,\n });\n }\n }\n\n if (updates.length > 0) {\n updateItemSizes(updates);\n }\n });\n}\n\n// Watch for scroll details and emit event\nwatch(scrollDetails, (details, oldDetails) => {\n if (!isHydrated.value) {\n return;\n }\n emit('scroll', details);\n\n if (\n !oldDetails\n || details.range.start !== oldDetails.range.start\n || details.range.end !== oldDetails.range.end\n || details.columnRange.start !== oldDetails.columnRange.start\n || details.columnRange.end !== oldDetails.columnRange.end\n ) {\n emit('visibleRangeChange', {\n start: details.range.start,\n end: details.range.end,\n colStart: details.columnRange.start,\n colEnd: details.columnRange.end,\n });\n }\n\n if (props.loading) {\n return;\n }\n\n // vertical or both\n if (props.direction !== 'horizontal') {\n const remaining = details.totalSize.height - (details.scrollOffset.y + details.viewportSize.height);\n if (remaining <= props.loadDistance) {\n emit('load', 'vertical');\n }\n }\n // horizontal or both\n if (props.direction !== 'vertical') {\n const remaining = details.totalSize.width - (details.scrollOffset.x + details.viewportSize.width);\n if (remaining <= props.loadDistance) {\n emit('load', 'horizontal');\n }\n }\n});\n\nwatch(isHydrated, (hydrated) => {\n if (hydrated) {\n emit('visibleRangeChange', {\n start: scrollDetails.value.range.start,\n end: scrollDetails.value.range.end,\n colStart: scrollDetails.value.columnRange.start,\n colEnd: scrollDetails.value.columnRange.end,\n });\n }\n}, { once: true });\n\nconst hostResizeObserver = typeof window === 'undefined'\n ? null\n : new ResizeObserver(updateHostOffset);\n\nconst itemResizeObserver = typeof window === 'undefined'\n ? null\n : new ResizeObserver((entries) => {\n const updates: { index: number; inlineSize: number; blockSize: number; element?: HTMLElement; }[] = [];\n\n for (const entry of entries) {\n const target = entry.target as HTMLElement;\n const index = Number(target.dataset.index);\n const colIndex = target.dataset.colIndex;\n\n let inlineSize = entry.contentRect.width;\n let blockSize = entry.contentRect.height;\n\n if (entry.borderBoxSize && entry.borderBoxSize.length > 0) {\n inlineSize = entry.borderBoxSize[ 0 ]!.inlineSize;\n blockSize = entry.borderBoxSize[ 0 ]!.blockSize;\n } else {\n // Fallback for older browsers or if borderBoxSize is missing\n inlineSize = target.offsetWidth;\n blockSize = target.offsetHeight;\n }\n\n if (colIndex !== undefined) {\n // It's a cell measurement. row index is not strictly needed for column width.\n // We use -1 as a placeholder for row index if it's a cell measurement.\n updates.push({ index: -1, inlineSize, blockSize, element: target });\n } else if (!Number.isNaN(index)) {\n updates.push({ index, inlineSize, blockSize, element: target });\n }\n }\n\n if (updates.length > 0) {\n updateItemSizes(updates);\n }\n });\n\nconst extraResizeObserver = typeof window === 'undefined'\n ? null\n : new ResizeObserver(() => {\n measuredPaddingStart.value = headerRef.value?.offsetHeight || 0;\n measuredPaddingEnd.value = footerRef.value?.offsetHeight || 0;\n updateHostOffset();\n });\n\nwatch(headerRef, (newEl, oldEl) => {\n if (oldEl) {\n extraResizeObserver?.unobserve(oldEl);\n }\n if (newEl) {\n extraResizeObserver?.observe(newEl);\n }\n}, { immediate: true });\n\nwatch(footerRef, (newEl, oldEl) => {\n if (oldEl) {\n extraResizeObserver?.unobserve(oldEl);\n }\n if (newEl) {\n extraResizeObserver?.observe(newEl);\n }\n}, { immediate: true });\n\nonMounted(() => {\n if (hostRef.value) {\n hostResizeObserver?.observe(hostRef.value);\n }\n\n // Re-observe items that were set before observer was ready\n for (const el of itemRefs.values()) {\n itemResizeObserver?.observe(el);\n if (props.direction === 'both') {\n el.querySelectorAll('[data-col-index]').forEach((c) => itemResizeObserver?.observe(c));\n }\n }\n});\n\nwatch([ hostRef, wrapperRef ], ([ newHost ], [ oldHost ]) => {\n if (oldHost) {\n hostResizeObserver?.unobserve(oldHost);\n }\n if (newHost) {\n hostResizeObserver?.observe(newHost);\n }\n});\n\nfunction setItemRef(el: unknown, index: number) {\n if (el) {\n itemRefs.set(index, el as HTMLElement);\n itemResizeObserver?.observe(el as HTMLElement);\n\n if (props.direction === 'both') {\n (el as HTMLElement).querySelectorAll('[data-col-index]').forEach((c) => itemResizeObserver?.observe(c));\n }\n } else {\n const oldEl = itemRefs.get(index);\n if (oldEl) {\n itemResizeObserver?.unobserve(oldEl);\n if (props.direction === 'both') {\n oldEl.querySelectorAll('[data-col-index]').forEach((c) => itemResizeObserver?.unobserve(c));\n }\n itemRefs.delete(index);\n }\n }\n}\n\nfunction handleKeyDown(event: KeyboardEvent) {\n const { viewportSize, scrollOffset } = scrollDetails.value;\n const isHorizontal = props.direction !== 'vertical';\n const isVertical = props.direction !== 'horizontal';\n\n switch (event.key) {\n case 'Home':\n event.preventDefault();\n stopProgrammaticScroll();\n scrollToIndex(0, 0, 'start');\n break;\n case 'End': {\n event.preventDefault();\n stopProgrammaticScroll();\n const lastItemIndex = props.items.length - 1;\n const lastColIndex = (props.columnCount || 0) > 0 ? props.columnCount - 1 : 0;\n\n if (isHorizontal) {\n if (isVertical) {\n scrollToIndex(lastItemIndex, lastColIndex, 'end');\n } else {\n scrollToIndex(0, lastItemIndex, 'end');\n }\n } else {\n scrollToIndex(lastItemIndex, 0, 'end');\n }\n break;\n }\n case 'ArrowUp':\n event.preventDefault();\n stopProgrammaticScroll();\n scrollToOffset(null, scrollOffset.y - DEFAULT_ITEM_SIZE);\n break;\n case 'ArrowDown':\n event.preventDefault();\n stopProgrammaticScroll();\n scrollToOffset(null, scrollOffset.y + DEFAULT_ITEM_SIZE);\n break;\n case 'ArrowLeft':\n event.preventDefault();\n stopProgrammaticScroll();\n scrollToOffset(scrollOffset.x - DEFAULT_ITEM_SIZE, null);\n break;\n case 'ArrowRight':\n event.preventDefault();\n stopProgrammaticScroll();\n scrollToOffset(scrollOffset.x + DEFAULT_ITEM_SIZE, null);\n break;\n case 'PageUp':\n event.preventDefault();\n stopProgrammaticScroll();\n scrollToOffset(\n !isVertical && isHorizontal ? scrollOffset.x - viewportSize.width : null,\n isVertical ? scrollOffset.y - viewportSize.height : null,\n );\n break;\n case 'PageDown':\n event.preventDefault();\n stopProgrammaticScroll();\n scrollToOffset(\n !isVertical && isHorizontal ? scrollOffset.x + viewportSize.width : null,\n isVertical ? scrollOffset.y + viewportSize.height : null,\n );\n break;\n }\n}\n\nonUnmounted(() => {\n hostResizeObserver?.disconnect();\n itemResizeObserver?.disconnect();\n extraResizeObserver?.disconnect();\n});\n\nconst isWindowContainer = computed(() => isWindowLike(props.container));\n\nconst containerStyle = computed(() => {\n if (isWindowContainer.value) {\n return {\n ...(props.direction !== 'vertical' ? { whiteSpace: 'nowrap' as const } : {}),\n };\n }\n\n if (props.containerTag === 'table') {\n return {\n minInlineSize: props.direction === 'vertical' ? '100%' : 'auto',\n };\n }\n\n return {\n ...(props.direction !== 'vertical' ? { whiteSpace: 'nowrap' as const } : {}),\n };\n});\n\nconst wrapperStyle = computed(() => ({\n inlineSize: props.direction === 'vertical' ? '100%' : `${ totalWidth.value }px`,\n blockSize: props.direction === 'horizontal' ? '100%' : `${ totalHeight.value }px`,\n}));\n\nconst loadingStyle = computed(() => {\n const isHorizontal = props.direction === 'horizontal';\n\n return {\n display: isHorizontal ? 'inline-block' : 'block',\n ...(isHorizontal ? { blockSize: '100%', verticalAlign: 'top' } : { inlineSize: '100%' }),\n };\n});\n\nconst spacerStyle = computed(() => ({\n inlineSize: props.direction === 'vertical' ? '1px' : `${ totalWidth.value }px`,\n blockSize: props.direction === 'horizontal' ? '1px' : `${ totalHeight.value }px`,\n}));\n\nfunction getItemStyle(item: RenderedItem<T>) {\n return calculateItemStyle({\n containerTag: props.containerTag,\n direction: props.direction,\n isHydrated: isHydrated.value,\n item,\n itemSize: props.itemSize,\n paddingStartX: (virtualScrollProps.value.scrollPaddingStart as { x: number; y: number; }).x,\n paddingStartY: (virtualScrollProps.value.scrollPaddingStart as { x: number; y: number; }).y,\n });\n}\n\nconst isDebug = computed(() => props.debug);\nconst isTable = computed(() => props.containerTag === 'table');\nconst headerTag = computed(() => isTable.value ? 'thead' : 'div');\nconst footerTag = computed(() => isTable.value ? 'tfoot' : 'div');\n\ndefineExpose({\n /**\n * Detailed information about the current scroll state.\n * @see ScrollDetails\n * @see useVirtualScroll\n */\n scrollDetails,\n\n /**\n * Information about the current visible range of columns.\n * @see ColumnRange\n * @see useVirtualScroll\n */\n columnRange,\n\n /**\n * Helper to get the width of a specific column.\n * @param index - The column index.\n * @see useVirtualScroll\n */\n getColumnWidth,\n\n /**\n * Programmatically scroll to a specific row and/or column.\n *\n * @param rowIndex - The row index to scroll to. Pass null to only scroll horizontally.\n * @param colIndex - The column index to scroll to. Pass null to only scroll vertically.\n * @param options - Alignment and behavior options. Defaults to { align: 'auto', behavior: 'auto' }.\n * @see ScrollAlignment\n * @see ScrollToIndexOptions\n * @see useVirtualScroll\n */\n scrollToIndex,\n\n /**\n * Programmatically scroll to a specific pixel offset.\n *\n * @param x - The pixel offset to scroll to on the X axis. Pass null to keep current position.\n * @param y - The pixel offset to scroll to on the Y axis. Pass null to keep current position.\n * @param options - Scroll options (behavior). Defaults to { behavior: 'auto' }.\n * @see useVirtualScroll\n */\n scrollToOffset,\n\n /**\n * Resets all dynamic measurements and re-initializes from props.\n * @see useVirtualScroll\n */\n refresh,\n\n /**\n * Immediately stops any currently active smooth scroll animation and clears pending corrections.\n * @see useVirtualScroll\n */\n stopProgrammaticScroll,\n});\n</script>\n\n<template>\n <component\n :is=\"containerTag\"\n ref=\"hostRef\"\n class=\"virtual-scroll-container\"\n :class=\"[\n `virtual-scroll--${ direction }`,\n {\n 'virtual-scroll--hydrated': isHydrated,\n 'virtual-scroll--window': isWindowContainer,\n 'virtual-scroll--table': isTable,\n },\n ]\"\n :style=\"containerStyle\"\n tabindex=\"0\"\n @keydown=\"handleKeyDown\"\n @wheel.passive=\"stopProgrammaticScroll\"\n @pointerdown.passive=\"stopProgrammaticScroll\"\n @touchstart.passive=\"stopProgrammaticScroll\"\n >\n <component\n :is=\"headerTag\"\n v-if=\"slots.header\"\n ref=\"headerRef\"\n class=\"virtual-scroll-header\"\n :class=\"{ 'virtual-scroll--sticky': stickyHeader }\"\n >\n <slot name=\"header\" />\n </component>\n\n <component\n :is=\"wrapperTag\"\n ref=\"wrapperRef\"\n class=\"virtual-scroll-wrapper\"\n :style=\"wrapperStyle\"\n >\n <!-- Phantom element to push scroll height -->\n <component\n :is=\"itemTag\"\n v-if=\"isTable\"\n class=\"virtual-scroll-spacer\"\n :style=\"spacerStyle\"\n >\n <td style=\"padding: 0; border: none; block-size: inherit;\" />\n </component>\n\n <component\n :is=\"itemTag\"\n v-for=\"renderedItem in renderedItems\"\n :key=\"renderedItem.index\"\n :ref=\"(el: unknown) => setItemRef(el, renderedItem.index)\"\n :data-index=\"renderedItem.index\"\n class=\"virtual-scroll-item\"\n :class=\"{\n 'virtual-scroll--sticky': renderedItem.isStickyActive,\n 'virtual-scroll--debug': isDebug,\n }\"\n :style=\"getItemStyle(renderedItem)\"\n >\n <slot\n name=\"item\"\n :item=\"renderedItem.item\"\n :index=\"renderedItem.index\"\n :column-range=\"columnRange\"\n :get-column-width=\"getColumnWidth\"\n :is-sticky=\"renderedItem.isSticky\"\n :is-sticky-active=\"renderedItem.isStickyActive\"\n />\n <div v-if=\"isDebug\" class=\"virtual-scroll-debug-info\">\n #{{ renderedItem.index }} ({{ Math.round(renderedItem.offset.x) }}, {{ Math.round(renderedItem.offset.y) }})\n </div>\n </component>\n </component>\n\n <div\n v-if=\"loading && slots.loading\"\n class=\"virtual-scroll-loading\"\n :style=\"loadingStyle\"\n >\n <slot name=\"loading\" />\n </div>\n\n <component\n :is=\"footerTag\"\n v-if=\"slots.footer\"\n ref=\"footerRef\"\n class=\"virtual-scroll-footer\"\n :class=\"{ 'virtual-scroll--sticky': stickyFooter }\"\n >\n <slot name=\"footer\" />\n </component>\n </component>\n</template>\n\n<style scoped>\n.virtual-scroll-container {\n position: relative;\n block-size: 100%;\n inline-size: 100%;\n outline-offset: 1px;\n\n &:not(.virtual-scroll--window) {\n overflow: auto;\n overscroll-behavior: contain;\n }\n\n &.virtual-scroll--table {\n display: block;\n }\n}\n\n.virtual-scroll--horizontal {\n white-space: nowrap;\n}\n\n.virtual-scroll-wrapper {\n contain: layout;\n position: relative;\n\n :where(.virtual-scroll--hydrated > & > .virtual-scroll-item) {\n position: absolute;\n inset-block-start: 0;\n inset-inline-start: 0;\n }\n}\n\n.virtual-scroll-item {\n display: grid;\n box-sizing: border-box;\n will-change: transform;\n\n &:where(.virtual-scroll--debug) {\n outline: 1px dashed rgba(255, 0, 0, 0.5);\n background-color: rgba(255, 0, 0, 0.05);\n\n &:where(:hover) {\n background-color: rgba(255, 0, 0, 0.1);\n z-index: 100;\n }\n }\n}\n\n.virtual-scroll-debug-info {\n position: absolute;\n inset-block-start: 2px;\n inset-inline-end: 2px;\n background: rgba(0, 0, 0, 0.7);\n color: white;\n font-size: 10px;\n padding: 2px 4px;\n border-radius: 4px;\n pointer-events: none;\n z-index: 100;\n font-family: monospace;\n}\n\n.virtual-scroll-spacer {\n pointer-events: none;\n}\n\n.virtual-scroll-header,\n.virtual-scroll-footer {\n position: relative;\n z-index: 20;\n}\n\n.virtual-scroll--sticky {\n position: sticky;\n\n &:where(.virtual-scroll-header) {\n inset-block-start: 0;\n inset-inline-start: 0;\n min-inline-size: 100%;\n box-sizing: border-box;\n }\n\n &:where(.virtual-scroll-footer) {\n inset-block-end: 0;\n inset-inline-start: 0;\n min-inline-size: 100%;\n box-sizing: border-box;\n }\n\n &:where(.virtual-scroll-item) {\n z-index: 10;\n }\n}\n\n:is(tbody.virtual-scroll-wrapper, thead.virtual-scroll-header, tfoot.virtual-scroll-footer) {\n display: inline-flex;\n min-inline-size: 100%;\n & > :deep(tr) {\n display: inline-flex;\n min-inline-size: 100%;\n\n & > :is(td, th) {\n display: inline-block;\n align-items: center;\n }\n }\n}\n</style>\n"],"mappings":"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"}