@pdanpdan/virtual-scroll 0.8.0 → 0.9.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":["EMPTY_SCROLL_DETAILS: ScrollDetails<unknown>","nextStickyIdx: number | undefined","prevStickyIdx: number | undefined","align: ScrollAlignment | ScrollAlignmentOptions | ScrollToIndexOptions | undefined","effectiveAlignX: ScrollAlignment","effectiveAlignY: ScrollAlignment","style: Record<string, string | number | undefined>","scrollTimeout: ReturnType<typeof setTimeout> | undefined","computedStyle: CSSStyleDeclaration | null","lastItems: T[]","behavior: 'auto' | 'smooth' | undefined","scrollOptions: ScrollToOptions","lastRenderedItems: RenderedItem<T>[]","items: RenderedItem<T>[]","sortedIndices: number[]","correctionOptions: ScrollToIndexOptions","resizeObserver: ResizeObserver | null","directionObserver: MutationObserver | null","directionInterval: ReturnType<typeof setInterval> | undefined","cleanup: (() => void) | undefined"],"sources":["../src/types.ts","../src/utils/fenwick-tree.ts","../src/utils/scroll.ts","../src/utils/virtual-scroll-logic.ts","../src/composables/useVirtualScroll.ts","../src/composables/useVirtualScrollbar.ts","../src/components/VirtualScrollbar.vue","../src/components/VirtualScrollbar.vue","../src/components/VirtualScroll.vue","../src/components/VirtualScroll.vue"],"sourcesContent":["/** Default fallback size for items (VU). */\nexport const DEFAULT_ITEM_SIZE = 40;\n/** Default fallback width for columns (VU). */\nexport const DEFAULT_COLUMN_WIDTH = 100;\n/** Default number of items to render outside the viewport. */\nexport const DEFAULT_BUFFER = 5;\n\n/** Represents a point in 2D space. */\nexport interface Point {\n /** X coordinate. */\n x: number;\n /** Y coordinate. */\n y: number;\n}\n\n/** Represents dimensions in 2D space. */\nexport interface Size {\n /** Width dimension. */\n width: number;\n /** Height dimension. */\n height: number;\n}\n\n/** Initial empty state for scroll details. */\nexport const EMPTY_SCROLL_DETAILS: ScrollDetails<unknown> = {\n items: [],\n currentIndex: 0,\n currentColIndex: 0,\n currentEndIndex: 0,\n currentEndColIndex: 0,\n scrollOffset: { x: 0, y: 0 },\n displayScrollOffset: { x: 0, y: 0 },\n viewportSize: { width: 0, height: 0 },\n displayViewportSize: { width: 0, height: 0 },\n totalSize: { width: 0, height: 0 },\n isScrolling: false,\n isProgrammaticScroll: false,\n range: { start: 0, end: 0 },\n columnRange: { start: 0, end: 0, padStart: 0, padEnd: 0 },\n};\n\n/**\n * The direction of the virtual scroll.\n * - 'vertical': Single-column vertical scrolling.\n * - 'horizontal': Single-row horizontal scrolling.\n * - 'both': Bidirectional grid-based scrolling.\n */\nexport type ScrollDirection = 'vertical' | 'horizontal' | 'both';\n\n/**\n * Alignment of an item within the viewport after a scroll operation.\n * - 'start': Aligns item to the top or left edge.\n * - 'center': Aligns item to the center of the viewport.\n * - 'end': Aligns item to the bottom or right edge.\n * - 'auto': Smart alignment. If visible, stays. If not, aligns to nearest edge.\n */\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 (horizontal) axis. */\n x?: ScrollAlignment;\n /** Alignment on the Y (vertical) axis. */\n y?: ScrollAlignment;\n}\n\n/** Options for the `scrollToIndex` method. */\nexport interface ScrollToIndexOptions {\n /**\n * Where to align the item in the viewport.\n * Can be a single value for both axes or an object for individual control.\n * @default 'auto'\n */\n align?: ScrollAlignment | ScrollAlignmentOptions;\n\n /**\n * Scroll behavior.\n * - 'auto': Instant jump.\n * - 'smooth': Animated transition.\n * @default 'smooth'\n */\n behavior?: 'auto' | 'smooth';\n\n /**\n * Internal flag for recursive correction calls.\n * Users should generally not set this.\n * @internal\n */\n isCorrection?: boolean;\n}\n\n/** Represents an item currently rendered in the virtual scroll area. */\nexport interface RenderedItem<T = unknown> {\n /** The original data item from the provided source array. */\n item: T;\n /** The 0-based index of the item in the original array. */\n index: number;\n /** The calculated pixel offset relative to the items wrapper in display pixels (DU). */\n offset: Point;\n /** The current measured or estimated size of the item in virtual units (VU). */\n size: Size;\n /** The original horizontal pixel offset before any sticky adjustments in VU. */\n originalX: number;\n /** The original vertical pixel offset before any sticky adjustments in VU. */\n originalY: number;\n /** Whether this item is configured to be sticky via the `stickyIndices` property. */\n isSticky?: boolean;\n /** Whether this item is currently in a stuck state at the viewport edge. */\n isStickyActive?: boolean;\n /** Whether this item is currently in a stuck state at the horizontal viewport edge. */\n isStickyActiveX?: boolean;\n /** Whether this item is currently in a stuck state at the vertical viewport edge. */\n isStickyActiveY?: boolean;\n /** The relative translation applied to the item for the sticky pushing effect in DU. */\n stickyOffset: Point;\n}\n\n/** Information about the currently visible range of columns and their paddings. */\nexport interface ColumnRange {\n /** Inclusive start index. */\n start: number;\n /** Exclusive end index. */\n end: number;\n /** Pixel padding to maintain at the start of the row in VU. */\n padStart: number;\n /** Pixel padding to maintain at the end of the row in VU. */\n padEnd: number;\n}\n\n/** Comprehensive state of the virtual scroll system. */\nexport interface ScrollDetails<T = unknown> {\n /** List of items currently rendered in the DOM buffer. */\n items: RenderedItem<T>[];\n /** Index of the first item visible below any sticky header in the viewport. */\n currentIndex: number;\n /** Index of the first column visible after any sticky column in the viewport (grid mode). */\n currentColIndex: number;\n /** Index of the last item visible above any sticky footer in the viewport. */\n currentEndIndex: number;\n /** Index of the last column visible before any sticky end column in the viewport (grid mode). */\n currentEndColIndex: number;\n /** Current relative pixel scroll position from the content start in VU. */\n scrollOffset: Point;\n /** Current display pixel scroll position (before scaling) in DU. */\n displayScrollOffset: Point;\n /** Current dimensions of the visible viewport area in VU. */\n viewportSize: Size;\n /** Current dimensions of the visible viewport area in display pixels (DU). */\n displayViewportSize: Size;\n /** Total calculated or estimated size of all items and gaps in VU. */\n totalSize: Size;\n /** Whether the container is currently being scrolled by the user or an animation. */\n isScrolling: boolean;\n /** Whether the current scroll operation was initiated programmatically. */\n isProgrammaticScroll: boolean;\n /** The range of item indices currently being rendered. */\n range: {\n /** Inclusive start index. */\n start: number;\n /** Exclusive end index. */\n end: number;\n };\n /** The range of column indices and associated paddings currently being rendered. */\n columnRange: ColumnRange;\n}\n\n/** Helper to get ARIA attributes for an item. */\nexport type GetItemAriaProps = (index: number) => Record<string, string | number | undefined>;\n\n/**\n * Configuration for Server-Side Rendering.\n * Defines which items are rendered statically on the server.\n */\nexport interface SSRRange {\n /** First row index (for list or grid). */\n start: number;\n /** Exclusive last row index (for list or grid). */\n end: number;\n /** First column index (for grid mode). */\n colStart?: number;\n /** Exclusive last column index (for grid mode). */\n colEnd?: number;\n}\n\n/** Pixel padding configuration in display pixels (DU). */\nexport type PaddingValue = number | { x?: number; y?: number; };\n\n/** Base configuration properties shared between the component and the composable. */\nexport interface VirtualScrollBaseProps<T = unknown> {\n /** Array of data items to virtualize. */\n items: T[];\n\n /**\n * Fixed size of each item in virtual units (VU) or a function that returns the size of an item.\n * Pass `0`, `null` or `undefined` for automatic dynamic size detection via `ResizeObserver`.\n */\n itemSize?: number | ((item: T, index: number) => number) | null | undefined;\n\n /**\n * Direction of the virtual scroll.\n * @default 'vertical'\n */\n direction?: ScrollDirection | undefined;\n\n /**\n * Number of items to render before the visible viewport.\n * @default 5\n */\n bufferBefore?: number | undefined;\n\n /**\n * Number of items to render after the visible viewport.\n * @default 5\n */\n bufferAfter?: number | undefined;\n\n /**\n * The scrollable element or window object.\n * If not provided, virtualization usually happens relative to the `hostRef`.\n */\n container?: HTMLElement | Window | null | undefined;\n\n /**\n * Configuration for Server-Side Rendering.\n * Defines which items are rendered statically on the server.\n */\n ssrRange?: SSRRange | undefined;\n\n /**\n * Number of columns for bidirectional grid scrolling.\n */\n columnCount?: number | undefined;\n\n /**\n * Fixed width of columns in VU, an array of widths, or a function returning widths.\n * Pass `0`, `null` or `undefined` for dynamic column detection.\n */\n columnWidth?: number | number[] | ((index: number) => number) | null | undefined;\n\n /**\n * Pixel padding at the start of the scroll container in display pixels (DU).\n */\n scrollPaddingStart?: PaddingValue | undefined;\n\n /**\n * Pixel padding at the end of the scroll container in DU.\n */\n scrollPaddingEnd?: PaddingValue | undefined;\n\n /**\n * Gap between items in virtual units (VU).\n * Applied vertically in list/grid mode, horizontally in horizontal list mode.\n */\n gap?: number | undefined;\n\n /**\n * Gap between columns in VU.\n * Applied in horizontal and bidirectional grid modes.\n */\n columnGap?: number | undefined;\n\n /**\n * List of indices that should stick to the viewport edge.\n */\n stickyIndices?: number[] | undefined;\n\n /**\n * Threshold distance from the end in display pixels (DU) to emit the 'load' event.\n * @default 200\n */\n loadDistance?: number | undefined;\n\n /**\n * Whether data is currently loading.\n */\n loading?: boolean | undefined;\n\n /**\n * Whether to automatically restore and maintain scroll position when items are prepended to the array.\n */\n restoreScrollOnPrepend?: boolean | undefined;\n\n /**\n * Initial row index to jump to on mount.\n */\n initialScrollIndex?: number | undefined;\n\n /**\n * Initial scroll alignment logic.\n * @default 'start'\n */\n initialScrollAlign?: ScrollAlignment | ScrollAlignmentOptions | undefined;\n\n /**\n * Default fallback size for items before they are measured in VU.\n */\n defaultItemSize?: number | undefined;\n\n /**\n * Default fallback width for columns before they are measured in VU.\n */\n defaultColumnWidth?: number | undefined;\n\n /**\n * Enable debug visualization of buffers and indices.\n */\n debug?: boolean | undefined;\n\n /**\n * ARIA role for the scroll container.\n * Defaults to 'list' for vertical/horizontal and 'grid' for both.\n */\n role?: string | undefined;\n\n /**\n * ARIA label for the scroll container.\n */\n ariaLabel?: string | undefined;\n\n /**\n * ID of the element that labels the scroll container.\n */\n ariaLabelledby?: string | undefined;\n\n /**\n * ARIA role for each rendered item.\n * Defaults to 'listitem' for list roles and 'row' for grid roles.\n * Set to 'none' or 'presentation' to disable automatic role assignment on the wrapper.\n */\n itemRole?: string | undefined;\n}\n\n/** Configuration properties for the `useVirtualScroll` composable. */\nexport interface VirtualScrollProps<T = unknown> extends VirtualScrollBaseProps<T> {\n /**\n * The host element that directly wraps the absolute-positioned items.\n * Used for calculating relative offsets in display pixels (DU).\n */\n hostElement?: HTMLElement | null | undefined;\n\n /**\n * The root element of the VirtualScroll component.\n * Used for calculating relative offsets in display pixels (DU).\n */\n hostRef?: HTMLElement | null | undefined;\n\n /**\n * Size of sticky elements at the start of the viewport (top or left) in DU.\n * Used to adjust the visible range and item positioning without increasing content size.\n */\n stickyStart?: PaddingValue | undefined;\n\n /**\n * Size of sticky elements at the end of the viewport (bottom or right) in DU.\n * Used to adjust the visible range without increasing content size.\n */\n stickyEnd?: PaddingValue | undefined;\n\n /**\n * Extra padding (display pixels - DU) at the start of the flow (e.g. non-sticky header).\n */\n flowPaddingStart?: PaddingValue | undefined;\n\n /**\n * Extra padding (DU) at the end of the flow (e.g. non-sticky footer).\n */\n flowPaddingEnd?: PaddingValue | undefined;\n}\n\n/** Help provide axis specific information to the scrollbar. */\nexport type ScrollAxis = 'vertical' | 'horizontal';\n\n/** Properties for the `VirtualScrollbar` component. */\nexport interface VirtualScrollbarProps {\n /**\n * The axis for this scrollbar.\n * - 'vertical': Vertical scrollbar.\n * - 'horizontal': Horizontal scrollbar.\n * @default 'vertical'\n */\n axis?: ScrollAxis;\n\n /**\n * Total size of the scrollable content in pixels.\n */\n totalSize: number;\n\n /**\n * Current scroll position in pixels.\n */\n position: number;\n\n /**\n * Viewport size in pixels.\n */\n viewportSize: number;\n\n /**\n * Function to scroll to a specific pixel offset on this axis.\n * @param offset - The pixel offset to scroll to.\n */\n scrollToOffset?: (offset: number) => void;\n\n /**\n * The ID of the container element this scrollbar controls.\n */\n containerId?: string;\n\n /**\n * Whether the scrollbar is in Right-to-Left (RTL) mode.\n * @default false\n */\n isRtl?: boolean;\n\n /**\n * Accessible label for the scrollbar.\n */\n ariaLabel?: string;\n}\n\n/** Properties passed to the 'scrollbar' scoped slot. */\nexport interface ScrollbarSlotProps {\n /** The axis for this scrollbar. */\n axis: ScrollAxis;\n /** Current scroll position as a percentage (0 to 1). */\n positionPercent: number;\n /** Viewport size as a percentage of total size (0 to 1). */\n viewportPercent: number;\n /** Calculated thumb size as a percentage of the track size (0 to 100). */\n thumbSizePercent: number;\n /** Calculated thumb position as a percentage of the track size (0 to 100). */\n thumbPositionPercent: number;\n\n /**\n * Attributes and event listeners to be bound to the scrollbar track element.\n * Use `v-bind=\"trackProps\"` on your track element.\n */\n trackProps: Record<string, unknown>;\n\n /**\n * Attributes and event listeners to be bound to the scrollbar thumb element.\n * Use `v-bind=\"thumbProps\"` on your thumb element.\n */\n thumbProps: Record<string, unknown>;\n\n /**\n * Grouped props for the `VirtualScrollbar` component.\n * Useful for passing directly to `<VirtualScrollbar v-bind=\"scrollbarProps\" />`.\n */\n scrollbarProps: VirtualScrollbarProps;\n\n /** Whether the thumb is currently being dragged. */\n isDragging: boolean;\n}\n\n/** Properties passed to the 'item' scoped slot. */\nexport interface ItemSlotProps<T = unknown> {\n /** The original data item being rendered. */\n item: T;\n /** The 0-based index of the item. */\n index: number;\n /** Helper to get ARIA attributes for the item. */\n getItemAriaProps: GetItemAriaProps;\n /** Information about the currently visible range of columns. */\n columnRange: ColumnRange;\n /** Helper to get the current calculated width of any column index. */\n getColumnWidth: (index: number) => number;\n /** Helper to get ARIA attributes for a cell. */\n getCellAriaProps: (colIndex: number) => Record<string, string | number | undefined>;\n /** Vertical gap between items. */\n gap: number;\n /** Horizontal gap between columns. */\n columnGap: number;\n /** Whether this item index is configured as sticky. */\n isSticky?: boolean | undefined;\n /** Whether this item is currently in a sticky state at the edge. */\n isStickyActive?: boolean | undefined;\n /** Whether this item is currently in a sticky state at the horizontal edge. */\n isStickyActiveX?: boolean | undefined;\n /** Whether this item is currently in a sticky state at the vertical edge. */\n isStickyActiveY?: boolean | undefined;\n /** The calculated pixel offset relative to the items wrapper in display pixels (DU). */\n offset: {\n /** Horizontal offset (left) in DU. */\n x: number;\n /** Vertical offset (top) in DU. */\n y: number;\n };\n}\n\n/** Configuration properties for the `VirtualScroll` component. */\nexport interface VirtualScrollComponentProps<T = unknown> extends VirtualScrollBaseProps<T> {\n /** The HTML tag to use for the root 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 /** Whether the content in the 'header' slot is sticky. */\n stickyHeader?: boolean;\n /** Whether the content in the 'footer' slot is sticky. */\n stickyFooter?: boolean;\n /** Whether to use virtual scrollbars for styling purposes. */\n virtualScrollbar?: boolean;\n}\n\n/** Exposed methods and properties of the `VirtualScroll` component instance. */\nexport interface VirtualScrollInstance<T = unknown> extends VirtualScrollComponentProps<T> {\n /** Detailed information about the current scroll state. */\n scrollDetails: ScrollDetails<T>;\n /** Information about the current visible range of columns. */\n columnRange: ScrollDetails<T>[ 'columnRange' ];\n /** Helper to get the width of a specific column. */\n getColumnWidth: (index: number) => number;\n /** Helper to get the height of a specific row. */\n getRowHeight: (index: number) => number;\n /** Helper to get ARIA attributes for a cell. */\n getCellAriaProps: (colIndex: number) => Record<string, string | number | undefined>;\n /** Helper to get the virtual offset of a specific row. */\n getRowOffset: (index: number) => number;\n /** Helper to get the virtual offset of a specific column. */\n getColumnOffset: (index: number) => number;\n /** Helper to get the virtual offset of a specific item. */\n getItemOffset: (index: number) => number;\n /** Helper to get the size of a specific item along the scroll axis. */\n getItemSize: (index: number) => number;\n /** Programmatically scroll to a specific row and/or column. */\n scrollToIndex: (rowIndex: number | null | undefined, colIndex: number | null | undefined, options?: ScrollAlignment | ScrollAlignmentOptions | ScrollToIndexOptions) => void;\n /** Programmatically scroll to a specific pixel offset. */\n scrollToOffset: (x?: number | null, y?: number | null, options?: { behavior?: 'auto' | 'smooth'; }) => void;\n /** Resets all dynamic measurements and re-initializes from props. */\n refresh: () => void;\n /** Immediately stops any currently active smooth scroll animation and clears pending corrections. */\n stopProgrammaticScroll: () => void;\n /** Detects the current direction (LTR/RTL) of the scroll container. */\n updateDirection: () => void;\n /** Whether the scroll container is in Right-to-Left (RTL) mode. */\n isRtl: boolean;\n /** Whether the component has finished its first client - side mount and hydration. */\n isHydrated: boolean;\n /** Coordinate scaling factor for X axis. */\n scaleX: number;\n /** Coordinate scaling factor for Y axis. */\n scaleY: number;\n /** Physical width of the content in the DOM (clamped to browser limits). */\n renderedWidth: number;\n /** Physical height of the content in the DOM (clamped to browser limits). */\n renderedHeight: number;\n /** Absolute offset of the component within its container. */\n componentOffset: Point;\n /** Properties for the vertical scrollbar. */\n scrollbarPropsVertical: ScrollbarSlotProps | null;\n /** Properties for the horizontal scrollbar. */\n scrollbarPropsHorizontal: ScrollbarSlotProps | null;\n}\n\n/** Parameters for calculating the scroll target position. */\nexport interface ScrollTargetParams {\n /** Row index to target. */\n rowIndex: number | null | undefined;\n /** Column index to target. */\n colIndex: number | null | undefined;\n /** Scroll options. */\n options?: ScrollAlignment | ScrollAlignmentOptions | ScrollToIndexOptions | undefined;\n /** Current scroll direction. */\n direction: ScrollDirection;\n /** Current viewport width. */\n viewportWidth: number;\n /** Current viewport height. */\n viewportHeight: number;\n /** Current total estimated width. */\n totalWidth: number;\n /** Current total estimated height. */\n totalHeight: number;\n /** Item gap. */\n gap: number;\n /** Column gap. */\n columnGap: number;\n /** Fixed item size. */\n fixedSize: number | null;\n /** Fixed column width. */\n fixedWidth: number | null;\n /** Current relative X scroll. */\n relativeScrollX: number;\n /** Current relative Y scroll. */\n relativeScrollY: number;\n /** Resolver for item height. */\n getItemSizeY: (index: number) => number;\n /** Resolver for item width. */\n getItemSizeX: (index: number) => number;\n /** Prefix sum resolver for item height. */\n getItemQueryY: (index: number) => number;\n /** Prefix sum resolver for item width. */\n getItemQueryX: (index: number) => number;\n /** Resolver for column size. */\n getColumnSize: (index: number) => number;\n /** Prefix sum resolver for column width. */\n getColumnQuery: (index: number) => number;\n /** Coordinate scaling factor for X axis. */\n scaleX: number;\n /** Coordinate scaling factor for Y axis. */\n scaleY: number;\n /** Host offset on X axis in display pixels. */\n hostOffsetX: number;\n /** Host offset on Y axis in display pixels. */\n hostOffsetY: number;\n /** List of sticky indices. */\n stickyIndices?: number[] | undefined;\n /** Sticky start offset on X axis. */\n stickyStartX?: number | undefined;\n /** Sticky start offset on Y axis. */\n stickyStartY?: number | undefined;\n /** Sticky end offset on X axis. */\n stickyEndX?: number | undefined;\n /** Sticky end offset on Y axis. */\n stickyEndY?: number | undefined;\n /** Flow padding start on X axis. */\n flowPaddingStartX?: number | undefined;\n /** Flow padding start on Y axis. */\n flowPaddingStartY?: number | undefined;\n /** Scroll padding start on X axis. */\n paddingStartX?: number | undefined;\n /** Scroll padding start on Y axis. */\n paddingStartY?: number | undefined;\n /** Scroll padding end on X axis. */\n paddingEndX?: number | undefined;\n /** Scroll padding end on Y axis. */\n paddingEndY?: number | undefined;\n}\n\n/** Calculated scroll target result. */\nexport interface ScrollTargetResult {\n /** Target relative horizontal position. */\n targetX: number;\n /** Target relative vertical position. */\n targetY: number;\n /** Resolved width of the target item. */\n itemWidth: number;\n /** Resolved height of the target item. */\n itemHeight: number;\n /** Effective alignment used for X axis. */\n effectiveAlignX: ScrollAlignment;\n /** Effective alignment used for Y axis. */\n effectiveAlignY: ScrollAlignment;\n}\n\n/** Parameters for calculating the visible range of items. */\nexport interface RangeParams {\n /** Scroll direction. */\n direction: ScrollDirection;\n /** Relative horizontal scroll position. */\n relativeScrollX: number;\n /** Relative vertical scroll position. */\n relativeScrollY: number;\n /** Usable viewport width. */\n usableWidth: number;\n /** Usable viewport height. */\n usableHeight: number;\n /** Total item count. */\n itemsLength: number;\n /** Buffer items before. */\n bufferBefore: number;\n /** Buffer items after. */\n bufferAfter: number;\n /** Item gap. */\n gap: number;\n /** Column gap. */\n columnGap: number;\n /** Fixed item size. */\n fixedSize: number | null;\n /** Binary search for row index. */\n findLowerBoundY: (offset: number) => number;\n /** Binary search for row index (horizontal). */\n findLowerBoundX: (offset: number) => number;\n /** Prefix sum for row height. */\n queryY: (index: number) => number;\n /** Prefix sum for row width. */\n queryX: (index: number) => number;\n}\n\n/** Parameters for calculating the visible range of columns in grid mode. */\nexport interface ColumnRangeParams {\n /** Column count. */\n columnCount: number;\n /** Relative horizontal scroll position. */\n relativeScrollX: number;\n /** Usable viewport width. */\n usableWidth: number;\n /** Column buffer count. */\n colBuffer: number;\n /** Fixed column width. */\n fixedWidth: number | null;\n /** Column gap. */\n columnGap: number;\n /** Binary search for column index. */\n findLowerBound: (offset: number) => number;\n /** Prefix sum for column width. */\n query: (index: number) => number;\n /** Resolver for total column width. */\n totalColsQuery: () => number;\n}\n\n/** Parameters for calculating sticky item offsets. */\nexport interface StickyParams {\n /** Item index. */\n index: number;\n /** Is sticky configured. */\n isSticky: boolean;\n /** Scroll direction. */\n direction: ScrollDirection;\n /** Relative horizontal scroll. */\n relativeScrollX: number;\n /** Relative vertical scroll. */\n relativeScrollY: number;\n /** Original X offset. */\n originalX: number;\n /** Original Y offset. */\n originalY: number;\n /** Current width. */\n width: number;\n /** Current height. */\n height: number;\n /** All sticky indices. */\n stickyIndices: number[];\n /** Fixed item size. */\n fixedSize: number | null;\n /** Fixed column width. */\n fixedWidth: number | null;\n /** Item gap. */\n gap: number;\n /** Column gap. */\n columnGap: number;\n /** Prefix sum resolver for rows. */\n getItemQueryY: (index: number) => number;\n /** Prefix sum resolver for rows (horizontal). */\n getItemQueryX: (index: number) => number;\n}\n\n/** Parameters for calculating an item's position and size. */\nexport interface ItemPositionParams {\n /** Item index. */\n index: number;\n /** Scroll direction. */\n direction: ScrollDirection;\n /** Fixed item size. */\n fixedSize: number | null;\n /** Item gap. */\n gap: number;\n /** Column gap. */\n columnGap: number;\n /** Usable viewport width. */\n usableWidth: number;\n /** Usable viewport height. */\n usableHeight: number;\n /** Total estimated width. */\n totalWidth: number;\n /** Prefix sum for row height. */\n queryY: (idx: number) => number;\n /** Prefix sum for row width. */\n queryX: (idx: number) => number;\n /** Height resolver. */\n getSizeY: (idx: number) => number;\n /** Width resolver. */\n getSizeX: (idx: number) => number;\n /** Current column range (for grid mode). */\n columnRange?: ColumnRange | undefined;\n}\n\n/** Parameters for calculating an item's style object. */\nexport interface ItemStyleParams<T = unknown> {\n /** The rendered item state. */\n item: RenderedItem<T>;\n /** Scroll direction. */\n direction: ScrollDirection;\n /** Configured item size logic. */\n itemSize: number | ((item: T, index: number) => number) | null | undefined;\n /** Parent container tag. */\n containerTag: string;\n /** Padding start on X axis. */\n paddingStartX: number;\n /** Padding start on Y axis. */\n paddingStartY: number;\n /** Hydration state. */\n isHydrated: boolean;\n /** Whether the container is in Right-to-Left (RTL) mode. */\n isRtl: boolean;\n}\n\n/** Parameters for calculating the total size of the scrollable area. */\nexport interface TotalSizeParams {\n /** The scroll direction. */\n direction: ScrollDirection;\n /** The number of items in the list. */\n itemsLength: number;\n /** The number of columns (for grid mode). */\n columnCount: number;\n /** The fixed size of items, if applicable. */\n fixedSize: number | null;\n /** The fixed width of columns, if applicable. */\n fixedWidth: number | null;\n /** The gap between items. */\n gap: number;\n /** The gap between columns. */\n columnGap: number;\n /** Usable viewport width. */\n usableWidth: number;\n /** Usable viewport height. */\n usableHeight: number;\n /** Function to query the prefix sum of item heights. */\n queryY: (index: number) => number;\n /** Function to query the prefix sum of item widths. */\n queryX: (index: number) => number;\n /** Function to query the prefix sum of column widths. */\n queryColumn: (index: number) => number;\n}\n","/**\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","/**\n * Utilities for scroll management and element type detection.\n * Provides helper functions for checking Window and Body elements,\n * and a universal scrollTo function.\n */\n\nimport type { ScrollDirection, ScrollToIndexOptions } from '../types';\n\n/* global ScrollToOptions */\n\n/**\n * Maximum size (in pixels) for an element that most browsers can handle reliably.\n * Beyond this size, we use scaling for the scrollable area.\n * @default 10000000\n */\nexport const BROWSER_MAX_SIZE = 10000000;\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 || container === document.documentElement || (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 != null && 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 != null && '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 != null && 'scrollLeft' in target;\n}\n\n/**\n * Universal scroll function that handles both Window and HTMLElements.\n *\n * @param container - The container to scroll.\n * @param options - Scroll options.\n */\nexport function scrollTo(container: HTMLElement | Window | null | undefined, options: ScrollToOptions) {\n if (isWindow(container)) {\n window.scrollTo(options);\n } else if (container != null && isScrollableElement(container)) {\n if (typeof container.scrollTo === 'function') {\n container.scrollTo(options);\n } else {\n if (options.left !== undefined) {\n container.scrollLeft = options.left;\n }\n if (options.top !== undefined) {\n container.scrollTop = options.top;\n }\n }\n }\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 { BROWSER_MAX_SIZE, isScrollToIndexOptions } from './scroll';\n\n// --- Internal Helper Types ---\n\ninterface GenericRangeParams {\n scrollPos: number;\n containerSize: number;\n count: number;\n bufferBefore: number;\n bufferAfter: number;\n gap: number;\n fixedSize: number | null;\n findLowerBound: (offset: number) => number;\n query: (index: number) => number;\n}\n\ninterface AxisAlignmentParams {\n align: ScrollAlignment;\n targetPos: number;\n itemSize: number;\n scrollPos: number;\n viewSize: number;\n stickyOffsetStart: number;\n stickyOffsetEnd: number;\n}\n\n// --- Internal Helpers ---\n\n/**\n * Generic range calculation for a single axis (row or column).\n *\n * @param params - Range parameters.\n * @param params.scrollPos - Virtual scroll position.\n * @param params.containerSize - Usable viewport size.\n * @param params.count - Total item count.\n * @param params.bufferBefore - Buffer items before.\n * @param params.bufferAfter - Buffer items after.\n * @param params.gap - Item gap.\n * @param params.fixedSize - Fixed item size.\n * @param params.findLowerBound - Binary search for index.\n * @param params.query - Prefix sum for index.\n * @returns Start and end indices.\n */\nfunction calculateGenericRange({\n scrollPos,\n containerSize,\n count,\n bufferBefore,\n bufferAfter,\n gap,\n fixedSize,\n findLowerBound,\n query,\n}: GenericRangeParams) {\n let start = 0;\n let end = count;\n const endOffset = scrollPos + containerSize;\n\n if (fixedSize !== null) {\n const step = fixedSize + gap;\n start = Math.floor(scrollPos / step);\n end = Math.ceil(endOffset / step);\n } else {\n start = findLowerBound(scrollPos);\n end = findLowerBound(endOffset);\n if (end < count && query(end) < endOffset) {\n end++;\n }\n }\n\n return {\n start: Math.max(0, start - bufferBefore),\n end: Math.min(count, end + bufferAfter),\n };\n}\n\n/**\n * Binary search for the next sticky index after the current index.\n *\n * @param stickyIndices - Sorted array of sticky indices.\n * @param index - Current index.\n * @returns Next sticky index or undefined.\n */\nfunction findNextStickyIndex(stickyIndices: number[], index: number): number | undefined {\n let low = 0;\n let high = stickyIndices.length - 1;\n let nextStickyIdx: number | undefined;\n\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 return nextStickyIdx;\n}\n\n/**\n * Binary search for the previous sticky index before the current index.\n *\n * @param stickyIndices - Sorted array of sticky indices.\n * @param index - Current index.\n * @returns Previous sticky index or undefined.\n */\nexport function findPrevStickyIndex(stickyIndices: number[], index: number): number | undefined {\n let low = 0;\n let high = stickyIndices.length - 1;\n let prevStickyIdx: number | undefined;\n\n while (low <= high) {\n const mid = (low + high) >>> 1;\n if (stickyIndices[ mid ]! < index) {\n prevStickyIdx = stickyIndices[ mid ];\n low = mid + 1;\n } else {\n high = mid - 1;\n }\n }\n return prevStickyIdx;\n}\n\n/**\n * Generic alignment calculation for a single axis.\n *\n * @param params - Alignment parameters.\n * @param params.align - Desired alignment.\n * @param params.targetPos - Virtual item position.\n * @param params.itemSize - Virtual item size.\n * @param params.scrollPos - Virtual scroll position.\n * @param params.viewSize - Full viewport size.\n * @param params.stickyOffsetStart - Dynamic sticky offset at start.\n * @param params.stickyOffsetEnd - Sticky offset at end.\n * @returns Target scroll position and effective alignment.\n */\nfunction calculateAxisAlignment({\n align,\n targetPos,\n itemSize,\n scrollPos,\n viewSize,\n stickyOffsetStart,\n stickyOffsetEnd,\n}: AxisAlignmentParams) {\n const targetStart = targetPos - stickyOffsetStart;\n const targetEnd = targetPos - (viewSize - stickyOffsetEnd - itemSize);\n\n if (align === 'start') {\n return { target: targetStart, effectiveAlign: 'start' as const };\n }\n if (align === 'center') {\n return {\n target: targetPos - stickyOffsetStart - (viewSize - stickyOffsetStart - stickyOffsetEnd - itemSize) / 2,\n effectiveAlign: 'center' as const,\n };\n }\n if (align === 'end') {\n return { target: targetEnd, effectiveAlign: 'end' as const };\n }\n\n if (isItemVisible(targetPos, itemSize, scrollPos, viewSize, stickyOffsetStart, stickyOffsetEnd)) {\n return { target: scrollPos, effectiveAlign: 'auto' as const };\n }\n\n const usableSize = viewSize - stickyOffsetStart - stickyOffsetEnd;\n\n if (itemSize <= usableSize) {\n return targetPos < scrollPos + stickyOffsetStart\n ? {\n target: targetStart,\n effectiveAlign: 'start' as const,\n }\n : {\n target: targetEnd,\n effectiveAlign: 'end' as const,\n };\n }\n\n return Math.abs(targetStart - scrollPos) < Math.abs(targetEnd - scrollPos)\n ? {\n target: targetStart,\n effectiveAlign: 'start' as const,\n }\n : {\n target: targetEnd,\n effectiveAlign: 'end' as const,\n };\n}\n\n/**\n * Helper to calculate total size for a single axis.\n *\n * @param count - Item count.\n * @param fixedSize - Fixed size if any.\n * @param gap - Gap size.\n * @param query - Prefix sum resolver.\n * @returns Total size.\n */\nfunction calculateAxisSize(\n count: number,\n fixedSize: number | null,\n gap: number,\n query: (index: number) => number,\n): number {\n if (count <= 0) {\n return 0;\n }\n if (fixedSize !== null) {\n return Math.max(0, count * (fixedSize + gap) - gap);\n }\n return Math.max(0, query(count) - gap);\n}\n\n/**\n * Helper to calculate target scroll position for a single axis.\n *\n * @param params - Axis target parameters.\n * @param params.index - Row/column index.\n * @param params.align - Desired alignment.\n * @param params.viewSize - Full viewport size.\n * @param params.scrollPos - Virtual scroll position.\n * @param params.fixedSize - Fixed item size.\n * @param params.gap - Item gap.\n * @param params.query - Prefix sum resolver.\n * @param params.getSize - Item size resolver.\n * @param params.stickyIndices - Sticky indices.\n * @param params.stickyStart - Sticky start element size.\n * @param params.stickyEnd - Sticky end element size.\n * @returns Target position, item size and effective alignment.\n */\nfunction calculateAxisTarget({\n index,\n align,\n viewSize,\n scrollPos,\n fixedSize,\n gap,\n query,\n getSize,\n stickyIndices,\n stickyStart,\n stickyEnd = 0,\n}: {\n index: number;\n align: ScrollAlignment;\n viewSize: number;\n scrollPos: number;\n fixedSize: number | null;\n gap: number;\n query: (idx: number) => number;\n getSize: (idx: number) => number;\n stickyIndices?: number[] | undefined;\n stickyStart: number;\n stickyEnd?: number;\n}) {\n let stickyOffsetStart = stickyStart;\n if (stickyIndices && stickyIndices.length > 0) {\n const activeStickyIdx = findPrevStickyIndex(stickyIndices, index);\n if (activeStickyIdx !== undefined) {\n stickyOffsetStart += calculateAxisSize(1, fixedSize, 0, () => getSize(activeStickyIdx));\n }\n }\n\n const itemPos = (fixedSize !== null ? index * (fixedSize + gap) : query(index));\n const itemSize = fixedSize !== null ? fixedSize : getSize(index) - gap;\n\n const { target, effectiveAlign } = calculateAxisAlignment({\n align,\n targetPos: itemPos,\n itemSize,\n scrollPos,\n viewSize,\n stickyOffsetStart,\n stickyOffsetEnd: stickyEnd,\n });\n\n return { target, itemSize, effectiveAlign };\n}\n\n/**\n * Helper to calculate sticky state for a single axis.\n *\n * @param scrollPos - Virtual scroll position.\n * @param originalPos - Original virtual item position.\n * @param size - Virtual item size.\n * @param index - Item index.\n * @param stickyIndices - All sticky indices.\n * @param getNextStickyPos - Resolver for the next sticky item's position.\n * @returns Sticky state for this axis.\n */\nfunction calculateAxisSticky(\n scrollPos: number,\n originalPos: number,\n size: number,\n index: number,\n stickyIndices: number[],\n getNextStickyPos: (idx: number) => number,\n) {\n if (scrollPos <= originalPos) {\n return { isActive: false, offset: 0 };\n }\n\n const nextStickyIdx = findNextStickyIndex(stickyIndices, index);\n if (nextStickyIdx === undefined) {\n return { isActive: true, offset: 0 };\n }\n\n const nextStickyPos = getNextStickyPos(nextStickyIdx);\n if (scrollPos >= nextStickyPos) {\n return { isActive: false, offset: 0 };\n }\n\n return {\n isActive: true,\n offset: Math.max(0, Math.min(size, nextStickyPos - scrollPos)) - size,\n };\n}\n\n// --- Exported Functions ---\n\n/**\n * Determines if an item is visible within the usable viewport.\n *\n * @param itemPos - Virtual start position of the item (VU).\n * @param itemSize - Virtual size of the item (VU).\n * @param scrollPos - Virtual scroll position (VU).\n * @param viewSize - Full size of the viewport (VU).\n * @param stickyOffsetStart - Dynamic offset from sticky items at start (VU).\n * @param stickyOffsetEnd - Offset from sticky items at end (VU).\n * @returns True if visible.\n */\nexport function isItemVisible(\n itemPos: number,\n itemSize: number,\n scrollPos: number,\n viewSize: number,\n stickyOffsetStart: number = 0,\n stickyOffsetEnd: number = 0,\n): boolean {\n const usableStart = scrollPos + stickyOffsetStart;\n const usableEnd = scrollPos + viewSize - stickyOffsetEnd;\n const usableSize = viewSize - stickyOffsetStart - stickyOffsetEnd;\n\n if (itemSize <= usableSize) {\n return itemPos >= usableStart - 0.5 && (itemPos + itemSize) <= usableEnd + 0.5;\n }\n return itemPos <= usableStart + 0.5 && (itemPos + itemSize) >= usableEnd - 0.5;\n}\n\n/**\n * Maps a display scroll position to a virtual content position.\n *\n * @param displayPos - Display pixel position (DU).\n * @param hostOffset - Offset of the host element in display pixels (DU).\n * @param scale - Coordinate scaling factor (VU/DU).\n * @returns Virtual content position (VU).\n */\nexport function displayToVirtual(displayPos: number, hostOffset: number, scale: number): number {\n return (displayPos - hostOffset) * scale;\n}\n\n/**\n * Maps a virtual content position to a display scroll position.\n *\n * @param virtualPos - Virtual content position (VU).\n * @param hostOffset - Offset of the host element in display pixels (DU).\n * @param scale - Coordinate scaling factor (VU/DU).\n * @returns Display pixel position (DU).\n */\nexport function virtualToDisplay(virtualPos: number, hostOffset: number, scale: number): number {\n return virtualPos / scale + hostOffset;\n}\n\n/**\n * Calculates the target scroll position (relative to content) for a given row/column index and alignment.\n *\n * @param params - Scroll target parameters.\n * @param params.rowIndex - Row index to target.\n * @param params.colIndex - Column index to target.\n * @param params.options - Scroll options including alignment.\n * @param params.direction - Current scroll direction.\n * @param params.viewportWidth - Full viewport width (DU).\n * @param params.viewportHeight - Full viewport height (DU).\n * @param params.totalWidth - Total estimated width (VU).\n * @param params.totalHeight - Total estimated height (VU).\n * @param params.gap - Item gap (VU).\n * @param params.columnGap - Column gap (VU).\n * @param params.fixedSize - Fixed item size (VU).\n * @param params.fixedWidth - Fixed column width (VU).\n * @param params.relativeScrollX - Current relative X scroll (VU).\n * @param params.relativeScrollY - Current relative Y scroll (VU).\n * @param params.getItemSizeY - Resolver for item height (VU).\n * @param params.getItemSizeX - Resolver for item width (VU).\n * @param params.getItemQueryY - Prefix sum resolver for item height (VU).\n * @param params.getItemQueryX - Prefix sum resolver for item width (VU).\n * @param params.getColumnSize - Resolver for column size (VU).\n * @param params.getColumnQuery - Prefix sum resolver for column width (VU).\n * @param params.scaleX - Coordinate scaling factor for X axis.\n * @param params.scaleY - Coordinate scaling factor for Y axis.\n * @param params.hostOffsetX - Display pixels offset of items wrapper on X axis (DU).\n * @param params.hostOffsetY - Display pixels offset of items wrapper on Y axis (DU).\n * @param params.flowPaddingStartX - Display pixels padding at flow start on X axis (DU).\n * @param params.flowPaddingStartY - Display pixels padding at flow start on Y axis (DU).\n * @param params.paddingStartX - Display pixels padding at scroll start on X axis (DU).\n * @param params.paddingStartY - Display pixels padding at scroll start on Y axis (DU).\n * @param params.paddingEndX - Display pixels padding at scroll end on X axis (DU).\n * @param params.paddingEndY - Display pixels padding at scroll end on Y axis (DU).\n * @param params.stickyIndices - List of sticky indices.\n * @param params.stickyStartX - Sticky start offset on X axis (DU).\n * @param params.stickyStartY - Sticky start offset on Y axis (DU).\n * @param params.stickyEndX - Sticky end offset on X axis (DU).\n * @param params.stickyEndY - Sticky end offset on Y axis (DU).\n * @returns The target X and Y positions (VU) and item dimensions (VU).\n * @see ScrollTargetParams\n * @see ScrollTargetResult\n */\nexport function calculateScrollTarget({\n rowIndex,\n colIndex,\n options,\n direction,\n viewportWidth,\n viewportHeight,\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 scaleX,\n scaleY,\n hostOffsetX,\n hostOffsetY,\n stickyIndices,\n stickyStartX = 0,\n stickyStartY = 0,\n stickyEndX = 0,\n stickyEndY = 0,\n flowPaddingStartX = 0,\n flowPaddingStartY = 0,\n paddingStartX = 0,\n paddingStartY = 0,\n paddingEndX = 0,\n paddingEndY = 0,\n}: ScrollTargetParams): ScrollTargetResult {\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 = (align && typeof align === 'object' ? align.x : align) || 'auto';\n const alignY = (align && typeof align === 'object' ? align.y : align) || 'auto';\n\n let targetX = relativeScrollX;\n let targetY = relativeScrollY;\n let itemWidth = 0;\n let itemHeight = 0;\n let effectiveAlignX: ScrollAlignment = 'auto';\n let effectiveAlignY: ScrollAlignment = 'auto';\n\n // Clamp to valid range\n const rWidth = scaleX === 1 ? totalWidth : BROWSER_MAX_SIZE;\n const rHeight = scaleY === 1 ? totalHeight : BROWSER_MAX_SIZE;\n\n const maxDisplayX = Math.max(0, hostOffsetX + rWidth - viewportWidth);\n const maxDisplayY = Math.max(0, hostOffsetY + rHeight - viewportHeight);\n\n // maxTarget should be in virtual internalScroll coordinates\n const maxTargetX = (maxDisplayX - hostOffsetX) * scaleX;\n const maxTargetY = (maxDisplayY - hostOffsetY) * scaleY;\n\n const itemsStartVirtualX = flowPaddingStartX + stickyStartX + paddingStartX;\n const itemsStartVirtualY = flowPaddingStartY + stickyStartY + paddingStartY;\n\n // Y calculation\n if (rowIndex != null) {\n const res = calculateAxisTarget({\n index: rowIndex,\n align: alignY as ScrollAlignment,\n viewSize: viewportHeight,\n scrollPos: relativeScrollY,\n fixedSize,\n gap,\n query: getItemQueryY,\n getSize: getItemSizeY,\n stickyIndices,\n stickyStart: stickyStartY + paddingStartY,\n stickyEnd: stickyEndY + paddingEndY,\n });\n targetY = res.target + itemsStartVirtualY;\n itemHeight = res.itemSize;\n effectiveAlignY = res.effectiveAlign;\n }\n\n // X calculation\n if (colIndex != null) {\n const isGrid = direction === 'both';\n const isHorizontal = direction === 'horizontal';\n const res = calculateAxisTarget({\n index: colIndex,\n align: alignX as ScrollAlignment,\n viewSize: viewportWidth,\n scrollPos: relativeScrollX,\n fixedSize: isGrid ? fixedWidth : fixedSize,\n gap: (isGrid || isHorizontal) ? columnGap : gap,\n query: isGrid ? getColumnQuery : getItemQueryX,\n getSize: isGrid ? getColumnSize : getItemSizeX,\n stickyIndices,\n stickyStart: stickyStartX + paddingStartX,\n stickyEnd: stickyEndX + paddingEndX,\n });\n targetX = res.target + itemsStartVirtualX;\n itemWidth = res.itemSize;\n effectiveAlignX = res.effectiveAlign;\n }\n\n targetX = Math.max(0, Math.min(targetX, maxTargetX));\n targetY = Math.max(0, Math.min(targetY, maxTargetY));\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 - Range parameters.\n * @param params.direction - Scroll direction.\n * @param params.relativeScrollX - Virtual horizontal position (VU).\n * @param params.relativeScrollY - Virtual vertical position (VU).\n * @param params.usableWidth - Usable viewport width (VU).\n * @param params.usableHeight - Usable viewport height (VU).\n * @param params.itemsLength - Total item count.\n * @param params.bufferBefore - Buffer items before.\n * @param params.bufferAfter - Buffer items after.\n * @param params.gap - Item gap (VU).\n * @param params.columnGap - Column gap (VU).\n * @param params.fixedSize - Fixed item size (VU).\n * @param params.findLowerBoundY - Resolver for vertical index.\n * @param params.findLowerBoundX - Resolver for horizontal index.\n * @param params.queryY - Resolver for vertical offset (VU).\n * @param params.queryX - Resolver for horizontal offset (VU).\n * @returns The start and end indices of the items to render.\n * @see RangeParams\n */\nexport function calculateRange({\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}: RangeParams) {\n const isVertical = direction === 'vertical' || direction === 'both';\n\n return calculateGenericRange({\n scrollPos: isVertical ? relativeScrollY : relativeScrollX,\n containerSize: isVertical ? usableHeight : usableWidth,\n count: itemsLength,\n bufferBefore,\n bufferAfter,\n gap: isVertical ? gap : columnGap,\n fixedSize,\n findLowerBound: isVertical ? findLowerBoundY : findLowerBoundX,\n query: isVertical ? queryY : queryX,\n });\n}\n\n/**\n * Calculates the range of columns to render for bidirectional scroll.\n *\n * @param params - Column range parameters.\n * @param params.columnCount - Total column count.\n * @param params.relativeScrollX - Virtual horizontal position (VU).\n * @param params.usableWidth - Usable viewport width (VU).\n * @param params.colBuffer - Column buffer size.\n * @param params.fixedWidth - Fixed column width (VU).\n * @param params.columnGap - Column gap (VU).\n * @param params.findLowerBound - Resolver for column index.\n * @param params.query - Resolver for column offset (VU).\n * @param params.totalColsQuery - Resolver for total width (VU).\n * @returns The start and end indices and paddings for columns (VU).\n * @see ColumnRangeParams\n * @see ColumnRange\n */\nexport function calculateColumnRange({\n columnCount,\n relativeScrollX,\n usableWidth,\n colBuffer,\n fixedWidth,\n columnGap,\n findLowerBound,\n query,\n totalColsQuery,\n}: ColumnRangeParams) {\n if (!columnCount) {\n return { start: 0, end: 0, padStart: 0, padEnd: 0 };\n }\n\n const { start, end } = calculateGenericRange({\n scrollPos: relativeScrollX,\n containerSize: usableWidth,\n count: columnCount,\n bufferBefore: colBuffer,\n bufferAfter: colBuffer,\n gap: columnGap,\n fixedSize: fixedWidth,\n findLowerBound,\n query,\n });\n\n const safeStart = start;\n const safeEnd = end;\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 contentEnd = fixedWidth !== null\n ? (safeEnd * (fixedWidth + columnGap) - (safeEnd > 0 ? columnGap : 0))\n : (query(safeEnd) - (safeEnd > 0 ? columnGap : 0));\n\n return {\n start: safeStart,\n end: safeEnd,\n padStart,\n padEnd: Math.max(0, totalWidth - contentEnd),\n };\n}\n\n/**\n * Calculates the sticky state and offset for a single item.\n *\n * @param params - Sticky item parameters.\n * @param params.index - Item index.\n * @param params.isSticky - If configured as sticky.\n * @param params.direction - Scroll direction.\n * @param params.relativeScrollX - Virtual horizontal position (VU).\n * @param params.relativeScrollY - Virtual vertical position (VU).\n * @param params.originalX - Virtual original X position (VU).\n * @param params.originalY - Virtual original Y position (VU).\n * @param params.width - Virtual item width (VU).\n * @param params.height - Virtual item height (VU).\n * @param params.stickyIndices - All sticky indices.\n * @param params.fixedSize - Fixed item size (VU).\n * @param params.gap - Item gap (VU).\n * @param params.columnGap - Column gap (VU).\n * @param params.getItemQueryY - Resolver for vertical offset (VU).\n * @param params.getItemQueryX - Resolver for horizontal offset (VU).\n * @returns Sticky state and offset (VU).\n * @see StickyParams\n */\nexport function calculateStickyItem({\n index,\n isSticky,\n direction,\n relativeScrollX,\n relativeScrollY,\n originalX,\n originalY,\n width,\n height,\n stickyIndices,\n fixedSize,\n gap,\n columnGap,\n getItemQueryY,\n getItemQueryX,\n}: StickyParams) {\n let isStickyActiveX = false;\n let isStickyActiveY = false;\n const stickyOffset = { x: 0, y: 0 };\n\n if (!isSticky) {\n return { isStickyActiveX, isStickyActiveY, isStickyActive: false, stickyOffset };\n }\n\n // Y Axis (Sticky Rows)\n if (direction === 'vertical' || direction === 'both') {\n const res = calculateAxisSticky(\n relativeScrollY,\n originalY,\n height,\n index,\n stickyIndices,\n (nextIdx) => (fixedSize !== null ? nextIdx * (fixedSize + gap) : getItemQueryY(nextIdx)),\n );\n isStickyActiveY = res.isActive;\n stickyOffset.y = res.offset;\n }\n\n // X Axis (Sticky Columns / Items)\n if (direction === 'horizontal') {\n const res = calculateAxisSticky(\n relativeScrollX,\n originalX,\n width,\n index,\n stickyIndices,\n (nextIdx) => (fixedSize !== null ? nextIdx * (fixedSize + columnGap) : getItemQueryX(nextIdx)),\n );\n\n if (res.isActive) {\n isStickyActiveX = true;\n stickyOffset.x = res.offset;\n }\n }\n\n return {\n isStickyActiveX,\n isStickyActiveY,\n isStickyActive: isStickyActiveX || isStickyActiveY,\n stickyOffset,\n };\n}\n\n/**\n * Calculates the position and size of a single item.\n *\n * @param params - Item position parameters.\n * @param params.index - Item index.\n * @param params.direction - Scroll direction.\n * @param params.fixedSize - Fixed item size (VU).\n * @param params.gap - Item gap (VU).\n * @param params.columnGap - Column gap (VU).\n * @param params.usableWidth - Usable viewport width (VU).\n * @param params.usableHeight - Usable viewport height (VU).\n * @param params.totalWidth - Total estimated width (VU).\n * @param params.queryY - Resolver for vertical offset (VU).\n * @param params.queryX - Resolver for horizontal offset (VU).\n * @param params.getSizeY - Resolver for height (VU).\n * @param params.getSizeX - Resolver for width (VU).\n * @param params.columnRange - Current column range (for grid mode).\n * @returns Item position and size (VU).\n * @see ItemPositionParams\n */\nexport function calculateItemPosition({\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 columnRange,\n}: ItemPositionParams) {\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 if (direction === 'both' && columnRange) {\n y = fixedSize !== null ? index * (fixedSize + gap) : queryY(index);\n height = fixedSize !== null ? fixedSize : getSizeY(index) - gap;\n x = columnRange.padStart;\n width = Math.max(0, totalWidth - columnRange.padStart - columnRange.padEnd);\n } else {\n y = 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 - Item style parameters.\n * @param params.item - Rendered item state.\n * @param params.direction - Scroll direction.\n * @param params.itemSize - Virtual item size (VU).\n * @param params.containerTag - Container HTML tag.\n * @param params.paddingStartX - Horizontal virtual padding (DU).\n * @param params.paddingStartY - Vertical virtual padding (DU).\n * @param params.isHydrated - If mounted and hydrated.\n * @param params.isRtl - If in RTL mode.\n * @returns Style object.\n * @see ItemStyleParams\n */\nexport function calculateItemStyle<T = unknown>({\n item,\n direction,\n itemSize,\n containerTag,\n paddingStartX,\n paddingStartY,\n isHydrated,\n isRtl,\n}: ItemStyleParams<T>) {\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 const isStickingVertically = item.isStickyActiveY ?? (item.isStickyActive && (isVertical || isBoth));\n const isStickingHorizontally = item.isStickyActiveX ?? (item.isStickyActive && isHorizontal);\n\n const tx = isRtl\n ? -(isStickingHorizontally ? item.stickyOffset.x : item.offset.x)\n : (isStickingHorizontally ? item.stickyOffset.x : item.offset.x);\n const ty = isStickingVertically ? item.stickyOffset.y : item.offset.y;\n\n if (item.isStickyActive || item.isStickyActiveX || item.isStickyActiveY) {\n style.insetBlockStart = isStickingVertically ? `${ paddingStartY }px` : 'auto';\n style.insetInlineStart = isStickingHorizontally ? `${ paddingStartX }px` : 'auto';\n style.transform = `translate(${ tx }px, ${ ty }px)`;\n } else {\n style.transform = `translate(${ tx }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 - Total size parameters.\n * @param params.direction - Scroll direction.\n * @param params.itemsLength - Total item count.\n * @param params.columnCount - Column count.\n * @param params.fixedSize - Fixed item size (VU).\n * @param params.fixedWidth - Fixed column width (VU).\n * @param params.gap - Item gap (VU).\n * @param params.columnGap - Column gap (VU).\n * @param params.usableWidth - Usable viewport width (VU).\n * @param params.usableHeight - Usable viewport height (VU).\n * @param params.queryY - Resolver for vertical offset (VU).\n * @param params.queryX - Resolver for horizontal offset (VU).\n * @param params.queryColumn - Resolver for column offset (VU).\n * @returns Total width and height (VU).\n * @see TotalSizeParams\n */\nexport function calculateTotalSize({\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}: TotalSizeParams) {\n const isBoth = direction === 'both';\n const isHorizontal = direction === 'horizontal';\n\n let width = 0;\n let height = 0;\n\n if (isBoth) {\n width = calculateAxisSize(columnCount, fixedWidth, columnGap, queryColumn);\n height = calculateAxisSize(itemsLength, fixedSize, gap, queryY);\n } else if (isHorizontal) {\n width = calculateAxisSize(itemsLength, fixedSize, columnGap, queryX);\n height = usableHeight;\n } else {\n width = usableWidth;\n height = calculateAxisSize(itemsLength, fixedSize, gap, queryY);\n }\n\n return {\n width: isBoth ? Math.max(width, usableWidth) : width,\n height: isBoth ? Math.max(height, usableHeight) : height,\n };\n}\n","import type {\n RenderedItem,\n ScrollAlignment,\n ScrollAlignmentOptions,\n ScrollDetails,\n ScrollDirection,\n ScrollToIndexOptions,\n VirtualScrollProps,\n} from '../types';\nimport type { MaybeRefOrGetter } from 'vue';\n\n/* global ScrollToOptions */\nimport { computed, getCurrentInstance, nextTick, onMounted, onUnmounted, reactive, ref, toValue, watch } from 'vue';\n\nimport {\n DEFAULT_BUFFER,\n DEFAULT_COLUMN_WIDTH,\n DEFAULT_ITEM_SIZE,\n} from '../types';\nimport { FenwickTree } from '../utils/fenwick-tree';\nimport { BROWSER_MAX_SIZE, getPaddingX, getPaddingY, isElement, isScrollableElement, isScrollToIndexOptions, isWindowLike, scrollTo } from '../utils/scroll';\nimport {\n calculateColumnRange,\n calculateItemPosition,\n calculateRange,\n calculateScrollTarget,\n calculateStickyItem,\n calculateTotalSize,\n displayToVirtual,\n findPrevStickyIndex,\n virtualToDisplay,\n} from '../utils/virtual-scroll-logic';\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 propsInput - The configuration properties. Can be a plain object, a Ref, or a getter function.\n * @see VirtualScrollProps\n */\nexport function useVirtualScroll<T = unknown>(propsInput: MaybeRefOrGetter<VirtualScrollProps<T>>) {\n const props = computed(() => toValue(propsInput));\n\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 isRtl = ref(false);\n const viewportWidth = ref(0);\n const viewportHeight = ref(0);\n const hostOffset = reactive({ x: 0, y: 0 });\n const hostRefOffset = reactive({ x: 0, y: 0 });\n let scrollTimeout: ReturnType<typeof setTimeout> | undefined;\n\n const isProgrammaticScroll = ref(false);\n const internalScrollX = ref(0);\n const internalScrollY = ref(0);\n\n let computedStyle: CSSStyleDeclaration | null = null;\n\n /**\n * Detects the current direction (LTR/RTL) of the scroll container.\n */\n const updateDirection = () => {\n if (typeof window === 'undefined') {\n return;\n }\n const container = props.value.container || props.value.hostRef || window;\n const el = isElement(container) ? container : document.documentElement;\n\n if (!computedStyle || !('direction' in computedStyle)) {\n computedStyle = window.getComputedStyle(el);\n }\n\n const newRtl = computedStyle.direction === 'rtl';\n if (isRtl.value !== newRtl) {\n isRtl.value = newRtl;\n }\n };\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 direction = computed(() => [ 'vertical', 'horizontal', 'both' ].includes(props.value.direction as string) ? props.value.direction as ScrollDirection : 'vertical' as ScrollDirection);\n\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 stickyStartX = computed(() => getPaddingX(props.value.stickyStart, props.value.direction));\n const stickyEndX = computed(() => getPaddingX(props.value.stickyEnd, props.value.direction));\n const stickyStartY = computed(() => getPaddingY(props.value.stickyStart, props.value.direction));\n const stickyEndY = computed(() => getPaddingY(props.value.stickyEnd, props.value.direction));\n\n const flowStartX = computed(() => getPaddingX(props.value.flowPaddingStart, props.value.direction));\n const flowEndX = computed(() => getPaddingX(props.value.flowPaddingEnd, props.value.direction));\n const flowStartY = computed(() => getPaddingY(props.value.flowPaddingStart, props.value.direction));\n const flowEndY = computed(() => getPaddingY(props.value.flowPaddingEnd, props.value.direction));\n\n const usableWidth = computed(() => viewportWidth.value - (direction.value !== 'vertical' ? (stickyStartX.value + stickyEndX.value) : 0));\n\n const usableHeight = computed(() => viewportHeight.value - (direction.value !== 'horizontal' ? (stickyStartY.value + stickyEndY.value) : 0));\n\n // --- Size Calculations ---\n /**\n * Total size (width and height) of all items in the scrollable area.\n */\n const totalSize = 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 const gap = props.value.gap || 0;\n const columnGap = props.value.columnGap || 0;\n\n let width = 0;\n let height = 0;\n\n if (direction.value === 'both') {\n if (colCount > 0) {\n const effectiveColEnd = colEnd || colCount;\n const total = columnSizes.query(effectiveColEnd) - columnSizes.query(colStart);\n width = Math.max(0, total - (effectiveColEnd > colStart ? columnGap : 0));\n }\n if (fixedItemSize.value !== null) {\n const len = end - start;\n height = Math.max(0, len * (fixedItemSize.value + gap) - (len > 0 ? gap : 0));\n } else {\n const total = itemSizesY.query(end) - itemSizesY.query(start);\n height = Math.max(0, total - (end > start ? gap : 0));\n }\n } else if (direction.value === 'horizontal') {\n if (fixedItemSize.value !== null) {\n const len = end - start;\n width = Math.max(0, len * (fixedItemSize.value + columnGap) - (len > 0 ? columnGap : 0));\n } else {\n const total = itemSizesX.query(end) - itemSizesX.query(start);\n width = Math.max(0, total - (end > start ? columnGap : 0));\n }\n height = usableHeight.value;\n } else {\n // vertical\n width = usableWidth.value;\n if (fixedItemSize.value !== null) {\n const len = end - start;\n height = Math.max(0, len * (fixedItemSize.value + gap) - (len > 0 ? gap : 0));\n } else {\n const total = itemSizesY.query(end) - itemSizesY.query(start);\n height = Math.max(0, total - (end > start ? gap : 0));\n }\n }\n\n return {\n width: Math.max(width, usableWidth.value),\n height: Math.max(height, usableHeight.value),\n };\n }\n\n return calculateTotalSize({\n direction: direction.value,\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 });\n });\n\n const isWindowContainer = computed(() => isWindowLike(props.value.container));\n\n const virtualWidth = computed(() => totalSize.value.width + paddingStartX.value + paddingEndX.value);\n const virtualHeight = computed(() => totalSize.value.height + paddingStartY.value + paddingEndY.value);\n\n const totalWidth = computed(() => (flowStartX.value + stickyStartX.value + stickyEndX.value + flowEndX.value + virtualWidth.value));\n\n const totalHeight = computed(() => (flowStartY.value + stickyStartY.value + stickyEndY.value + flowEndY.value + virtualHeight.value));\n\n const componentOffset = reactive({\n x: computed(() => Math.max(0, hostOffset.x - (flowStartX.value + stickyStartX.value))),\n y: computed(() => Math.max(0, hostOffset.y - (flowStartY.value + stickyStartY.value))),\n });\n\n const renderedWidth = computed(() => (isWindowContainer.value ? totalWidth.value : Math.min(totalWidth.value, BROWSER_MAX_SIZE)));\n const renderedHeight = computed(() => (isWindowContainer.value ? totalHeight.value : Math.min(totalHeight.value, BROWSER_MAX_SIZE)));\n\n const renderedVirtualWidth = computed(() => (isWindowContainer.value ? virtualWidth.value : Math.max(0, renderedWidth.value - (flowStartX.value + stickyStartX.value + stickyEndX.value + flowEndX.value))));\n const renderedVirtualHeight = computed(() => (isWindowContainer.value ? virtualHeight.value : Math.max(0, renderedHeight.value - (flowStartY.value + stickyStartY.value + stickyEndY.value + flowEndY.value))));\n\n const scaleX = computed(() => {\n if (isWindowContainer.value || totalWidth.value <= BROWSER_MAX_SIZE) {\n return 1;\n }\n const realRange = totalWidth.value - viewportWidth.value;\n const displayRange = renderedWidth.value - viewportWidth.value;\n return displayRange > 0 ? realRange / displayRange : 1;\n });\n\n const scaleY = computed(() => {\n if (isWindowContainer.value || totalHeight.value <= BROWSER_MAX_SIZE) {\n return 1;\n }\n const realRange = totalHeight.value - viewportHeight.value;\n const displayRange = renderedHeight.value - viewportHeight.value;\n return displayRange > 0 ? realRange / displayRange : 1;\n });\n\n const relativeScrollX = computed(() => {\n if (direction.value === 'vertical') {\n return 0;\n }\n const flowPaddingX = flowStartX.value + stickyStartX.value + paddingStartX.value;\n return internalScrollX.value - flowPaddingX;\n });\n\n const relativeScrollY = computed(() => {\n if (direction.value === 'horizontal') {\n return 0;\n }\n const flowPaddingY = flowStartY.value + stickyStartY.value + paddingStartY.value;\n return internalScrollY.value - flowPaddingY;\n });\n\n /**\n * Returns the currently calculated width for a specific column index, taking measurements and gaps into account.\n *\n * @param index - The column index.\n * @returns The width in pixels (excluding gap).\n */\n const getColumnWidth = (index: number) => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n const columnGap = props.value.columnGap || 0;\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 const val = columnSizes.get(index);\n return val > 0 ? val - columnGap : (props.value.defaultColumnWidth || DEFAULT_COLUMN_WIDTH);\n };\n\n /**\n * Returns the currently calculated height for a specific row index, taking measurements and gaps into account.\n *\n * @param index - The row index.\n * @returns The height in pixels (excluding gap).\n */\n const getRowHeight = (index: number) => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n if (direction.value === 'horizontal') {\n return usableHeight.value;\n }\n\n const gap = props.value.gap || 0;\n const itemSize = props.value.itemSize;\n if (typeof itemSize === 'number' && itemSize > 0) {\n return itemSize;\n }\n if (typeof itemSize === 'function') {\n const item = props.value.items[ index ];\n return item !== undefined ? itemSize(item, index) : (props.value.defaultItemSize || DEFAULT_ITEM_SIZE);\n }\n\n const val = itemSizesY.get(index);\n return val > 0 ? val - gap : (props.value.defaultItemSize || DEFAULT_ITEM_SIZE);\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 function 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 { targetX, targetY, effectiveAlignX, effectiveAlignY } = calculateScrollTarget({\n rowIndex,\n colIndex,\n options,\n direction: direction.value,\n viewportWidth: viewportWidth.value,\n viewportHeight: viewportHeight.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 scaleX: scaleX.value,\n scaleY: scaleY.value,\n hostOffsetX: componentOffset.x,\n hostOffsetY: componentOffset.y,\n stickyIndices: sortedStickyIndices.value,\n stickyStartX: stickyStartX.value,\n stickyStartY: stickyStartY.value,\n stickyEndX: stickyEndX.value,\n stickyEndY: stickyEndY.value,\n flowPaddingStartX: flowStartX.value,\n flowPaddingStartY: flowStartY.value,\n paddingStartX: paddingStartX.value,\n paddingStartY: paddingStartY.value,\n paddingEndX: paddingEndX.value,\n paddingEndY: paddingEndY.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 displayTargetX = virtualToDisplay(targetX, componentOffset.x, scaleX.value);\n const displayTargetY = virtualToDisplay(targetY, componentOffset.y, scaleY.value);\n\n const finalX = isRtl.value ? -displayTargetX : displayTargetX;\n const finalY = displayTargetY;\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 const scrollOptions: ScrollToOptions = {\n behavior: scrollBehavior,\n };\n if (colIndex !== null && colIndex !== undefined) {\n scrollOptions.left = isRtl.value ? finalX : Math.max(0, finalX);\n }\n if (rowIndex !== null && rowIndex !== undefined) {\n scrollOptions.top = Math.max(0, finalY);\n }\n\n scrollTo(container, scrollOptions);\n\n if (scrollBehavior === 'auto' || scrollBehavior === undefined) {\n if (colIndex !== null && colIndex !== undefined) {\n scrollX.value = (isRtl.value ? finalX : Math.max(0, finalX));\n internalScrollX.value = targetX;\n }\n if (rowIndex !== null && rowIndex !== undefined) {\n scrollY.value = Math.max(0, finalY);\n internalScrollY.value = targetY;\n }\n\n if (pendingScroll.value) {\n const currentOptions = pendingScroll.value.options;\n if (isScrollToIndexOptions(currentOptions)) {\n currentOptions.behavior = 'auto';\n } else {\n pendingScroll.value.options = {\n align: currentOptions as ScrollAlignment | ScrollAlignmentOptions,\n behavior: 'auto',\n };\n }\n }\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'). 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 pendingScroll.value = null;\n\n const clampedX = (x !== null && x !== undefined)\n ? Math.max(0, Math.min(x, totalWidth.value - viewportWidth.value))\n : null;\n const clampedY = (y !== null && y !== undefined)\n ? Math.max(0, Math.min(y, totalHeight.value - viewportHeight.value))\n : null;\n\n if (clampedX !== null) {\n internalScrollX.value = clampedX;\n }\n if (clampedY !== null) {\n internalScrollY.value = clampedY;\n }\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 displayTargetX = (clampedX !== null) ? virtualToDisplay(clampedX, componentOffset.x, scaleX.value) : null;\n const displayTargetY = (clampedY !== null) ? virtualToDisplay(clampedY, componentOffset.y, scaleY.value) : null;\n\n const targetX = (displayTargetX !== null)\n ? (isRtl.value ? -displayTargetX : displayTargetX)\n : currentX;\n const targetY = (displayTargetY !== null) ? displayTargetY : currentY;\n\n const scrollOptions: ScrollToOptions = {\n behavior: options?.behavior || 'auto',\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 scrollTo(container, scrollOptions);\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 resizeMeasurements = (len: number, colCount: number) => {\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\n const initializeMeasurements = () => {\n const newItems = props.value.items;\n const len = newItems.length;\n const colCount = props.value.columnCount || 0;\n const gap = props.value.gap || 0;\n const columnGap = props.value.columnGap || 0;\n const cw = props.value.columnWidth;\n\n let colNeedsRebuild = false;\n let itemsNeedRebuild = false;\n\n // Initialize columns\n if (colCount > 0) {\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 }\n\n // Initialize items\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 const isMeasuredX = measuredItemsX[ i ] === 1;\n const isMeasuredY = measuredItemsY[ i ] === 1;\n\n if (direction.value === 'horizontal') {\n if (!isDynamicItemSize.value || (!isMeasuredX && currentX === 0)) {\n const baseSize = typeof props.value.itemSize === 'function' ? props.value.itemSize(item as T, i) : defaultSize.value;\n const targetX = baseSize + columnGap;\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 if (direction.value !== 'horizontal') {\n if (!isDynamicItemSize.value || (!isMeasuredY && currentY === 0)) {\n const baseSize = typeof props.value.itemSize === 'function' ? props.value.itemSize(item as T, i) : defaultSize.value;\n const targetY = baseSize + gap;\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 (colNeedsRebuild) {\n columnSizes.rebuild();\n }\n if (itemsNeedRebuild) {\n itemSizesX.rebuild();\n itemSizesY.rebuild();\n }\n };\n\n const initializeSizes = () => {\n const newItems = props.value.items;\n const len = newItems.length;\n const colCount = props.value.columnCount || 0;\n\n resizeMeasurements(len, colCount);\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' ? props.value.itemSize(newItems[ i ] as T, i) : defaultSize.value;\n if (direction.value === 'horizontal') {\n addedX += size + columnGap;\n } else { addedY += size + gap; }\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', isCorrection: true } as ScrollToIndexOptions,\n );\n });\n }\n }\n\n initializeMeasurements();\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 (typeof window === 'undefined') {\n return;\n }\n const container = props.value.container || window;\n\n const calculateOffset = (el: HTMLElement) => {\n const rect = el.getBoundingClientRect();\n if (container === window) {\n return {\n x: isRtl.value\n ? document.documentElement.clientWidth - rect.right - window.scrollX\n : rect.left + window.scrollX,\n y: rect.top + window.scrollY,\n };\n }\n if (container === el) {\n return { x: 0, y: 0 };\n }\n if (isElement(container)) {\n const containerRect = container.getBoundingClientRect();\n return {\n x: isRtl.value\n ? containerRect.right - rect.right - container.scrollLeft\n : rect.left - containerRect.left + container.scrollLeft,\n y: rect.top - containerRect.top + container.scrollTop,\n };\n }\n return { x: 0, y: 0 };\n };\n\n if (props.value.hostElement) {\n const newOffset = calculateOffset(props.value.hostElement);\n if (Math.abs(hostOffset.x - newOffset.x) > 0.1 || Math.abs(hostOffset.y - newOffset.y) > 0.1) {\n hostOffset.x = newOffset.x;\n hostOffset.y = newOffset.y;\n }\n }\n\n if (props.value.hostRef) {\n const newOffset = calculateOffset(props.value.hostRef);\n if (Math.abs(hostRefOffset.x - newOffset.x) > 0.1 || Math.abs(hostRefOffset.y - newOffset.y) > 0.1) {\n hostRefOffset.x = newOffset.x;\n hostRefOffset.y = newOffset.y;\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 watch(isRtl, (newRtl, oldRtl) => {\n if (oldRtl === undefined || newRtl === oldRtl || !isMounted.value) {\n return;\n }\n\n // Use the oldRtl to correctly interpret the current scrollX\n if (direction.value === 'vertical') {\n updateHostOffset();\n return;\n }\n\n const scrollValue = oldRtl ? Math.abs(scrollX.value) : scrollX.value;\n const oldRelativeScrollX = displayToVirtual(scrollValue, hostOffset.x, scaleX.value);\n\n // Update host offset for the new direction\n updateHostOffset();\n\n // Maintain logical horizontal position when direction changes\n scrollToOffset(oldRelativeScrollX, null, { behavior: 'auto' });\n }, { flush: 'sync' });\n\n watch([ scaleX, scaleY ], () => {\n if (!isMounted.value || isScrolling.value || isProgrammaticScroll.value) {\n return;\n }\n // Sync display scroll to maintain logical position\n scrollToOffset(internalScrollX.value, internalScrollY.value, { behavior: 'auto' });\n });\n\n watch([ () => props.value.items.length, () => props.value.columnCount ], ([ newLen, newColCount ], [ oldLen, oldColCount ]) => {\n nextTick(() => {\n const maxRelX = Math.max(0, totalWidth.value - viewportWidth.value);\n const maxRelY = Math.max(0, totalHeight.value - viewportHeight.value);\n\n if (internalScrollX.value > maxRelX || internalScrollY.value > maxRelY) {\n scrollToOffset(\n Math.min(internalScrollX.value, maxRelX),\n Math.min(internalScrollY.value, maxRelY),\n { behavior: 'auto' },\n );\n } else if ((newLen !== oldLen && scaleY.value !== 1) || (newColCount !== oldColCount && scaleX.value !== 1)) {\n // Even if within bounds, we must sync the display scroll position\n // because the coordinate scaling factor changed.\n scrollToOffset(internalScrollX.value, internalScrollY.value, { behavior: 'auto' });\n }\n updateHostOffset();\n });\n });\n\n // --- Range & Visible Items ---\n const getRowIndexAt = (offset: number) => {\n const gap = props.value.gap || 0;\n const columnGap = props.value.columnGap || 0;\n const fixedSize = fixedItemSize.value;\n\n if (direction.value === 'horizontal') {\n const step = (fixedSize || 0) + columnGap;\n if (fixedSize !== null && step > 0) {\n return Math.floor(offset / step);\n }\n return itemSizesX.findLowerBound(offset);\n }\n const step = (fixedSize || 0) + gap;\n if (fixedSize !== null && step > 0) {\n return Math.floor(offset / step);\n }\n return itemSizesY.findLowerBound(offset);\n };\n\n const getColIndexAt = (offset: number) => {\n if (direction.value === 'both') {\n return columnSizes.findLowerBound(offset);\n }\n if (direction.value === 'horizontal') {\n return getRowIndexAt(offset);\n }\n return 0;\n };\n\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: direction.value,\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 offsetX = relativeScrollX.value + stickyStartX.value;\n const offsetY = relativeScrollY.value + stickyStartY.value;\n const offset = direction.value === 'horizontal' ? offsetX : offsetY;\n return getRowIndexAt(offset);\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\n const columnGap = props.value.columnGap || 0;\n const padStart = fixedColumnWidth.value !== null\n ? safeStart * (fixedColumnWidth.value + columnGap)\n : columnSizes.query(safeStart);\n\n const totalColWidth = fixedColumnWidth.value !== null\n ? totalCols * (fixedColumnWidth.value + columnGap) - columnGap\n : Math.max(0, columnSizes.query(totalCols) - columnGap);\n\n const contentEnd = fixedColumnWidth.value !== null\n ? (safeEnd * (fixedColumnWidth.value + columnGap) - (safeEnd > 0 ? columnGap : 0))\n : (columnSizes.query(safeEnd) - (safeEnd > 0 ? columnGap : 0));\n\n return {\n start: safeStart,\n end: safeEnd,\n padStart,\n padEnd: Math.max(0, totalColWidth - contentEnd),\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 * 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 const sortedIndices: number[] = [];\n\n if (isHydrated.value || !props.value.ssrRange) {\n const activeIdx = currentIndex.value;\n const prevStickyIdx = findPrevStickyIndex(stickyIndices, activeIdx);\n\n if (prevStickyIdx !== undefined && prevStickyIdx < start) {\n sortedIndices.push(prevStickyIdx);\n }\n }\n\n for (let i = start; i < end; i++) {\n sortedIndices.push(i);\n }\n\n const ssrStartRow = props.value.ssrRange?.start || 0;\n\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 = (direction.value !== 'horizontal')\n ? (fixedSize !== null ? ssrStartRow * (fixedSize + gap) : itemSizesY.query(ssrStartRow))\n : 0;\n\n if (direction.value === 'horizontal') {\n ssrOffsetX = fixedSize !== null ? ssrStartCol * (fixedSize + columnGap) : itemSizesX.query(ssrStartCol);\n } else if (direction.value === 'both') {\n ssrOffsetX = columnSizes.query(ssrStartCol);\n }\n }\n\n const lastItemsMap = new Map(lastRenderedItems.map((it) => [ it.index, it ]));\n\n // Optimization: Cache sequential queries to avoid O(log N) tree traversal for every item\n let lastIndexX = -1;\n let lastOffsetX = 0;\n let lastIndexY = -1;\n let lastOffsetY = 0;\n\n const queryXCached = (idx: number) => {\n if (idx === lastIndexX + 1) {\n lastOffsetX += itemSizesX.get(lastIndexX);\n lastIndexX = idx;\n return lastOffsetX;\n }\n lastOffsetX = itemSizesX.query(idx);\n lastIndexX = idx;\n return lastOffsetX;\n };\n\n const queryYCached = (idx: number) => {\n if (idx === lastIndexY + 1) {\n lastOffsetY += itemSizesY.get(lastIndexY);\n lastIndexY = idx;\n return lastOffsetY;\n }\n lastOffsetY = itemSizesY.query(idx);\n lastIndexY = idx;\n return lastOffsetY;\n };\n\n const itemsStartVU_X = flowStartX.value + stickyStartX.value + paddingStartX.value;\n const itemsStartVU_Y = flowStartY.value + stickyStartY.value + paddingStartY.value;\n const wrapperStartDU_X = flowStartX.value + stickyStartX.value;\n const wrapperStartDU_Y = flowStartY.value + stickyStartY.value;\n\n const colRange = columnRange.value;\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: direction.value,\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: totalSize.value.width,\n queryY: queryYCached,\n queryX: queryXCached,\n getSizeY: (idx) => itemSizesY.get(idx),\n getSizeX: (idx) => itemSizesX.get(idx),\n columnRange: colRange,\n });\n\n const isSticky = stickySet.has(i);\n const originalX = x;\n const originalY = y;\n\n const { isStickyActive, isStickyActiveX, isStickyActiveY, stickyOffset } = calculateStickyItem({\n index: i,\n isSticky,\n direction: direction.value,\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: queryYCached,\n getItemQueryX: queryXCached,\n });\n\n const offsetX = isHydrated.value\n ? (internalScrollX.value / scaleX.value + (x + itemsStartVU_X - internalScrollX.value)) - wrapperStartDU_X\n : (x - ssrOffsetX);\n const offsetY = isHydrated.value\n ? (internalScrollY.value / scaleY.value + (y + itemsStartVU_Y - internalScrollY.value)) - wrapperStartDU_Y\n : (y - ssrOffsetY);\n\n const last = lastItemsMap.get(i);\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.isStickyActiveX === isStickyActiveX\n && last.isStickyActiveY === isStickyActiveY\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 isStickyActiveX,\n isStickyActiveY,\n stickyOffset,\n });\n }\n }\n\n lastRenderedItems = items;\n\n return items;\n });\n\n const scrollDetails = computed<ScrollDetails<T>>(() => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n const currentScrollX = relativeScrollX.value + stickyStartX.value;\n const currentScrollY = relativeScrollY.value + stickyStartY.value;\n\n const currentEndScrollX = relativeScrollX.value + (viewportWidth.value - stickyEndX.value) - 1;\n const currentEndScrollY = relativeScrollY.value + (viewportHeight.value - stickyEndY.value) - 1;\n\n const currentColIndex = getColIndexAt(currentScrollX);\n const currentRowIndex = getRowIndexAt(currentScrollY);\n const currentEndIndex = getRowIndexAt(direction.value === 'horizontal' ? currentEndScrollX : currentEndScrollY);\n const currentEndColIndex = getColIndexAt(currentEndScrollX);\n\n return {\n items: renderedItems.value,\n currentIndex: currentRowIndex,\n currentColIndex,\n currentEndIndex,\n currentEndColIndex,\n scrollOffset: {\n x: internalScrollX.value,\n y: internalScrollY.value,\n },\n displayScrollOffset: {\n x: isRtl.value ? Math.abs(scrollX.value + hostRefOffset.x) : Math.max(0, scrollX.value - hostRefOffset.x),\n y: Math.max(0, scrollY.value - hostRefOffset.y),\n },\n viewportSize: {\n width: viewportWidth.value,\n height: viewportHeight.value,\n },\n displayViewportSize: {\n width: viewportWidth.value,\n height: viewportHeight.value,\n },\n totalSize: {\n width: totalWidth.value,\n height: totalHeight.value,\n },\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 updateDirection();\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 const scrollValueX = isRtl.value ? Math.abs(scrollX.value) : scrollX.value;\n internalScrollX.value = displayToVirtual(scrollValueX, componentOffset.x, scaleX.value);\n internalScrollY.value = displayToVirtual(scrollY.value, componentOffset.y, scaleY.value);\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\n const firstRowIndex = getRowIndexAt(direction.value === 'horizontal' ? currentRelX : currentRelY);\n const firstColIndex = getColIndexAt(currentRelX);\n\n const isHorizontalMode = direction.value === 'horizontal';\n const isBothMode = direction.value === 'both';\n\n const processedRows = new Set<number>();\n const processedCols = new Set<number>();\n\n const tryUpdateColumn = (colIdx: number, width: number) => {\n if (colIdx >= 0 && colIdx < (props.value.columnCount || 0) && !processedCols.has(colIdx)) {\n processedCols.add(colIdx);\n const oldW = columnSizes.get(colIdx);\n const targetW = width + columnGap;\n\n if (!measuredColumns[ colIdx ] || Math.abs(oldW - targetW) > 0.1) {\n const d = targetW - oldW;\n if (Math.abs(d) > 0.1) {\n columnSizes.update(colIdx, d);\n needUpdate = true;\n if (colIdx < firstColIndex) {\n deltaX += d;\n }\n }\n measuredColumns[ colIdx ] = 1;\n }\n }\n };\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 (!isHorizontalMode) {\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 tryUpdateColumn(Number.parseInt(colIndexAttr, 10), inlineSize);\n } else {\n // If the element is a row, try to find cells with data-col-index\n const cells = 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 tryUpdateColumn(colIndex, child.getBoundingClientRect().width);\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 const contentStartLogicalX = flowStartX.value + stickyStartX.value + paddingStartX.value;\n const contentStartLogicalY = flowStartY.value + stickyStartY.value + paddingStartY.value;\n scrollToOffset(\n deltaX !== 0 ? currentRelX + deltaX + contentStartLogicalX : null,\n deltaY !== 0 ? currentRelY + deltaY + contentStartLogicalY : 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 function 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 container = props.value.container || window;\n const actualScrollX = (typeof window !== 'undefined' && container === window ? window.scrollX : (container as HTMLElement).scrollLeft);\n const actualScrollY = (typeof window !== 'undefined' && container === window ? window.scrollY : (container as HTMLElement).scrollTop);\n\n const scrollValueX = isRtl.value ? Math.abs(actualScrollX) : actualScrollX;\n const scrollValueY = actualScrollY;\n\n const currentRelX = displayToVirtual(scrollValueX, 0, scaleX.value);\n const currentRelY = displayToVirtual(scrollValueY, 0, scaleY.value);\n\n const { targetX, targetY } = calculateScrollTarget({\n rowIndex,\n colIndex,\n options,\n direction: direction.value,\n viewportWidth: viewportWidth.value,\n viewportHeight: viewportHeight.value,\n totalWidth: virtualWidth.value,\n totalHeight: virtualHeight.value,\n gap: props.value.gap || 0,\n columnGap: props.value.columnGap || 0,\n fixedSize: fixedItemSize.value,\n fixedWidth: fixedColumnWidth.value,\n relativeScrollX: currentRelX,\n relativeScrollY: currentRelY,\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 scaleX: scaleX.value,\n scaleY: scaleY.value,\n hostOffsetX: componentOffset.x,\n hostOffsetY: componentOffset.y,\n stickyIndices: sortedStickyIndices.value,\n stickyStartX: stickyStartX.value,\n stickyStartY: stickyStartY.value,\n stickyEndX: stickyEndX.value,\n stickyEndY: stickyEndY.value,\n flowPaddingStartX: flowStartX.value,\n flowPaddingStartY: flowStartY.value,\n paddingStartX: paddingStartX.value,\n paddingStartY: paddingStartY.value,\n paddingEndX: paddingEndX.value,\n paddingEndY: paddingEndY.value,\n });\n\n const toleranceX = 2;\n const toleranceY = 2;\n const reachedX = (colIndex === null || colIndex === undefined) || Math.abs(currentRelX - targetX) < toleranceX;\n const reachedY = (rowIndex === null || rowIndex === undefined) || Math.abs(currentRelY - targetY) < toleranceY;\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 && !isScrolling.value && !isProgrammaticScroll.value) {\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 let directionObserver: MutationObserver | null = null;\n let directionInterval: ReturnType<typeof setInterval> | undefined;\n\n const attachEvents = (container: HTMLElement | Window | null) => {\n if (typeof window === 'undefined') {\n return;\n }\n const effectiveContainer = container || window;\n const scrollTarget = (effectiveContainer === window || (isElement(effectiveContainer) && effectiveContainer === document.documentElement)) ? document : effectiveContainer;\n\n scrollTarget.addEventListener('scroll', handleScroll, { passive: true });\n\n computedStyle = null;\n updateDirection();\n\n if (isElement(effectiveContainer)) {\n directionObserver = new MutationObserver(() => updateDirection());\n directionObserver.observe(effectiveContainer, { attributes: true, attributeFilter: [ 'dir', 'style' ] });\n }\n\n directionInterval = setInterval(updateDirection, 1000);\n\n if (effectiveContainer === 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 updateDirection();\n viewportWidth.value = document.documentElement.clientWidth;\n viewportHeight.value = document.documentElement.clientHeight;\n updateHostOffset();\n };\n window.addEventListener('resize', onResize);\n\n return () => {\n scrollTarget.removeEventListener('scroll', handleScroll);\n window.removeEventListener('resize', onResize);\n directionObserver?.disconnect();\n clearInterval(directionInterval);\n computedStyle = null;\n };\n } else {\n viewportWidth.value = (effectiveContainer as HTMLElement).clientWidth;\n viewportHeight.value = (effectiveContainer as HTMLElement).clientHeight;\n scrollX.value = (effectiveContainer as HTMLElement).scrollLeft;\n scrollY.value = (effectiveContainer as HTMLElement).scrollTop;\n\n resizeObserver = new ResizeObserver(() => {\n updateDirection();\n viewportWidth.value = (effectiveContainer as HTMLElement).clientWidth;\n viewportHeight.value = (effectiveContainer as HTMLElement).clientHeight;\n updateHostOffset();\n });\n resizeObserver.observe(effectiveContainer as HTMLElement);\n\n return () => {\n scrollTarget.removeEventListener('scroll', handleScroll);\n resizeObserver?.disconnect();\n directionObserver?.disconnect();\n clearInterval(directionInterval);\n computedStyle = null;\n };\n }\n };\n\n let cleanup: (() => void) | undefined;\n\n if (getCurrentInstance()) {\n onMounted(() => {\n isMounted.value = true;\n updateDirection();\n\n watch(() => props.value.container, (newContainer) => {\n cleanup?.();\n cleanup = attachEvents(newContainer || null);\n }, { immediate: true });\n\n updateHostOffset();\n\n // Ensure we have a layout cycle before considering it hydrated\n // and starting virtualization. This avoids issues with 0-size viewports.\n nextTick(() => {\n updateHostOffset();\n if (props.value.ssrRange || props.value.initialScrollIndex !== undefined) {\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 } else {\n isHydrated.value = true;\n }\n });\n });\n\n onUnmounted(() => {\n cleanup?.();\n });\n }\n\n /**\n * The list of items currently rendered in the DOM.\n */\n /**\n * Resets all dynamic measurements and re-initializes from current props.\n * Useful if item source data has changed in a way that affects sizes without changing the items array reference.\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 * Offsets are in Display Units (DU), sizes are in Virtual Units (VU).\n * @see RenderedItem\n */\n renderedItems,\n\n /**\n * Total calculated width of all items including gaps (in VU).\n */\n totalWidth,\n\n /**\n * Total calculated height of all items including gaps (in VU).\n */\n totalHeight,\n\n /**\n * Total width to be rendered in the DOM (clamped to browser limits, in DU).\n */\n renderedWidth,\n\n /**\n * Total height to be rendered in the DOM (clamped to browser limits, in DU).\n */\n renderedHeight,\n\n /**\n * Detailed information about the current scroll state.\n * Includes currentIndex, scrollOffset (VU), displayScrollOffset (DU), viewportSize (DU), totalSize (VU), and scrolling status.\n * @see ScrollDetails\n */\n scrollDetails,\n\n /**\n * Helper to get the height of a specific row based on current configuration and measurements.\n *\n * @param index - The row index.\n * @returns The height in VU (excluding gap).\n */\n getRowHeight,\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 * @returns The width in VU (excluding gap).\n */\n getColumnWidth,\n\n /**\n * Helper to get the virtual offset of a specific row.\n *\n * @param index - The row index.\n * @returns The virtual offset in VU.\n */\n getRowOffset: (index: number) => (flowStartY.value + stickyStartY.value + paddingStartY.value) + itemSizesY.query(index),\n\n /**\n * Helper to get the virtual offset of a specific column.\n *\n * @param index - The column index.\n * @returns The virtual offset in VU.\n */\n getColumnOffset: (index: number) => (flowStartX.value + stickyStartX.value + paddingStartX.value) + columnSizes.query(index),\n\n /**\n * Helper to get the virtual offset of a specific item along the scroll axis.\n *\n * @param index - The item index.\n * @returns The virtual offset in VU.\n */\n getItemOffset: (index: number) => (direction.value === 'horizontal' ? (flowStartX.value + stickyStartX.value + paddingStartX.value) + itemSizesX.query(index) : (flowStartY.value + stickyStartY.value + paddingStartY.value) + itemSizesY.query(index)),\n\n /**\n * Helper to get the size of a specific item along the scroll axis.\n *\n * @param index - The item index.\n * @returns The size in VU (excluding gap).\n */\n getItemSize: (index: number) => {\n if (direction.value === 'horizontal') {\n return Math.max(0, itemSizesX.get(index) - (props.value.columnGap || 0));\n }\n const itemSize = props.value.itemSize;\n if (typeof itemSize === 'number' && itemSize > 0) {\n return itemSize;\n }\n if (typeof itemSize === 'function') {\n const item = props.value.items[ index ];\n return item !== undefined ? itemSize(item, index) : (props.value.defaultItemSize || DEFAULT_ITEM_SIZE);\n }\n return Math.max(0, itemSizesY.get(index) - (props.value.gap || 0));\n },\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 (VU). Pass null to keep current position.\n * @param y - The pixel offset to scroll to on the Y axis (VU). 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 in DU).\n * @param height - The measured blockSize (height in DU).\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 (sizes in DU).\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 * Detects the current direction (LTR/RTL) of the scroll container.\n */\n updateDirection,\n\n /**\n * Information about the current visible range of columns and their paddings.\n * @see ColumnRange\n */\n columnRange,\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 * Whether the container is the window or body.\n */\n isWindowContainer,\n\n /**\n * Whether the scroll container is in Right-to-Left (RTL) mode.\n */\n isRtl,\n\n /**\n * Coordinate scaling factor for X axis (VU/DU).\n */\n scaleX,\n\n /**\n * Coordinate scaling factor for Y axis (VU/DU).\n */\n scaleY,\n\n /**\n * Absolute offset of the component within its container (DU).\n */\n componentOffset,\n\n /**\n * Physical width of the items wrapper in the DOM (clamped to browser limits, in DU).\n */\n renderedVirtualWidth,\n\n /**\n * Physical height of the items wrapper in the DOM (clamped to browser limits, in DU).\n */\n renderedVirtualHeight,\n };\n}\n","/**\n * Composable for virtual scrollbar logic.\n * Handles calculation of thumb position and size, track interactions, and dragging.\n */\n\nimport type { ScrollAxis } from '../types';\nimport type { MaybeRefOrGetter } from 'vue';\n\nimport { computed, getCurrentInstance, onUnmounted, ref, toValue } from 'vue';\n\n/** Configuration properties for the `useVirtualScrollbar` composable. */\nexport interface UseVirtualScrollbarProps {\n /** The axis for this scrollbar. */\n axis: MaybeRefOrGetter<ScrollAxis>;\n /** Total size of the scrollable content area in display pixels (DU). */\n totalSize: MaybeRefOrGetter<number>;\n /** Current scroll position in display pixels (DU). */\n position: MaybeRefOrGetter<number>;\n /** Viewport size in display pixels (DU). */\n viewportSize: MaybeRefOrGetter<number>;\n /**\n * Function to scroll to a specific display pixel offset (DU) on this axis.\n * @param offset - The display pixel offset to scroll to.\n */\n scrollToOffset: (offset: number) => void;\n /** The ID of the container element this scrollbar controls. */\n containerId?: MaybeRefOrGetter<string | undefined>;\n /** Whether the scrollbar is in Right-to-Left (RTL) mode. */\n isRtl?: MaybeRefOrGetter<boolean>;\n /** Accessible label for the scrollbar. */\n ariaLabel?: MaybeRefOrGetter<string | undefined>;\n}\n\n/**\n * Composable for virtual scrollbar logic.\n * Provides attributes and event listeners for track and thumb elements.\n *\n * @param props - Configuration properties.\n */\nexport function useVirtualScrollbar(props: UseVirtualScrollbarProps) {\n const axis = computed(() => toValue(props.axis));\n const totalSize = computed(() => toValue(props.totalSize));\n const position = computed(() => toValue(props.position));\n const viewportSize = computed(() => toValue(props.viewportSize));\n const containerId = computed(() => toValue(props.containerId));\n const isRtl = computed(() => !!toValue(props.isRtl));\n\n const isHorizontal = computed(() => axis.value === 'horizontal');\n\n const viewportPercent = computed(() => {\n if (totalSize.value <= 0) {\n return 0;\n }\n return Math.min(1, viewportSize.value / totalSize.value);\n });\n\n const positionPercent = computed(() => {\n const scrollableRange = totalSize.value - viewportSize.value;\n if (scrollableRange <= 0) {\n return 0;\n }\n return Math.max(0, Math.min(1, position.value / scrollableRange));\n });\n\n const thumbSizePercent = computed(() => {\n // Minimum thumb size in pixels (32px for better touch targets and visibility)\n const minThumbSize = 32;\n const minPercent = viewportSize.value > 0 ? (minThumbSize / viewportSize.value) : 0.1;\n return Math.max(Math.min(minPercent, 0.1), viewportPercent.value) * 100;\n });\n /** Calculated thumb position as a percentage of the track size (0 to 100). */\n const thumbPositionPercent = computed(() => positionPercent.value * (100 - thumbSizePercent.value));\n\n /** Reactive style object for the scrollbar thumb. */\n const thumbStyle = computed(() => {\n if (isHorizontal.value) {\n return {\n inlineSize: `${ thumbSizePercent.value }%`,\n insetInlineStart: `${ thumbPositionPercent.value }%`,\n };\n }\n return {\n blockSize: `${ thumbSizePercent.value }%`,\n insetBlockStart: `${ thumbPositionPercent.value }%`,\n };\n });\n\n /** Reactive style object for the scrollbar track. */\n const trackStyle = computed(() => {\n const displayViewportSize = viewportSize.value;\n const scrollbarGap = 'var(--vs-scrollbar-has-cross-gap, var(--vsi-scrollbar-has-cross-gap, 0)) * var(--vs-scrollbar-cross-gap, var(--vsi-scrollbar-size, 8px))';\n\n return isHorizontal.value\n ? {\n inlineSize: `calc(${ Math.max(0, displayViewportSize - 4) }px - ${ scrollbarGap })`,\n }\n : {\n blockSize: `calc(${ Math.max(0, displayViewportSize - 4) }px - ${ scrollbarGap })`,\n };\n });\n\n const isDragging = ref(false);\n let startPos = 0;\n let startScrollPos = 0;\n\n function handleTrackClick(event: MouseEvent) {\n const track = event.currentTarget as HTMLElement;\n if (event.target !== track) {\n return;\n }\n\n const rect = track.getBoundingClientRect();\n const trackSize = isHorizontal.value ? rect.width : rect.height;\n let clickPos = 0;\n\n if (isHorizontal.value) {\n clickPos = isRtl.value ? rect.right - event.clientX : event.clientX - rect.left;\n } else {\n clickPos = event.clientY - rect.top;\n }\n\n const thumbSize = (thumbSizePercent.value / 100) * trackSize;\n const targetPercent = (clickPos - thumbSize / 2) / (trackSize - thumbSize);\n const scrollableRange = totalSize.value - viewportSize.value;\n\n let targetOffset = targetPercent * scrollableRange;\n if (targetOffset > scrollableRange - 1) {\n targetOffset = scrollableRange;\n }\n\n props.scrollToOffset(Math.max(0, Math.min(scrollableRange, targetOffset)));\n }\n\n function handleThumbPointerDown(event: PointerEvent) {\n isDragging.value = true;\n startPos = isHorizontal.value\n ? (isRtl.value ? -event.clientX : event.clientX)\n : event.clientY;\n startScrollPos = position.value;\n\n const thumb = event.currentTarget as HTMLElement;\n thumb.setPointerCapture(event.pointerId);\n event.preventDefault();\n event.stopPropagation();\n }\n\n function handleThumbPointerMove(event: PointerEvent) {\n if (!isDragging.value) {\n return;\n }\n\n const thumb = event.currentTarget as HTMLElement;\n const track = thumb.parentElement;\n if (!track) {\n return;\n }\n\n const currentPos = isHorizontal.value\n ? (isRtl.value ? -event.clientX : event.clientX)\n : event.clientY;\n const delta = currentPos - startPos;\n const rect = track.getBoundingClientRect();\n const trackSize = isHorizontal.value ? rect.width : rect.height;\n const thumbSize = (thumbSizePercent.value / 100) * trackSize;\n\n const scrollableTrackRange = trackSize - thumbSize;\n if (scrollableTrackRange <= 0) {\n return;\n }\n\n const scrollableContentRange = totalSize.value - viewportSize.value;\n let targetOffset = startScrollPos + (delta / scrollableTrackRange) * scrollableContentRange;\n\n if (targetOffset > scrollableContentRange - 1) {\n targetOffset = scrollableContentRange;\n }\n\n props.scrollToOffset(Math.max(0, Math.min(scrollableContentRange, targetOffset)));\n }\n\n function handleThumbPointerUp(event: PointerEvent) {\n if (!isDragging.value) {\n return;\n }\n isDragging.value = false;\n (event.currentTarget as HTMLElement).releasePointerCapture(event.pointerId);\n }\n\n if (getCurrentInstance()) {\n onUnmounted(() => {\n isDragging.value = false;\n });\n }\n\n const trackProps = computed(() => ({\n class: [\n 'virtual-scrollbar-track',\n `virtual-scrollbar-track--${ isHorizontal.value ? 'horizontal' : 'vertical' }`,\n ],\n style: trackStyle.value,\n role: 'scrollbar',\n 'aria-label': toValue(props.ariaLabel),\n 'aria-orientation': axis.value,\n 'aria-valuenow': Math.round(position.value),\n 'aria-valuemin': 0,\n 'aria-valuemax': Math.round(totalSize.value - viewportSize.value),\n 'aria-controls': containerId.value,\n tabindex: -1,\n onMousedown: handleTrackClick,\n }));\n\n const thumbProps = computed(() => ({\n class: [\n 'virtual-scrollbar-thumb',\n `virtual-scrollbar-thumb--${ isHorizontal.value ? 'horizontal' : 'vertical' }`,\n {\n 'virtual-scrollbar-thumb--active': isDragging.value,\n },\n ],\n style: thumbStyle.value,\n onPointerdown: handleThumbPointerDown,\n onPointermove: handleThumbPointerMove,\n onPointerup: handleThumbPointerUp,\n onPointercancel: handleThumbPointerUp,\n }));\n\n return {\n /** Viewport size as a percentage of total size (0 to 1). */\n viewportPercent,\n /** Current scroll position as a percentage of the scrollable range (0 to 1). */\n positionPercent,\n /** Calculated thumb size as a percentage of the track size (0 to 100). */\n thumbSizePercent,\n /** Calculated thumb position as a percentage of the track size (0 to 100). */\n thumbPositionPercent,\n /** Reactive style object for the scrollbar track. */\n trackStyle,\n /** Reactive style object for the scrollbar thumb. */\n thumbStyle,\n /** Attributes and event listeners to be bound to the track element. */\n trackProps,\n /** Attributes and event listeners to be bound to the thumb element. */\n thumbProps,\n /** Whether the thumb is currently being dragged. */\n isDragging,\n };\n}\n","<script setup lang=\"ts\">\n/**\n * A cross-browser consistent virtual scrollbar component.\n * Can be used independently or as part of the VirtualScroll component.\n * Supports both vertical and horizontal axes and RTL layouts.\n */\nimport type { VirtualScrollbarProps } from '../types';\n\nimport { useVirtualScrollbar } from '../composables/useVirtualScrollbar';\n\nexport interface Props extends VirtualScrollbarProps {}\n\nconst props = withDefaults(defineProps<Props>(), {\n axis: 'vertical',\n isRtl: false,\n});\n\nconst emit = defineEmits<{\n (e: 'scrollToOffset', offset: number): void;\n}>();\n\nconst { trackProps, thumbProps } = useVirtualScrollbar({\n axis: () => props.axis,\n totalSize: () => props.totalSize,\n position: () => props.position,\n viewportSize: () => props.viewportSize,\n containerId: () => props.containerId,\n isRtl: () => props.isRtl,\n scrollToOffset: (offset: number) => {\n props.scrollToOffset?.(offset);\n emit('scrollToOffset', offset);\n },\n});\n</script>\n\n<template>\n <div v-bind=\"trackProps\">\n <div v-bind=\"thumbProps\" />\n </div>\n</template>\n\n<style>\n@layer components {\n .virtual-scrollbar-track {\n --vsi-scrollbar-bg: var(--vs-scrollbar-bg, rgba(230, 230, 230, 0.9));\n --vsi-scrollbar-thumb-bg: var(--vs-scrollbar-thumb-bg, rgba(0, 0, 0, 0.3));\n --vsi-scrollbar-thumb-hover-bg: var(--vs-scrollbar-thumb-hover-bg, rgba(0, 0, 0, 0.6));\n\n --vsi-scrollbar-bg: var(--vs-scrollbar-bg, light-dark(rgba(230, 230, 230, 0.9), rgba(30, 30, 30, 0.9)));\n --vsi-scrollbar-thumb-bg: var(--vs-scrollbar-thumb-bg, light-dark(rgba(0, 0, 0, 0.3), rgba(255, 255, 255, 0.3)));\n --vsi-scrollbar-thumb-hover-bg: var(--vs-scrollbar-thumb-hover-bg, light-dark(rgba(0, 0, 0, 0.6), rgba(255, 255, 255, 0.6)));\n\n --vsi-scrollbar-radius: var(--vs-scrollbar-radius, 4px);\n --vsi-scrollbar-size: var(--vs-scrollbar-size, 8px);\n\n position: absolute;\n contain: layout;\n background-color: var(--vsi-scrollbar-bg);\n border-radius: var(--vsi-scrollbar-radius);\n z-index: 30;\n transition: opacity 0.2s;\n user-select: none;\n -webkit-user-select: none;\n pointer-events: auto;\n\n &.virtual-scrollbar-track--vertical {\n inline-size: var(--vsi-scrollbar-size);\n inset-block-start: 2px;\n inset-inline-end: 2px;\n }\n\n &.virtual-scrollbar-track--horizontal {\n block-size: var(--vsi-scrollbar-size);\n inset-inline-start: 2px;\n inset-block-end: 2px;\n }\n }\n\n .virtual-scrollbar-thumb {\n position: absolute;\n background-color: var(--vsi-scrollbar-thumb-bg);\n border-radius: var(--vsi-scrollbar-radius);\n touch-action: none;\n pointer-events: auto;\n cursor: pointer;\n\n &:hover,\n &:active,\n &.virtual-scrollbar-thumb--active {\n background-color: var(--vsi-scrollbar-thumb-hover-bg);\n }\n\n &.virtual-scrollbar-thumb--vertical {\n inline-size: 100%;\n }\n\n &.virtual-scrollbar-thumb--horizontal {\n block-size: 100%;\n }\n }\n}\n</style>\n","<script setup lang=\"ts\">\n/**\n * A cross-browser consistent virtual scrollbar component.\n * Can be used independently or as part of the VirtualScroll component.\n * Supports both vertical and horizontal axes and RTL layouts.\n */\nimport type { VirtualScrollbarProps } from '../types';\n\nimport { useVirtualScrollbar } from '../composables/useVirtualScrollbar';\n\nexport interface Props extends VirtualScrollbarProps {}\n\nconst props = withDefaults(defineProps<Props>(), {\n axis: 'vertical',\n isRtl: false,\n});\n\nconst emit = defineEmits<{\n (e: 'scrollToOffset', offset: number): void;\n}>();\n\nconst { trackProps, thumbProps } = useVirtualScrollbar({\n axis: () => props.axis,\n totalSize: () => props.totalSize,\n position: () => props.position,\n viewportSize: () => props.viewportSize,\n containerId: () => props.containerId,\n isRtl: () => props.isRtl,\n scrollToOffset: (offset: number) => {\n props.scrollToOffset?.(offset);\n emit('scrollToOffset', offset);\n },\n});\n</script>\n\n<template>\n <div v-bind=\"trackProps\">\n <div v-bind=\"thumbProps\" />\n </div>\n</template>\n\n<style>\n@layer components {\n .virtual-scrollbar-track {\n --vsi-scrollbar-bg: var(--vs-scrollbar-bg, rgba(230, 230, 230, 0.9));\n --vsi-scrollbar-thumb-bg: var(--vs-scrollbar-thumb-bg, rgba(0, 0, 0, 0.3));\n --vsi-scrollbar-thumb-hover-bg: var(--vs-scrollbar-thumb-hover-bg, rgba(0, 0, 0, 0.6));\n\n --vsi-scrollbar-bg: var(--vs-scrollbar-bg, light-dark(rgba(230, 230, 230, 0.9), rgba(30, 30, 30, 0.9)));\n --vsi-scrollbar-thumb-bg: var(--vs-scrollbar-thumb-bg, light-dark(rgba(0, 0, 0, 0.3), rgba(255, 255, 255, 0.3)));\n --vsi-scrollbar-thumb-hover-bg: var(--vs-scrollbar-thumb-hover-bg, light-dark(rgba(0, 0, 0, 0.6), rgba(255, 255, 255, 0.6)));\n\n --vsi-scrollbar-radius: var(--vs-scrollbar-radius, 4px);\n --vsi-scrollbar-size: var(--vs-scrollbar-size, 8px);\n\n position: absolute;\n contain: layout;\n background-color: var(--vsi-scrollbar-bg);\n border-radius: var(--vsi-scrollbar-radius);\n z-index: 30;\n transition: opacity 0.2s;\n user-select: none;\n -webkit-user-select: none;\n pointer-events: auto;\n\n &.virtual-scrollbar-track--vertical {\n inline-size: var(--vsi-scrollbar-size);\n inset-block-start: 2px;\n inset-inline-end: 2px;\n }\n\n &.virtual-scrollbar-track--horizontal {\n block-size: var(--vsi-scrollbar-size);\n inset-inline-start: 2px;\n inset-block-end: 2px;\n }\n }\n\n .virtual-scrollbar-thumb {\n position: absolute;\n background-color: var(--vsi-scrollbar-thumb-bg);\n border-radius: var(--vsi-scrollbar-radius);\n touch-action: none;\n pointer-events: auto;\n cursor: pointer;\n\n &:hover,\n &:active,\n &.virtual-scrollbar-thumb--active {\n background-color: var(--vsi-scrollbar-thumb-hover-bg);\n }\n\n &.virtual-scrollbar-thumb--vertical {\n inline-size: 100%;\n }\n\n &.virtual-scrollbar-thumb--horizontal {\n block-size: 100%;\n }\n }\n}\n</style>\n","<script setup lang=\"ts\" generic=\"T\">\n/**\n * A high-performance virtual scrolling component for Vue 3.\n * Supports large lists and grids by only rendering visible items and using coordinate scaling.\n * Features include sticky headers/footers, RTL support, custom scrollbars, and scroll restoration.\n */\nimport type {\n ItemSlotProps,\n RenderedItem,\n ScrollAlignment,\n ScrollbarSlotProps,\n ScrollDetails,\n ScrollToIndexOptions,\n VirtualScrollbarProps,\n VirtualScrollComponentProps,\n VirtualScrollProps,\n} from '../types';\nimport type { VNodeChild } from 'vue';\n\nimport { computed, nextTick, onMounted, onUnmounted, ref, toRefs, useId, watch } from 'vue';\n\nimport {\n useVirtualScroll,\n} from '../composables/useVirtualScroll';\nimport { useVirtualScrollbar } from '../composables/useVirtualScrollbar';\nimport { getPaddingX, getPaddingY } from '../utils/scroll';\nimport { calculateItemStyle, displayToVirtual } from '../utils/virtual-scroll-logic';\nimport VirtualScrollbar from './VirtualScrollbar.vue';\n\nexport interface Props<T = unknown> extends VirtualScrollComponentProps<T> {}\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 virtualScrollbar: false,\n itemRole: undefined,\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: ItemSlotProps<T>) => 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 /**\n * Scoped slot for rendering custom scrollbars.\n * If provided, the default VirtualScrollbar is not rendered.\n */\n scrollbar?: (props: ScrollbarSlotProps) => 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 instanceId = useId();\n\n/**\n * Unique ID for the scrollable container.\n * Used for accessibility (aria-controls) and to target the element in DOM.\n */\nconst containerId = computed(() => `vs-container-${ instanceId }`);\n\nconst measuredPaddingStart = ref(0);\nconst measuredPaddingEnd = ref(0);\n\nconst effectiveContainer = computed(() => (props.container === undefined ? hostRef.value : props.container));\n\nconst isHeaderFooterInsideContainer = computed(() => {\n const container = effectiveContainer.value;\n\n return container === hostRef.value\n || (typeof window !== 'undefined' && (container === window || container === null));\n});\n\nconst virtualScrollProps = computed(() => {\n /* Trigger re-evaluation on items array mutations */\n // eslint-disable-next-line ts/no-unused-expressions\n props.items.length;\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: effectiveContainer.value,\n hostElement: wrapperRef.value,\n hostRef: hostRef.value,\n ssrRange: props.ssrRange,\n columnCount: props.columnCount,\n columnWidth: props.columnWidth,\n scrollPaddingStart: {\n x: getPaddingX(props.scrollPaddingStart, props.direction),\n y: getPaddingY(props.scrollPaddingStart, props.direction),\n },\n scrollPaddingEnd: {\n x: getPaddingX(props.scrollPaddingEnd, props.direction),\n y: getPaddingY(props.scrollPaddingEnd, props.direction),\n },\n flowPaddingStart: {\n x: 0,\n y: props.stickyHeader ? 0 : measuredPaddingStart.value,\n },\n flowPaddingEnd: {\n x: 0,\n y: props.stickyFooter ? 0 : measuredPaddingEnd.value,\n },\n stickyStart: {\n x: 0,\n y: props.stickyHeader && isHeaderFooterInsideContainer.value ? measuredPaddingStart.value : 0,\n },\n stickyEnd: {\n x: 0,\n y: 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 isRtl,\n columnRange,\n renderedItems,\n scrollDetails,\n renderedHeight,\n renderedWidth,\n getColumnWidth,\n getRowHeight,\n scrollToIndex,\n scrollToOffset,\n updateHostOffset,\n updateItemSizes,\n updateDirection,\n getItemOffset,\n getRowOffset,\n getColumnOffset,\n getItemSize,\n refresh: coreRefresh,\n stopProgrammaticScroll,\n scaleX,\n scaleY,\n isWindowContainer,\n componentOffset,\n renderedVirtualWidth,\n renderedVirtualHeight,\n} = useVirtualScroll(virtualScrollProps);\n\nconst useVirtualScrolling = computed(() => scaleX.value !== 1 || scaleY.value !== 1);\n\nconst showVirtualScrollbars = computed(() => {\n if (isWindowContainer.value) {\n return false;\n }\n return props.virtualScrollbar === true || scaleX.value !== 1 || scaleY.value !== 1;\n});\n\nfunction handleVerticalScrollbarScrollToOffset(offset: number) {\n const { displayViewportSize } = scrollDetails.value;\n const scrollableRange = renderedHeight.value - displayViewportSize.height;\n if (offset >= scrollableRange - 0.5) {\n scrollToOffset(null, Number.POSITIVE_INFINITY);\n } else {\n const virtualOffset = displayToVirtual(offset, componentOffset.y, scaleY.value);\n scrollToOffset(null, virtualOffset);\n }\n}\n\nfunction handleHorizontalScrollbarScrollToOffset(offset: number) {\n const { displayViewportSize } = scrollDetails.value;\n const scrollableRange = renderedWidth.value - displayViewportSize.width;\n if (offset >= scrollableRange - 0.5) {\n scrollToOffset(Number.POSITIVE_INFINITY, null);\n } else {\n const virtualOffset = displayToVirtual(offset, componentOffset.x, scaleX.value);\n scrollToOffset(virtualOffset, null);\n }\n}\n\nconst verticalScrollbar = useVirtualScrollbar({\n axis: 'vertical',\n totalSize: renderedHeight,\n position: computed(() => scrollDetails.value.displayScrollOffset.y),\n viewportSize: computed(() => scrollDetails.value.displayViewportSize.height),\n scrollToOffset: handleVerticalScrollbarScrollToOffset,\n containerId,\n isRtl,\n});\n\nconst horizontalScrollbar = useVirtualScrollbar({\n axis: 'horizontal',\n totalSize: renderedWidth,\n position: computed(() => scrollDetails.value.displayScrollOffset.x),\n viewportSize: computed(() => scrollDetails.value.displayViewportSize.width),\n scrollToOffset: handleHorizontalScrollbarScrollToOffset,\n containerId,\n isRtl,\n});\n\nconst slotColumnRange = computed(() => {\n if (props.direction !== 'both') {\n return columnRange.value;\n }\n return {\n ...columnRange.value,\n padStart: 0,\n padEnd: 0,\n };\n});\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 updateDirection();\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 || !details) {\n return;\n }\n emit('scroll', details);\n\n if (\n !oldDetails\n || !oldDetails.range\n || !oldDetails.columnRange\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' && details.totalSize) {\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' && details.totalSize) {\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 && scrollDetails.value?.range && scrollDetails.value?.columnRange) {\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\nfunction watchExtraRef(refEl: typeof headerRef, measuredValue: typeof measuredPaddingStart) {\n watch(refEl, (newEl, oldEl) => {\n if (oldEl) {\n extraResizeObserver?.unobserve(oldEl);\n }\n if (newEl) {\n extraResizeObserver?.observe(newEl);\n } else {\n measuredValue.value = 0;\n }\n }, { immediate: true });\n}\n\nwatchExtraRef(headerRef, measuredPaddingStart);\nwatchExtraRef(footerRef, measuredPaddingEnd);\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\nwatch([ hostRef, useVirtualScrolling ], ([ host, virtual ], [ oldHost, oldVirtual ]) => {\n const needsUpdate = host !== oldHost || virtual !== oldVirtual;\n if (oldHost && needsUpdate) {\n oldHost.removeEventListener('wheel', handleWheel);\n }\n if (host && needsUpdate) {\n host.addEventListener('wheel', handleWheel, { passive: !virtual });\n }\n}, { immediate: true });\n\n/**\n * Callback ref to track and measure item elements.\n *\n * @param el - The element or null if unmounting.\n * @param index - The original index of the item.\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\n/**\n * State for inertia scrolling\n */\nconst isPointerScrolling = ref(false);\nlet startPointerPos = { x: 0, y: 0 };\nlet startScrollOffset = { x: 0, y: 0 };\nlet lastPointerPos = { x: 0, y: 0 };\nlet lastPointerTime = 0;\nlet velocity = { x: 0, y: 0 };\nlet inertiaAnimationFrame: number | null = null;\n\n// Friction constant (0.9 to 0.98 is usually best)\nconst FRICTION = 0.95;\n// Minimum velocity to continue the animation\nconst MIN_VELOCITY = 0.1;\n\n/**\n * Recursively animates the scroll offset based on velocity and friction.\n */\nfunction startInertiaAnimation() {\n const step = () => {\n // Apply friction to the velocity\n velocity.x *= FRICTION;\n velocity.y *= FRICTION;\n\n // Calculate the new scroll offset\n const currentX = scrollDetails.value.scrollOffset.x;\n const currentY = scrollDetails.value.scrollOffset.y;\n\n // Move the scroll position by the current velocity\n scrollToOffset(\n currentX + velocity.x * 16, // Assuming ~60fps (16ms per frame)\n currentY + velocity.y * 16,\n { behavior: 'auto' },\n );\n\n // Continue animation if we haven't slowed down to a halt\n if (Math.abs(velocity.x) > MIN_VELOCITY || Math.abs(velocity.y) > MIN_VELOCITY) {\n inertiaAnimationFrame = requestAnimationFrame(step);\n } else {\n stopInertia();\n }\n };\n\n inertiaAnimationFrame = requestAnimationFrame(step);\n}\n\n/**\n * Stops any ongoing inertia animation\n */\nfunction stopInertia() {\n if (inertiaAnimationFrame !== null) {\n cancelAnimationFrame(inertiaAnimationFrame);\n inertiaAnimationFrame = null;\n }\n velocity = { x: 0, y: 0 };\n}\n\n/**\n * Handles pointer down events on the container to start emulated scrolling when scaling is active.\n *\n * @param event - The pointer down event.\n */\nfunction handlePointerDown(event: PointerEvent) {\n stopProgrammaticScroll();\n stopInertia(); // Stop any existing momentum\n\n if (!useVirtualScrolling.value) {\n return;\n }\n\n // Only handle primary button or touch\n if (event.pointerType === 'mouse' && event.button !== 0) {\n return;\n }\n\n isPointerScrolling.value = true;\n startPointerPos = { x: event.clientX, y: event.clientY };\n lastPointerPos = { x: event.clientX, y: event.clientY };\n lastPointerTime = performance.now();\n startScrollOffset = {\n x: scrollDetails.value.scrollOffset.x,\n y: scrollDetails.value.scrollOffset.y,\n };\n\n (event.currentTarget as HTMLElement).setPointerCapture(event.pointerId);\n}\n\n/**\n * Handles pointer move events on the container to perform emulated scrolling.\n *\n * @param event - The pointer move event.\n */\nfunction handlePointerMove(event: PointerEvent) {\n if (!isPointerScrolling.value) {\n return;\n }\n\n const now = performance.now();\n const dt = now - lastPointerTime;\n\n if (dt > 0) {\n // Calculate instantaneous velocity (pixels per millisecond)\n const instantVelocityX = (lastPointerPos.x - event.clientX) / dt;\n const instantVelocityY = (lastPointerPos.y - event.clientY) / dt;\n\n // Use a moving average for smoother velocity tracking\n velocity.x = velocity.x * 0.2 + instantVelocityX * 0.8;\n velocity.y = velocity.y * 0.2 + instantVelocityY * 0.8;\n }\n\n lastPointerPos = { x: event.clientX, y: event.clientY };\n lastPointerTime = now;\n\n const deltaX = startPointerPos.x - event.clientX;\n const deltaY = startPointerPos.y - event.clientY;\n\n requestAnimationFrame(() => {\n scrollToOffset(\n startScrollOffset.x + deltaX,\n startScrollOffset.y + deltaY,\n { behavior: 'auto' },\n );\n });\n}\n\n/**\n * Handles pointer up and cancel events to end emulated scrolling.\n *\n * @param event - The pointer event.\n */\nfunction handlePointerUp(event: PointerEvent) {\n if (!isPointerScrolling.value) {\n return;\n }\n\n isPointerScrolling.value = false;\n (event.currentTarget as HTMLElement).releasePointerCapture(event.pointerId);\n\n // If the user was moving fast enough, start the inertia loop\n if (Math.abs(velocity.x) > MIN_VELOCITY || Math.abs(velocity.y) > MIN_VELOCITY) {\n // avoid unwanted cross-axis drift\n if (Math.abs(velocity.x) > 4 * Math.abs(velocity.y)) {\n velocity.y = 0;\n } else if (Math.abs(velocity.y) > 4 * Math.abs(velocity.x)) {\n velocity.x = 0;\n }\n\n startInertiaAnimation();\n }\n}\n\n/**\n * Handles mouse wheel events to support high-precision scrolling for large content or virtual scrollbars.\n *\n * @param event - The wheel event.\n */\nfunction handleWheel(event: WheelEvent) {\n const { scrollOffset } = scrollDetails.value;\n stopProgrammaticScroll();\n\n if (useVirtualScrolling.value) {\n // Prevent default browser scroll as we are handling it manually\n event.preventDefault();\n\n // For large content we manually scroll to keep precision/control\n let deltaX = event.deltaX;\n let deltaY = event.deltaY;\n\n if (event.shiftKey && deltaX === 0) {\n deltaX = deltaY;\n deltaY = 0;\n }\n\n const targetX = scrollOffset.x + deltaX;\n const targetY = scrollOffset.y + deltaY;\n\n scrollToOffset(targetX, targetY, { behavior: 'auto' });\n }\n}\n\n/**\n * Handles keyboard events for navigation (Home, End, Arrows, PageUp/Down).\n *\n * @param event - The keyboard event.\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 const sStart = virtualScrollProps.value.stickyStart as { x: number; y: number; };\n const sEnd = virtualScrollProps.value.stickyEnd as { x: number; y: number; };\n\n switch (event.key) {\n case 'Home': {\n event.preventDefault();\n stopProgrammaticScroll();\n const distance = Math.max(scrollOffset.x, scrollOffset.y);\n const viewport = props.direction === 'horizontal' ? viewportSize.width : viewportSize.height;\n const behavior = distance > 10 * viewport ? 'auto' : 'smooth';\n\n scrollToIndex(0, 0, { behavior, align: 'start' });\n break;\n }\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 const { totalSize } = scrollDetails.value;\n const distance = Math.max(\n totalSize.width - scrollOffset.x - viewportSize.width,\n totalSize.height - scrollOffset.y - viewportSize.height,\n );\n const viewport = props.direction === 'horizontal' ? viewportSize.width : viewportSize.height;\n const behavior = distance > 10 * viewport ? 'auto' : 'smooth';\n\n if (props.direction === 'both') {\n scrollToIndex(lastItemIndex, lastColIndex, { behavior, align: 'end' });\n } else {\n scrollToIndex(\n props.direction === 'vertical' ? lastItemIndex : 0,\n props.direction === 'horizontal' ? lastItemIndex : 0,\n { behavior, align: 'end' },\n );\n }\n break;\n }\n case 'ArrowUp': {\n event.preventDefault();\n stopProgrammaticScroll();\n if (!isVertical) {\n return;\n }\n\n const { currentIndex, scrollOffset } = scrollDetails.value;\n const viewportTop = scrollOffset.y + sStart.y + (virtualScrollProps.value.scrollPaddingStart as { x: number; y: number; }).y;\n const itemPos = getRowOffset(currentIndex);\n\n if (itemPos < viewportTop - 1) {\n scrollToIndex(currentIndex, null, { align: 'start' });\n } else if (currentIndex > 0) {\n scrollToIndex(currentIndex - 1, null, { align: 'start' });\n }\n break;\n }\n case 'ArrowDown': {\n event.preventDefault();\n stopProgrammaticScroll();\n if (!isVertical) {\n return;\n }\n\n const { currentEndIndex } = scrollDetails.value;\n const viewportBottom = scrollOffset.y + viewportSize.height - (sEnd.y + (virtualScrollProps.value.scrollPaddingEnd as { x: number; y: number; }).y);\n const itemBottom = getRowOffset(currentEndIndex) + getRowHeight(currentEndIndex);\n\n if (itemBottom > viewportBottom + 1) {\n scrollToIndex(currentEndIndex, null, { align: 'end' });\n } else if (currentEndIndex < props.items.length - 1) {\n scrollToIndex(currentEndIndex + 1, null, { align: 'end' });\n }\n break;\n }\n case 'ArrowLeft': {\n event.preventDefault();\n stopProgrammaticScroll();\n if (!isHorizontal) {\n return;\n }\n\n const { currentColIndex, currentEndColIndex } = scrollDetails.value;\n\n if (isRtl.value) {\n // RTL ArrowLeft -> towards logical END (Left)\n const viewportLeft = scrollOffset.x + viewportSize.width - (sEnd.x + (virtualScrollProps.value.scrollPaddingEnd as { x: number; y: number; }).x);\n const colEndPos = (props.columnCount ? getColumnOffset(currentEndColIndex) + getColumnWidth(currentEndColIndex) : getItemOffset(currentEndColIndex) + getItemSize(currentEndColIndex));\n\n if (colEndPos > viewportLeft + 1) {\n scrollToIndex(null, currentEndColIndex, { align: 'end' });\n } else {\n const maxColIdx = props.columnCount ? props.columnCount - 1 : props.items.length - 1;\n if (currentEndColIndex < maxColIdx) {\n scrollToIndex(null, currentEndColIndex + 1, { align: 'end' });\n }\n }\n } else {\n // LTR ArrowLeft -> towards logical START (Left)\n const viewportLeft = scrollOffset.x + sStart.x + (virtualScrollProps.value.scrollPaddingStart as { x: number; y: number; }).x;\n const colStartPos = (props.columnCount ? getColumnOffset(currentColIndex) : getItemOffset(currentColIndex));\n\n if (colStartPos < viewportLeft - 1) {\n scrollToIndex(null, currentColIndex, { align: 'start' });\n } else if (currentColIndex > 0) {\n scrollToIndex(null, currentColIndex - 1, { align: 'start' });\n }\n }\n break;\n }\n case 'ArrowRight': {\n event.preventDefault();\n stopProgrammaticScroll();\n if (!isHorizontal) {\n return;\n }\n\n const { currentColIndex, currentEndColIndex } = scrollDetails.value;\n\n if (isRtl.value) {\n // RTL ArrowRight -> towards logical START (Right)\n const viewportRight = scrollOffset.x + sStart.x + (virtualScrollProps.value.scrollPaddingStart as { x: number; y: number; }).x;\n const colStartPos = (props.columnCount ? getColumnOffset(currentColIndex) : getItemOffset(currentColIndex));\n\n if (colStartPos < viewportRight - 1) {\n scrollToIndex(null, currentColIndex, { align: 'start' });\n } else if (currentColIndex > 0) {\n scrollToIndex(null, currentColIndex - 1, { align: 'start' });\n }\n } else {\n // LTR ArrowRight -> towards logical END (Right)\n const viewportRight = scrollOffset.x + viewportSize.width - (sEnd.x + (virtualScrollProps.value.scrollPaddingEnd as { x: number; y: number; }).x);\n const colEndPos = (props.columnCount ? getColumnOffset(currentEndColIndex) + getColumnWidth(currentEndColIndex) : getItemOffset(currentEndColIndex) + getItemSize(currentEndColIndex));\n\n if (colEndPos > viewportRight + 1) {\n scrollToIndex(null, currentEndColIndex, { align: 'end' });\n } else {\n const maxColIdx = props.columnCount ? props.columnCount - 1 : props.items.length - 1;\n if (currentEndColIndex < maxColIdx) {\n scrollToIndex(null, currentEndColIndex + 1, { align: 'end' });\n }\n }\n }\n break;\n }\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 containerStyle = computed(() => {\n const base: Record<string, string | number | undefined> = {\n ...(props.direction !== 'vertical' ? { whiteSpace: 'nowrap' as const } : {}),\n };\n\n if (showVirtualScrollbars.value || !isWindowContainer.value) {\n base.overflow = 'auto';\n }\n\n if (useVirtualScrolling.value) {\n base.touchAction = 'none';\n }\n\n if (isWindowContainer.value) {\n return base;\n }\n\n if (props.containerTag === 'table') {\n return {\n ...base,\n display: 'block',\n minInlineSize: props.direction === 'vertical' ? '100%' : 'auto',\n };\n }\n\n return base;\n});\n\nconst verticalScrollbarProps = computed<ScrollbarSlotProps | null>(() => {\n if (props.direction === 'horizontal') {\n return null;\n }\n const { displayViewportSize, displayScrollOffset } = scrollDetails.value;\n if (renderedHeight.value <= displayViewportSize.height) {\n return null;\n }\n\n const scrollbarProps: VirtualScrollbarProps = {\n axis: 'vertical',\n totalSize: renderedHeight.value,\n position: displayScrollOffset.y,\n viewportSize: displayViewportSize.height,\n scrollToOffset: handleVerticalScrollbarScrollToOffset,\n containerId: containerId.value,\n isRtl: isRtl.value,\n ariaLabel: 'Vertical scroll',\n };\n\n return {\n axis: 'vertical',\n positionPercent: verticalScrollbar.positionPercent.value,\n viewportPercent: verticalScrollbar.viewportPercent.value,\n thumbSizePercent: verticalScrollbar.thumbSizePercent.value,\n thumbPositionPercent: verticalScrollbar.thumbPositionPercent.value,\n trackProps: verticalScrollbar.trackProps.value,\n thumbProps: verticalScrollbar.thumbProps.value,\n scrollbarProps,\n isDragging: verticalScrollbar.isDragging.value,\n };\n});\n\nconst horizontalScrollbarProps = computed<ScrollbarSlotProps | null>(() => {\n if (props.direction === 'vertical') {\n return null;\n }\n const { displayViewportSize, displayScrollOffset } = scrollDetails.value;\n if (renderedWidth.value <= displayViewportSize.width) {\n return null;\n }\n\n const scrollbarProps: VirtualScrollbarProps = {\n axis: 'horizontal',\n totalSize: renderedWidth.value,\n position: displayScrollOffset.x,\n viewportSize: displayViewportSize.width,\n scrollToOffset: handleHorizontalScrollbarScrollToOffset,\n containerId: containerId.value,\n isRtl: isRtl.value,\n ariaLabel: 'Horizontal scroll',\n };\n\n return {\n axis: 'horizontal',\n positionPercent: horizontalScrollbar.positionPercent.value,\n viewportPercent: horizontalScrollbar.viewportPercent.value,\n thumbSizePercent: horizontalScrollbar.thumbSizePercent.value,\n thumbPositionPercent: horizontalScrollbar.thumbPositionPercent.value,\n trackProps: horizontalScrollbar.trackProps.value,\n thumbProps: horizontalScrollbar.thumbProps.value,\n scrollbarProps,\n isDragging: horizontalScrollbar.isDragging.value,\n };\n});\n\nconst wrapperStyle = computed(() => {\n const isHorizontal = props.direction === 'horizontal';\n const isVertical = props.direction === 'vertical';\n const isBoth = props.direction === 'both';\n\n const style: Record<string, string | number | undefined> = {\n inlineSize: isVertical ? '100%' : `${ renderedVirtualWidth.value }px`,\n blockSize: isHorizontal ? '100%' : `${ renderedVirtualHeight.value }px`,\n };\n\n if (!isHydrated.value) {\n style.display = 'flex';\n style.flexDirection = isHorizontal ? 'row' : 'column';\n if ((isHorizontal || isBoth) && props.columnGap) {\n style.columnGap = `${ props.columnGap }px`;\n }\n if ((isVertical || isBoth) && props.gap) {\n style.rowGap = `${ props.gap }px`;\n }\n }\n\n return style;\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' : `${ renderedVirtualWidth.value }px`,\n blockSize: props.direction === 'horizontal' ? '1px' : `${ renderedVirtualHeight.value }px`,\n}));\n\n/**\n * Calculates the final style object for an item, including position and dimensions.\n *\n * @param item - The rendered item state.\n * @returns CSS style object.\n */\nfunction getItemStyle(item: RenderedItem<T>) {\n const style = calculateItemStyle({\n containerTag: props.containerTag || 'div',\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 isRtl: isRtl.value,\n });\n\n if (!isHydrated.value && props.direction === 'both') {\n style.display = 'flex';\n if (props.columnGap) {\n style.columnGap = `${ props.columnGap }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\nconst effectiveRole = computed(() => {\n if (props.role) {\n return props.role;\n }\n return isTable.value ? null : (props.direction === 'both' ? 'grid' : 'list');\n});\n\nconst isGrid = computed(() => effectiveRole.value === 'grid' || isTable.value);\n\nconst containerRole = computed(() => isTable.value\n ? null\n : ((props.ariaLabel || props.ariaLabelledby) ? 'region' : 'none'));\nconst wrapperRole = computed(() => isTable.value ? null : effectiveRole.value);\nconst internalItemRole = computed(() => {\n if (isGrid.value) {\n return 'row';\n }\n\n const role = effectiveRole.value;\n if (role === 'tree') {\n return 'treeitem';\n }\n if (role === 'listbox') {\n return 'option';\n }\n if (role === 'menu') {\n return 'menuitem';\n }\n return 'listitem';\n});\nconst itemRole = computed(() => props.itemRole != null ? props.itemRole : internalItemRole.value);\nconst cellRole = computed(() => {\n if (props.role === 'grid' || (!props.role && props.direction === 'both')) {\n return 'gridcell';\n }\n return isTable.value ? 'cell' : null;\n});\n\nconst shouldBindItemAria = computed(() => {\n const role = itemRole.value;\n return role == null || (role !== 'none' && role !== 'presentation');\n});\n\nconst rootAriaProps = computed(() => ({\n 'aria-label': props.ariaLabel,\n 'aria-labelledby': props.ariaLabelledby,\n 'aria-busy': props.loading ? 'true' : undefined,\n}));\n\nconst wrapperAriaProps = computed(() => {\n const aria: Record<string, string | number | undefined> = {};\n\n const role = effectiveRole.value;\n const supportsOrientation = role && [ 'grid', 'tree', 'listbox', 'menu', 'tablist' ].includes(role);\n\n if (supportsOrientation) {\n aria[ 'aria-orientation' ] = props.direction === 'both' ? undefined : props.direction;\n }\n\n if (isGrid.value) {\n aria[ 'aria-rowcount' ] = props.items.length;\n if (props.columnCount > 0) {\n aria[ 'aria-colcount' ] = props.columnCount;\n }\n }\n\n return aria;\n});\n\nfunction getItemAriaProps(index: number) {\n const aria: Record<string, string | number | undefined> = {};\n\n if (isGrid.value) {\n aria[ 'aria-rowindex' ] = index + 1;\n } else {\n aria[ 'aria-setsize' ] = props.items.length;\n aria[ 'aria-posinset' ] = index + 1;\n }\n\n const role = itemRole.value;\n if (role !== null) {\n aria.role = (role === 'none' || role === 'presentation')\n ? internalItemRole.value\n : role;\n }\n\n return aria;\n}\n\nfunction getCellAriaProps(colIndex: number) {\n const role = cellRole.value;\n if (!role) {\n return {};\n }\n\n const aria: Record<string, string | number | undefined> = {\n role,\n };\n\n if (isGrid.value) {\n aria[ 'aria-colindex' ] = colIndex + 1;\n }\n\n return aria;\n}\n\ndefineExpose({\n ...toRefs(props),\n\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 * Helper to get the height of a specific row.\n * @param index - The row index.\n * @see useVirtualScroll\n */\n getRowHeight,\n\n /**\n * Helper to get ARIA attributes for a cell.\n * @param colIndex - The column index.\n */\n getCellAriaProps,\n\n /**\n * Helper to get ARIA attributes for an item.\n * @param index - The item index.\n */\n getItemAriaProps,\n\n /**\n * Helper to get the virtual offset of a specific row.\n * @param index - The row index.\n * @see useVirtualScroll\n */\n getRowOffset,\n\n /**\n * Helper to get the virtual offset of a specific column.\n * @param index - The column index.\n * @see useVirtualScroll\n */\n getColumnOffset,\n\n /**\n * Helper to get the virtual offset of a specific item.\n * @param index - The item index.\n * @see useVirtualScroll\n */\n getItemOffset,\n\n /**\n * Helper to get the size of a specific item along the scroll axis.\n * @param index - The item index.\n * @see useVirtualScroll\n */\n getItemSize,\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 stopProgrammaticScroll();\n stopInertia();\n },\n\n /**\n * Detects the current direction (LTR/RTL) of the scroll container.\n */\n updateDirection,\n\n /**\n * Whether the scroll container is in Right-to-Left (RTL) mode.\n */\n isRtl,\n\n /**\n * Whether the component has finished its first client-side mount and hydration.\n */\n isHydrated,\n\n /**\n * Coordinate scaling factor for X axis.\n */\n scaleX,\n\n /**\n * Coordinate scaling factor for Y axis.\n */\n scaleY,\n\n /**\n * Physical width of the content in the DOM (clamped to browser limits).\n */\n renderedWidth,\n\n /**\n * Physical height of the content in the DOM (clamped to browser limits).\n */\n renderedHeight,\n\n /**\n * Absolute offset of the component within its container.\n */\n componentOffset,\n\n /**\n * Properties for the vertical scrollbar.\n * Useful when building custom scrollbar interfaces.\n */\n scrollbarPropsVertical: verticalScrollbarProps,\n\n /**\n * Properties for the horizontal scrollbar.\n * Useful when building custom scrollbar interfaces.\n */\n scrollbarPropsHorizontal: horizontalScrollbarProps,\n});\n</script>\n\n<template>\n <component\n :is=\"containerTag\"\n :id=\"containerId\"\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 'virtual-scroll--hide-scrollbar': showVirtualScrollbars,\n },\n ]\"\n :style=\"containerStyle\"\n tabindex=\"0\"\n :role=\"isTable ? undefined : containerRole\"\n v-bind=\"isTable ? { ...rootAriaProps, ...wrapperAriaProps } : rootAriaProps\"\n @keydown=\"handleKeyDown\"\n @pointerdown=\"handlePointerDown\"\n @pointermove=\"handlePointerMove\"\n @pointerup=\"handlePointerUp\"\n @pointercancel=\"handlePointerUp\"\n >\n <div\n v-if=\"showVirtualScrollbars\"\n class=\"virtual-scroll-scrollbar-container\"\n aria-hidden=\"true\"\n >\n <div\n class=\"virtual-scroll-scrollbar-viewport\"\n :style=\"{\n 'inlineSize': `${ scrollDetails.displayViewportSize.width }px`,\n 'blockSize': `${ scrollDetails.displayViewportSize.height }px`,\n '--vsi-scrollbar-has-cross-gap': direction === 'both' ? 1 : 0,\n }\"\n >\n <slot v-if=\"slots.scrollbar && verticalScrollbarProps\" name=\"scrollbar\" v-bind=\"verticalScrollbarProps\" />\n <VirtualScrollbar v-else-if=\"verticalScrollbarProps\" v-bind=\"verticalScrollbarProps.scrollbarProps\" />\n\n <slot v-if=\"slots.scrollbar && horizontalScrollbarProps\" name=\"scrollbar\" v-bind=\"horizontalScrollbarProps\" />\n <VirtualScrollbar v-else-if=\"horizontalScrollbarProps\" v-bind=\"horizontalScrollbarProps.scrollbarProps\" />\n </div>\n </div>\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 :role=\"isTable ? undefined : 'none'\"\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 :role=\"isTable ? undefined : wrapperRole\"\n v-bind=\"isTable ? {} : wrapperAriaProps\"\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 v-bind=\"shouldBindItemAria ? getItemAriaProps(renderedItem.index) : { role: 'none' }\"\n >\n <slot\n name=\"item\"\n :item=\"renderedItem.item\"\n :index=\"renderedItem.index\"\n :get-item-aria-props=\"getItemAriaProps\"\n :column-range=\"slotColumnRange\"\n :get-column-width=\"getColumnWidth\"\n :get-cell-aria-props=\"getCellAriaProps\"\n :gap=\"props.gap\"\n :column-gap=\"props.columnGap\"\n :is-sticky=\"renderedItem.isSticky\"\n :is-sticky-active=\"renderedItem.isStickyActive\"\n :is-sticky-active-x=\"renderedItem.isStickyActiveX\"\n :is-sticky-active-y=\"renderedItem.isStickyActiveY\"\n :offset=\"renderedItem.offset\"\n />\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 aria-live=\"polite\"\n aria-atomic=\"true\"\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 :role=\"isTable ? undefined : 'none'\"\n >\n <slot name=\"footer\" />\n </component>\n </component>\n</template>\n\n<style scoped>\n@layer components {\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 &.virtual-scroll--hide-scrollbar {\n scrollbar-width: none;\n -ms-overflow-style: none;\n\n &::-webkit-scrollbar {\n display: none;\n }\n }\n\n &.virtual-scroll--horizontal,\n &.virtual-scroll--both {\n white-space: nowrap;\n }\n }\n\n .virtual-scroll-scrollbar-container {\n position: sticky;\n inset-block-start: 0;\n inset-inline-start: 0;\n inline-size: 100%;\n block-size: 0;\n z-index: 30;\n pointer-events: none;\n overflow: visible;\n }\n\n .virtual-scroll-scrollbar-viewport {\n position: absolute;\n inset-block-start: 0;\n inset-inline-start: 0;\n pointer-events: none;\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}\n</style>\n","<script setup lang=\"ts\" generic=\"T\">\n/**\n * A high-performance virtual scrolling component for Vue 3.\n * Supports large lists and grids by only rendering visible items and using coordinate scaling.\n * Features include sticky headers/footers, RTL support, custom scrollbars, and scroll restoration.\n */\nimport type {\n ItemSlotProps,\n RenderedItem,\n ScrollAlignment,\n ScrollbarSlotProps,\n ScrollDetails,\n ScrollToIndexOptions,\n VirtualScrollbarProps,\n VirtualScrollComponentProps,\n VirtualScrollProps,\n} from '../types';\nimport type { VNodeChild } from 'vue';\n\nimport { computed, nextTick, onMounted, onUnmounted, ref, toRefs, useId, watch } from 'vue';\n\nimport {\n useVirtualScroll,\n} from '../composables/useVirtualScroll';\nimport { useVirtualScrollbar } from '../composables/useVirtualScrollbar';\nimport { getPaddingX, getPaddingY } from '../utils/scroll';\nimport { calculateItemStyle, displayToVirtual } from '../utils/virtual-scroll-logic';\nimport VirtualScrollbar from './VirtualScrollbar.vue';\n\nexport interface Props<T = unknown> extends VirtualScrollComponentProps<T> {}\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 virtualScrollbar: false,\n itemRole: undefined,\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: ItemSlotProps<T>) => 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 /**\n * Scoped slot for rendering custom scrollbars.\n * If provided, the default VirtualScrollbar is not rendered.\n */\n scrollbar?: (props: ScrollbarSlotProps) => 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 instanceId = useId();\n\n/**\n * Unique ID for the scrollable container.\n * Used for accessibility (aria-controls) and to target the element in DOM.\n */\nconst containerId = computed(() => `vs-container-${ instanceId }`);\n\nconst measuredPaddingStart = ref(0);\nconst measuredPaddingEnd = ref(0);\n\nconst effectiveContainer = computed(() => (props.container === undefined ? hostRef.value : props.container));\n\nconst isHeaderFooterInsideContainer = computed(() => {\n const container = effectiveContainer.value;\n\n return container === hostRef.value\n || (typeof window !== 'undefined' && (container === window || container === null));\n});\n\nconst virtualScrollProps = computed(() => {\n /* Trigger re-evaluation on items array mutations */\n // eslint-disable-next-line ts/no-unused-expressions\n props.items.length;\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: effectiveContainer.value,\n hostElement: wrapperRef.value,\n hostRef: hostRef.value,\n ssrRange: props.ssrRange,\n columnCount: props.columnCount,\n columnWidth: props.columnWidth,\n scrollPaddingStart: {\n x: getPaddingX(props.scrollPaddingStart, props.direction),\n y: getPaddingY(props.scrollPaddingStart, props.direction),\n },\n scrollPaddingEnd: {\n x: getPaddingX(props.scrollPaddingEnd, props.direction),\n y: getPaddingY(props.scrollPaddingEnd, props.direction),\n },\n flowPaddingStart: {\n x: 0,\n y: props.stickyHeader ? 0 : measuredPaddingStart.value,\n },\n flowPaddingEnd: {\n x: 0,\n y: props.stickyFooter ? 0 : measuredPaddingEnd.value,\n },\n stickyStart: {\n x: 0,\n y: props.stickyHeader && isHeaderFooterInsideContainer.value ? measuredPaddingStart.value : 0,\n },\n stickyEnd: {\n x: 0,\n y: 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 isRtl,\n columnRange,\n renderedItems,\n scrollDetails,\n renderedHeight,\n renderedWidth,\n getColumnWidth,\n getRowHeight,\n scrollToIndex,\n scrollToOffset,\n updateHostOffset,\n updateItemSizes,\n updateDirection,\n getItemOffset,\n getRowOffset,\n getColumnOffset,\n getItemSize,\n refresh: coreRefresh,\n stopProgrammaticScroll,\n scaleX,\n scaleY,\n isWindowContainer,\n componentOffset,\n renderedVirtualWidth,\n renderedVirtualHeight,\n} = useVirtualScroll(virtualScrollProps);\n\nconst useVirtualScrolling = computed(() => scaleX.value !== 1 || scaleY.value !== 1);\n\nconst showVirtualScrollbars = computed(() => {\n if (isWindowContainer.value) {\n return false;\n }\n return props.virtualScrollbar === true || scaleX.value !== 1 || scaleY.value !== 1;\n});\n\nfunction handleVerticalScrollbarScrollToOffset(offset: number) {\n const { displayViewportSize } = scrollDetails.value;\n const scrollableRange = renderedHeight.value - displayViewportSize.height;\n if (offset >= scrollableRange - 0.5) {\n scrollToOffset(null, Number.POSITIVE_INFINITY);\n } else {\n const virtualOffset = displayToVirtual(offset, componentOffset.y, scaleY.value);\n scrollToOffset(null, virtualOffset);\n }\n}\n\nfunction handleHorizontalScrollbarScrollToOffset(offset: number) {\n const { displayViewportSize } = scrollDetails.value;\n const scrollableRange = renderedWidth.value - displayViewportSize.width;\n if (offset >= scrollableRange - 0.5) {\n scrollToOffset(Number.POSITIVE_INFINITY, null);\n } else {\n const virtualOffset = displayToVirtual(offset, componentOffset.x, scaleX.value);\n scrollToOffset(virtualOffset, null);\n }\n}\n\nconst verticalScrollbar = useVirtualScrollbar({\n axis: 'vertical',\n totalSize: renderedHeight,\n position: computed(() => scrollDetails.value.displayScrollOffset.y),\n viewportSize: computed(() => scrollDetails.value.displayViewportSize.height),\n scrollToOffset: handleVerticalScrollbarScrollToOffset,\n containerId,\n isRtl,\n});\n\nconst horizontalScrollbar = useVirtualScrollbar({\n axis: 'horizontal',\n totalSize: renderedWidth,\n position: computed(() => scrollDetails.value.displayScrollOffset.x),\n viewportSize: computed(() => scrollDetails.value.displayViewportSize.width),\n scrollToOffset: handleHorizontalScrollbarScrollToOffset,\n containerId,\n isRtl,\n});\n\nconst slotColumnRange = computed(() => {\n if (props.direction !== 'both') {\n return columnRange.value;\n }\n return {\n ...columnRange.value,\n padStart: 0,\n padEnd: 0,\n };\n});\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 updateDirection();\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 || !details) {\n return;\n }\n emit('scroll', details);\n\n if (\n !oldDetails\n || !oldDetails.range\n || !oldDetails.columnRange\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' && details.totalSize) {\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' && details.totalSize) {\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 && scrollDetails.value?.range && scrollDetails.value?.columnRange) {\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\nfunction watchExtraRef(refEl: typeof headerRef, measuredValue: typeof measuredPaddingStart) {\n watch(refEl, (newEl, oldEl) => {\n if (oldEl) {\n extraResizeObserver?.unobserve(oldEl);\n }\n if (newEl) {\n extraResizeObserver?.observe(newEl);\n } else {\n measuredValue.value = 0;\n }\n }, { immediate: true });\n}\n\nwatchExtraRef(headerRef, measuredPaddingStart);\nwatchExtraRef(footerRef, measuredPaddingEnd);\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\nwatch([ hostRef, useVirtualScrolling ], ([ host, virtual ], [ oldHost, oldVirtual ]) => {\n const needsUpdate = host !== oldHost || virtual !== oldVirtual;\n if (oldHost && needsUpdate) {\n oldHost.removeEventListener('wheel', handleWheel);\n }\n if (host && needsUpdate) {\n host.addEventListener('wheel', handleWheel, { passive: !virtual });\n }\n}, { immediate: true });\n\n/**\n * Callback ref to track and measure item elements.\n *\n * @param el - The element or null if unmounting.\n * @param index - The original index of the item.\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\n/**\n * State for inertia scrolling\n */\nconst isPointerScrolling = ref(false);\nlet startPointerPos = { x: 0, y: 0 };\nlet startScrollOffset = { x: 0, y: 0 };\nlet lastPointerPos = { x: 0, y: 0 };\nlet lastPointerTime = 0;\nlet velocity = { x: 0, y: 0 };\nlet inertiaAnimationFrame: number | null = null;\n\n// Friction constant (0.9 to 0.98 is usually best)\nconst FRICTION = 0.95;\n// Minimum velocity to continue the animation\nconst MIN_VELOCITY = 0.1;\n\n/**\n * Recursively animates the scroll offset based on velocity and friction.\n */\nfunction startInertiaAnimation() {\n const step = () => {\n // Apply friction to the velocity\n velocity.x *= FRICTION;\n velocity.y *= FRICTION;\n\n // Calculate the new scroll offset\n const currentX = scrollDetails.value.scrollOffset.x;\n const currentY = scrollDetails.value.scrollOffset.y;\n\n // Move the scroll position by the current velocity\n scrollToOffset(\n currentX + velocity.x * 16, // Assuming ~60fps (16ms per frame)\n currentY + velocity.y * 16,\n { behavior: 'auto' },\n );\n\n // Continue animation if we haven't slowed down to a halt\n if (Math.abs(velocity.x) > MIN_VELOCITY || Math.abs(velocity.y) > MIN_VELOCITY) {\n inertiaAnimationFrame = requestAnimationFrame(step);\n } else {\n stopInertia();\n }\n };\n\n inertiaAnimationFrame = requestAnimationFrame(step);\n}\n\n/**\n * Stops any ongoing inertia animation\n */\nfunction stopInertia() {\n if (inertiaAnimationFrame !== null) {\n cancelAnimationFrame(inertiaAnimationFrame);\n inertiaAnimationFrame = null;\n }\n velocity = { x: 0, y: 0 };\n}\n\n/**\n * Handles pointer down events on the container to start emulated scrolling when scaling is active.\n *\n * @param event - The pointer down event.\n */\nfunction handlePointerDown(event: PointerEvent) {\n stopProgrammaticScroll();\n stopInertia(); // Stop any existing momentum\n\n if (!useVirtualScrolling.value) {\n return;\n }\n\n // Only handle primary button or touch\n if (event.pointerType === 'mouse' && event.button !== 0) {\n return;\n }\n\n isPointerScrolling.value = true;\n startPointerPos = { x: event.clientX, y: event.clientY };\n lastPointerPos = { x: event.clientX, y: event.clientY };\n lastPointerTime = performance.now();\n startScrollOffset = {\n x: scrollDetails.value.scrollOffset.x,\n y: scrollDetails.value.scrollOffset.y,\n };\n\n (event.currentTarget as HTMLElement).setPointerCapture(event.pointerId);\n}\n\n/**\n * Handles pointer move events on the container to perform emulated scrolling.\n *\n * @param event - The pointer move event.\n */\nfunction handlePointerMove(event: PointerEvent) {\n if (!isPointerScrolling.value) {\n return;\n }\n\n const now = performance.now();\n const dt = now - lastPointerTime;\n\n if (dt > 0) {\n // Calculate instantaneous velocity (pixels per millisecond)\n const instantVelocityX = (lastPointerPos.x - event.clientX) / dt;\n const instantVelocityY = (lastPointerPos.y - event.clientY) / dt;\n\n // Use a moving average for smoother velocity tracking\n velocity.x = velocity.x * 0.2 + instantVelocityX * 0.8;\n velocity.y = velocity.y * 0.2 + instantVelocityY * 0.8;\n }\n\n lastPointerPos = { x: event.clientX, y: event.clientY };\n lastPointerTime = now;\n\n const deltaX = startPointerPos.x - event.clientX;\n const deltaY = startPointerPos.y - event.clientY;\n\n requestAnimationFrame(() => {\n scrollToOffset(\n startScrollOffset.x + deltaX,\n startScrollOffset.y + deltaY,\n { behavior: 'auto' },\n );\n });\n}\n\n/**\n * Handles pointer up and cancel events to end emulated scrolling.\n *\n * @param event - The pointer event.\n */\nfunction handlePointerUp(event: PointerEvent) {\n if (!isPointerScrolling.value) {\n return;\n }\n\n isPointerScrolling.value = false;\n (event.currentTarget as HTMLElement).releasePointerCapture(event.pointerId);\n\n // If the user was moving fast enough, start the inertia loop\n if (Math.abs(velocity.x) > MIN_VELOCITY || Math.abs(velocity.y) > MIN_VELOCITY) {\n // avoid unwanted cross-axis drift\n if (Math.abs(velocity.x) > 4 * Math.abs(velocity.y)) {\n velocity.y = 0;\n } else if (Math.abs(velocity.y) > 4 * Math.abs(velocity.x)) {\n velocity.x = 0;\n }\n\n startInertiaAnimation();\n }\n}\n\n/**\n * Handles mouse wheel events to support high-precision scrolling for large content or virtual scrollbars.\n *\n * @param event - The wheel event.\n */\nfunction handleWheel(event: WheelEvent) {\n const { scrollOffset } = scrollDetails.value;\n stopProgrammaticScroll();\n\n if (useVirtualScrolling.value) {\n // Prevent default browser scroll as we are handling it manually\n event.preventDefault();\n\n // For large content we manually scroll to keep precision/control\n let deltaX = event.deltaX;\n let deltaY = event.deltaY;\n\n if (event.shiftKey && deltaX === 0) {\n deltaX = deltaY;\n deltaY = 0;\n }\n\n const targetX = scrollOffset.x + deltaX;\n const targetY = scrollOffset.y + deltaY;\n\n scrollToOffset(targetX, targetY, { behavior: 'auto' });\n }\n}\n\n/**\n * Handles keyboard events for navigation (Home, End, Arrows, PageUp/Down).\n *\n * @param event - The keyboard event.\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 const sStart = virtualScrollProps.value.stickyStart as { x: number; y: number; };\n const sEnd = virtualScrollProps.value.stickyEnd as { x: number; y: number; };\n\n switch (event.key) {\n case 'Home': {\n event.preventDefault();\n stopProgrammaticScroll();\n const distance = Math.max(scrollOffset.x, scrollOffset.y);\n const viewport = props.direction === 'horizontal' ? viewportSize.width : viewportSize.height;\n const behavior = distance > 10 * viewport ? 'auto' : 'smooth';\n\n scrollToIndex(0, 0, { behavior, align: 'start' });\n break;\n }\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 const { totalSize } = scrollDetails.value;\n const distance = Math.max(\n totalSize.width - scrollOffset.x - viewportSize.width,\n totalSize.height - scrollOffset.y - viewportSize.height,\n );\n const viewport = props.direction === 'horizontal' ? viewportSize.width : viewportSize.height;\n const behavior = distance > 10 * viewport ? 'auto' : 'smooth';\n\n if (props.direction === 'both') {\n scrollToIndex(lastItemIndex, lastColIndex, { behavior, align: 'end' });\n } else {\n scrollToIndex(\n props.direction === 'vertical' ? lastItemIndex : 0,\n props.direction === 'horizontal' ? lastItemIndex : 0,\n { behavior, align: 'end' },\n );\n }\n break;\n }\n case 'ArrowUp': {\n event.preventDefault();\n stopProgrammaticScroll();\n if (!isVertical) {\n return;\n }\n\n const { currentIndex, scrollOffset } = scrollDetails.value;\n const viewportTop = scrollOffset.y + sStart.y + (virtualScrollProps.value.scrollPaddingStart as { x: number; y: number; }).y;\n const itemPos = getRowOffset(currentIndex);\n\n if (itemPos < viewportTop - 1) {\n scrollToIndex(currentIndex, null, { align: 'start' });\n } else if (currentIndex > 0) {\n scrollToIndex(currentIndex - 1, null, { align: 'start' });\n }\n break;\n }\n case 'ArrowDown': {\n event.preventDefault();\n stopProgrammaticScroll();\n if (!isVertical) {\n return;\n }\n\n const { currentEndIndex } = scrollDetails.value;\n const viewportBottom = scrollOffset.y + viewportSize.height - (sEnd.y + (virtualScrollProps.value.scrollPaddingEnd as { x: number; y: number; }).y);\n const itemBottom = getRowOffset(currentEndIndex) + getRowHeight(currentEndIndex);\n\n if (itemBottom > viewportBottom + 1) {\n scrollToIndex(currentEndIndex, null, { align: 'end' });\n } else if (currentEndIndex < props.items.length - 1) {\n scrollToIndex(currentEndIndex + 1, null, { align: 'end' });\n }\n break;\n }\n case 'ArrowLeft': {\n event.preventDefault();\n stopProgrammaticScroll();\n if (!isHorizontal) {\n return;\n }\n\n const { currentColIndex, currentEndColIndex } = scrollDetails.value;\n\n if (isRtl.value) {\n // RTL ArrowLeft -> towards logical END (Left)\n const viewportLeft = scrollOffset.x + viewportSize.width - (sEnd.x + (virtualScrollProps.value.scrollPaddingEnd as { x: number; y: number; }).x);\n const colEndPos = (props.columnCount ? getColumnOffset(currentEndColIndex) + getColumnWidth(currentEndColIndex) : getItemOffset(currentEndColIndex) + getItemSize(currentEndColIndex));\n\n if (colEndPos > viewportLeft + 1) {\n scrollToIndex(null, currentEndColIndex, { align: 'end' });\n } else {\n const maxColIdx = props.columnCount ? props.columnCount - 1 : props.items.length - 1;\n if (currentEndColIndex < maxColIdx) {\n scrollToIndex(null, currentEndColIndex + 1, { align: 'end' });\n }\n }\n } else {\n // LTR ArrowLeft -> towards logical START (Left)\n const viewportLeft = scrollOffset.x + sStart.x + (virtualScrollProps.value.scrollPaddingStart as { x: number; y: number; }).x;\n const colStartPos = (props.columnCount ? getColumnOffset(currentColIndex) : getItemOffset(currentColIndex));\n\n if (colStartPos < viewportLeft - 1) {\n scrollToIndex(null, currentColIndex, { align: 'start' });\n } else if (currentColIndex > 0) {\n scrollToIndex(null, currentColIndex - 1, { align: 'start' });\n }\n }\n break;\n }\n case 'ArrowRight': {\n event.preventDefault();\n stopProgrammaticScroll();\n if (!isHorizontal) {\n return;\n }\n\n const { currentColIndex, currentEndColIndex } = scrollDetails.value;\n\n if (isRtl.value) {\n // RTL ArrowRight -> towards logical START (Right)\n const viewportRight = scrollOffset.x + sStart.x + (virtualScrollProps.value.scrollPaddingStart as { x: number; y: number; }).x;\n const colStartPos = (props.columnCount ? getColumnOffset(currentColIndex) : getItemOffset(currentColIndex));\n\n if (colStartPos < viewportRight - 1) {\n scrollToIndex(null, currentColIndex, { align: 'start' });\n } else if (currentColIndex > 0) {\n scrollToIndex(null, currentColIndex - 1, { align: 'start' });\n }\n } else {\n // LTR ArrowRight -> towards logical END (Right)\n const viewportRight = scrollOffset.x + viewportSize.width - (sEnd.x + (virtualScrollProps.value.scrollPaddingEnd as { x: number; y: number; }).x);\n const colEndPos = (props.columnCount ? getColumnOffset(currentEndColIndex) + getColumnWidth(currentEndColIndex) : getItemOffset(currentEndColIndex) + getItemSize(currentEndColIndex));\n\n if (colEndPos > viewportRight + 1) {\n scrollToIndex(null, currentEndColIndex, { align: 'end' });\n } else {\n const maxColIdx = props.columnCount ? props.columnCount - 1 : props.items.length - 1;\n if (currentEndColIndex < maxColIdx) {\n scrollToIndex(null, currentEndColIndex + 1, { align: 'end' });\n }\n }\n }\n break;\n }\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 containerStyle = computed(() => {\n const base: Record<string, string | number | undefined> = {\n ...(props.direction !== 'vertical' ? { whiteSpace: 'nowrap' as const } : {}),\n };\n\n if (showVirtualScrollbars.value || !isWindowContainer.value) {\n base.overflow = 'auto';\n }\n\n if (useVirtualScrolling.value) {\n base.touchAction = 'none';\n }\n\n if (isWindowContainer.value) {\n return base;\n }\n\n if (props.containerTag === 'table') {\n return {\n ...base,\n display: 'block',\n minInlineSize: props.direction === 'vertical' ? '100%' : 'auto',\n };\n }\n\n return base;\n});\n\nconst verticalScrollbarProps = computed<ScrollbarSlotProps | null>(() => {\n if (props.direction === 'horizontal') {\n return null;\n }\n const { displayViewportSize, displayScrollOffset } = scrollDetails.value;\n if (renderedHeight.value <= displayViewportSize.height) {\n return null;\n }\n\n const scrollbarProps: VirtualScrollbarProps = {\n axis: 'vertical',\n totalSize: renderedHeight.value,\n position: displayScrollOffset.y,\n viewportSize: displayViewportSize.height,\n scrollToOffset: handleVerticalScrollbarScrollToOffset,\n containerId: containerId.value,\n isRtl: isRtl.value,\n ariaLabel: 'Vertical scroll',\n };\n\n return {\n axis: 'vertical',\n positionPercent: verticalScrollbar.positionPercent.value,\n viewportPercent: verticalScrollbar.viewportPercent.value,\n thumbSizePercent: verticalScrollbar.thumbSizePercent.value,\n thumbPositionPercent: verticalScrollbar.thumbPositionPercent.value,\n trackProps: verticalScrollbar.trackProps.value,\n thumbProps: verticalScrollbar.thumbProps.value,\n scrollbarProps,\n isDragging: verticalScrollbar.isDragging.value,\n };\n});\n\nconst horizontalScrollbarProps = computed<ScrollbarSlotProps | null>(() => {\n if (props.direction === 'vertical') {\n return null;\n }\n const { displayViewportSize, displayScrollOffset } = scrollDetails.value;\n if (renderedWidth.value <= displayViewportSize.width) {\n return null;\n }\n\n const scrollbarProps: VirtualScrollbarProps = {\n axis: 'horizontal',\n totalSize: renderedWidth.value,\n position: displayScrollOffset.x,\n viewportSize: displayViewportSize.width,\n scrollToOffset: handleHorizontalScrollbarScrollToOffset,\n containerId: containerId.value,\n isRtl: isRtl.value,\n ariaLabel: 'Horizontal scroll',\n };\n\n return {\n axis: 'horizontal',\n positionPercent: horizontalScrollbar.positionPercent.value,\n viewportPercent: horizontalScrollbar.viewportPercent.value,\n thumbSizePercent: horizontalScrollbar.thumbSizePercent.value,\n thumbPositionPercent: horizontalScrollbar.thumbPositionPercent.value,\n trackProps: horizontalScrollbar.trackProps.value,\n thumbProps: horizontalScrollbar.thumbProps.value,\n scrollbarProps,\n isDragging: horizontalScrollbar.isDragging.value,\n };\n});\n\nconst wrapperStyle = computed(() => {\n const isHorizontal = props.direction === 'horizontal';\n const isVertical = props.direction === 'vertical';\n const isBoth = props.direction === 'both';\n\n const style: Record<string, string | number | undefined> = {\n inlineSize: isVertical ? '100%' : `${ renderedVirtualWidth.value }px`,\n blockSize: isHorizontal ? '100%' : `${ renderedVirtualHeight.value }px`,\n };\n\n if (!isHydrated.value) {\n style.display = 'flex';\n style.flexDirection = isHorizontal ? 'row' : 'column';\n if ((isHorizontal || isBoth) && props.columnGap) {\n style.columnGap = `${ props.columnGap }px`;\n }\n if ((isVertical || isBoth) && props.gap) {\n style.rowGap = `${ props.gap }px`;\n }\n }\n\n return style;\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' : `${ renderedVirtualWidth.value }px`,\n blockSize: props.direction === 'horizontal' ? '1px' : `${ renderedVirtualHeight.value }px`,\n}));\n\n/**\n * Calculates the final style object for an item, including position and dimensions.\n *\n * @param item - The rendered item state.\n * @returns CSS style object.\n */\nfunction getItemStyle(item: RenderedItem<T>) {\n const style = calculateItemStyle({\n containerTag: props.containerTag || 'div',\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 isRtl: isRtl.value,\n });\n\n if (!isHydrated.value && props.direction === 'both') {\n style.display = 'flex';\n if (props.columnGap) {\n style.columnGap = `${ props.columnGap }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\nconst effectiveRole = computed(() => {\n if (props.role) {\n return props.role;\n }\n return isTable.value ? null : (props.direction === 'both' ? 'grid' : 'list');\n});\n\nconst isGrid = computed(() => effectiveRole.value === 'grid' || isTable.value);\n\nconst containerRole = computed(() => isTable.value\n ? null\n : ((props.ariaLabel || props.ariaLabelledby) ? 'region' : 'none'));\nconst wrapperRole = computed(() => isTable.value ? null : effectiveRole.value);\nconst internalItemRole = computed(() => {\n if (isGrid.value) {\n return 'row';\n }\n\n const role = effectiveRole.value;\n if (role === 'tree') {\n return 'treeitem';\n }\n if (role === 'listbox') {\n return 'option';\n }\n if (role === 'menu') {\n return 'menuitem';\n }\n return 'listitem';\n});\nconst itemRole = computed(() => props.itemRole != null ? props.itemRole : internalItemRole.value);\nconst cellRole = computed(() => {\n if (props.role === 'grid' || (!props.role && props.direction === 'both')) {\n return 'gridcell';\n }\n return isTable.value ? 'cell' : null;\n});\n\nconst shouldBindItemAria = computed(() => {\n const role = itemRole.value;\n return role == null || (role !== 'none' && role !== 'presentation');\n});\n\nconst rootAriaProps = computed(() => ({\n 'aria-label': props.ariaLabel,\n 'aria-labelledby': props.ariaLabelledby,\n 'aria-busy': props.loading ? 'true' : undefined,\n}));\n\nconst wrapperAriaProps = computed(() => {\n const aria: Record<string, string | number | undefined> = {};\n\n const role = effectiveRole.value;\n const supportsOrientation = role && [ 'grid', 'tree', 'listbox', 'menu', 'tablist' ].includes(role);\n\n if (supportsOrientation) {\n aria[ 'aria-orientation' ] = props.direction === 'both' ? undefined : props.direction;\n }\n\n if (isGrid.value) {\n aria[ 'aria-rowcount' ] = props.items.length;\n if (props.columnCount > 0) {\n aria[ 'aria-colcount' ] = props.columnCount;\n }\n }\n\n return aria;\n});\n\nfunction getItemAriaProps(index: number) {\n const aria: Record<string, string | number | undefined> = {};\n\n if (isGrid.value) {\n aria[ 'aria-rowindex' ] = index + 1;\n } else {\n aria[ 'aria-setsize' ] = props.items.length;\n aria[ 'aria-posinset' ] = index + 1;\n }\n\n const role = itemRole.value;\n if (role !== null) {\n aria.role = (role === 'none' || role === 'presentation')\n ? internalItemRole.value\n : role;\n }\n\n return aria;\n}\n\nfunction getCellAriaProps(colIndex: number) {\n const role = cellRole.value;\n if (!role) {\n return {};\n }\n\n const aria: Record<string, string | number | undefined> = {\n role,\n };\n\n if (isGrid.value) {\n aria[ 'aria-colindex' ] = colIndex + 1;\n }\n\n return aria;\n}\n\ndefineExpose({\n ...toRefs(props),\n\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 * Helper to get the height of a specific row.\n * @param index - The row index.\n * @see useVirtualScroll\n */\n getRowHeight,\n\n /**\n * Helper to get ARIA attributes for a cell.\n * @param colIndex - The column index.\n */\n getCellAriaProps,\n\n /**\n * Helper to get ARIA attributes for an item.\n * @param index - The item index.\n */\n getItemAriaProps,\n\n /**\n * Helper to get the virtual offset of a specific row.\n * @param index - The row index.\n * @see useVirtualScroll\n */\n getRowOffset,\n\n /**\n * Helper to get the virtual offset of a specific column.\n * @param index - The column index.\n * @see useVirtualScroll\n */\n getColumnOffset,\n\n /**\n * Helper to get the virtual offset of a specific item.\n * @param index - The item index.\n * @see useVirtualScroll\n */\n getItemOffset,\n\n /**\n * Helper to get the size of a specific item along the scroll axis.\n * @param index - The item index.\n * @see useVirtualScroll\n */\n getItemSize,\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 stopProgrammaticScroll();\n stopInertia();\n },\n\n /**\n * Detects the current direction (LTR/RTL) of the scroll container.\n */\n updateDirection,\n\n /**\n * Whether the scroll container is in Right-to-Left (RTL) mode.\n */\n isRtl,\n\n /**\n * Whether the component has finished its first client-side mount and hydration.\n */\n isHydrated,\n\n /**\n * Coordinate scaling factor for X axis.\n */\n scaleX,\n\n /**\n * Coordinate scaling factor for Y axis.\n */\n scaleY,\n\n /**\n * Physical width of the content in the DOM (clamped to browser limits).\n */\n renderedWidth,\n\n /**\n * Physical height of the content in the DOM (clamped to browser limits).\n */\n renderedHeight,\n\n /**\n * Absolute offset of the component within its container.\n */\n componentOffset,\n\n /**\n * Properties for the vertical scrollbar.\n * Useful when building custom scrollbar interfaces.\n */\n scrollbarPropsVertical: verticalScrollbarProps,\n\n /**\n * Properties for the horizontal scrollbar.\n * Useful when building custom scrollbar interfaces.\n */\n scrollbarPropsHorizontal: horizontalScrollbarProps,\n});\n</script>\n\n<template>\n <component\n :is=\"containerTag\"\n :id=\"containerId\"\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 'virtual-scroll--hide-scrollbar': showVirtualScrollbars,\n },\n ]\"\n :style=\"containerStyle\"\n tabindex=\"0\"\n :role=\"isTable ? undefined : containerRole\"\n v-bind=\"isTable ? { ...rootAriaProps, ...wrapperAriaProps } : rootAriaProps\"\n @keydown=\"handleKeyDown\"\n @pointerdown=\"handlePointerDown\"\n @pointermove=\"handlePointerMove\"\n @pointerup=\"handlePointerUp\"\n @pointercancel=\"handlePointerUp\"\n >\n <div\n v-if=\"showVirtualScrollbars\"\n class=\"virtual-scroll-scrollbar-container\"\n aria-hidden=\"true\"\n >\n <div\n class=\"virtual-scroll-scrollbar-viewport\"\n :style=\"{\n 'inlineSize': `${ scrollDetails.displayViewportSize.width }px`,\n 'blockSize': `${ scrollDetails.displayViewportSize.height }px`,\n '--vsi-scrollbar-has-cross-gap': direction === 'both' ? 1 : 0,\n }\"\n >\n <slot v-if=\"slots.scrollbar && verticalScrollbarProps\" name=\"scrollbar\" v-bind=\"verticalScrollbarProps\" />\n <VirtualScrollbar v-else-if=\"verticalScrollbarProps\" v-bind=\"verticalScrollbarProps.scrollbarProps\" />\n\n <slot v-if=\"slots.scrollbar && horizontalScrollbarProps\" name=\"scrollbar\" v-bind=\"horizontalScrollbarProps\" />\n <VirtualScrollbar v-else-if=\"horizontalScrollbarProps\" v-bind=\"horizontalScrollbarProps.scrollbarProps\" />\n </div>\n </div>\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 :role=\"isTable ? undefined : 'none'\"\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 :role=\"isTable ? undefined : wrapperRole\"\n v-bind=\"isTable ? {} : wrapperAriaProps\"\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 v-bind=\"shouldBindItemAria ? getItemAriaProps(renderedItem.index) : { role: 'none' }\"\n >\n <slot\n name=\"item\"\n :item=\"renderedItem.item\"\n :index=\"renderedItem.index\"\n :get-item-aria-props=\"getItemAriaProps\"\n :column-range=\"slotColumnRange\"\n :get-column-width=\"getColumnWidth\"\n :get-cell-aria-props=\"getCellAriaProps\"\n :gap=\"props.gap\"\n :column-gap=\"props.columnGap\"\n :is-sticky=\"renderedItem.isSticky\"\n :is-sticky-active=\"renderedItem.isStickyActive\"\n :is-sticky-active-x=\"renderedItem.isStickyActiveX\"\n :is-sticky-active-y=\"renderedItem.isStickyActiveY\"\n :offset=\"renderedItem.offset\"\n />\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 aria-live=\"polite\"\n aria-atomic=\"true\"\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 :role=\"isTable ? undefined : 'none'\"\n >\n <slot name=\"footer\" />\n </component>\n </component>\n</template>\n\n<style scoped>\n@layer components {\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 &.virtual-scroll--hide-scrollbar {\n scrollbar-width: none;\n -ms-overflow-style: none;\n\n &::-webkit-scrollbar {\n display: none;\n }\n }\n\n &.virtual-scroll--horizontal,\n &.virtual-scroll--both {\n white-space: nowrap;\n }\n }\n\n .virtual-scroll-scrollbar-container {\n position: sticky;\n inset-block-start: 0;\n inset-inline-start: 0;\n inline-size: 100%;\n block-size: 0;\n z-index: 30;\n pointer-events: none;\n overflow: visible;\n }\n\n .virtual-scroll-scrollbar-viewport {\n position: absolute;\n inset-block-start: 0;\n inset-inline-start: 0;\n pointer-events: none;\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}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["EMPTY_SCROLL_DETAILS: ScrollDetails<unknown>","nextStickyIdx: number | undefined","prevStickyIdx: number | undefined","align: ScrollAlignment | ScrollAlignmentOptions | ScrollToIndexOptions | undefined","effectiveAlignX: ScrollAlignment","effectiveAlignY: ScrollAlignment","style: Record<string, string | number | undefined>","lastItems: T[]","scrollTimeout: ReturnType<typeof setTimeout> | undefined","scrollDirectionX: 'start' | 'end' | null","scrollDirectionY: 'start' | 'end' | null","computedStyle: CSSStyleDeclaration | null","behavior: 'auto' | 'smooth' | undefined","scrollOptions: ScrollToOptions","lastRenderedItems: RenderedItem<T>[]","items: RenderedItem<T>[]","sortedIndices: number[]","targetRow: number | null","targetCol: number | null","alignY: ScrollAlignment","alignX: ScrollAlignment","correctionOptions: ScrollToIndexOptions","resizeObserver: ResizeObserver | null","directionObserver: MutationObserver | null","directionInterval: ReturnType<typeof setInterval> | undefined","cleanup: (() => void) | undefined"],"sources":["../src/types.ts","../src/utils/scroll.ts","../src/utils/virtual-scroll-logic.ts","../src/utils/fenwick-tree.ts","../src/composables/useVirtualScrollSizes.ts","../src/composables/useVirtualScroll.ts","../src/composables/useVirtualScrollbar.ts","../src/components/VirtualScrollbar.vue","../src/components/VirtualScrollbar.vue","../src/components/VirtualScroll.vue","../src/components/VirtualScroll.vue"],"sourcesContent":["/** Default fallback size for items (VU). */\nexport const DEFAULT_ITEM_SIZE = 40;\n/** Default fallback width for columns (VU). */\nexport const DEFAULT_COLUMN_WIDTH = 100;\n/** Default number of items to render outside the viewport. */\nexport const DEFAULT_BUFFER = 5;\n\n/** Represents a point in 2D space. */\nexport interface Point {\n /** X coordinate. */\n x: number;\n /** Y coordinate. */\n y: number;\n}\n\n/** Represents dimensions in 2D space. */\nexport interface Size {\n /** Width dimension. */\n width: number;\n /** Height dimension. */\n height: number;\n}\n\n/** Initial empty state for scroll details. */\nexport const EMPTY_SCROLL_DETAILS: ScrollDetails<unknown> = {\n items: [],\n currentIndex: 0,\n currentColIndex: 0,\n currentEndIndex: 0,\n currentEndColIndex: 0,\n scrollOffset: { x: 0, y: 0 },\n displayScrollOffset: { x: 0, y: 0 },\n viewportSize: { width: 0, height: 0 },\n displayViewportSize: { width: 0, height: 0 },\n totalSize: { width: 0, height: 0 },\n isScrolling: false,\n isProgrammaticScroll: false,\n range: { start: 0, end: 0 },\n columnRange: { start: 0, end: 0, padStart: 0, padEnd: 0 },\n};\n\n/**\n * The direction of the virtual scroll.\n * - 'vertical': Single-column vertical scrolling.\n * - 'horizontal': Single-row horizontal scrolling.\n * - 'both': Bidirectional grid-based scrolling.\n */\nexport type ScrollDirection = 'vertical' | 'horizontal' | 'both';\n\n/**\n * Alignment of an item within the viewport after a scroll operation.\n * - 'start': Aligns item to the top or left edge.\n * - 'center': Aligns item to the center of the viewport.\n * - 'end': Aligns item to the bottom or right edge.\n * - 'auto': Smart alignment. If visible, stays. If not, aligns to nearest edge.\n */\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 (horizontal) axis. */\n x?: ScrollAlignment;\n /** Alignment on the Y (vertical) axis. */\n y?: ScrollAlignment;\n}\n\n/** Options for the `scrollToIndex` method. */\nexport interface ScrollToIndexOptions {\n /**\n * Where to align the item in the viewport.\n * Can be a single value for both axes or an object for individual control.\n * @default 'auto'\n */\n align?: ScrollAlignment | ScrollAlignmentOptions;\n\n /**\n * Scroll behavior.\n * - 'auto': Instant jump.\n * - 'smooth': Animated transition.\n * @default 'smooth'\n */\n behavior?: 'auto' | 'smooth';\n\n /**\n * Internal flag for recursive correction calls.\n * Users should generally not set this.\n * @internal\n */\n isCorrection?: boolean;\n}\n\n/** Represents an item currently rendered in the virtual scroll area. */\nexport interface RenderedItem<T = unknown> {\n /** The original data item from the provided source array. */\n item: T;\n /** The 0-based index of the item in the original array. */\n index: number;\n /** The calculated pixel offset relative to the items wrapper in display pixels (DU). */\n offset: Point;\n /** The current measured or estimated size of the item in virtual units (VU). */\n size: Size;\n /** The original horizontal pixel offset before any sticky adjustments in VU. */\n originalX: number;\n /** The original vertical pixel offset before any sticky adjustments in VU. */\n originalY: number;\n /** Whether this item is configured to be sticky via the `stickyIndices` property. */\n isSticky?: boolean;\n /** Whether this item is currently in a stuck state at the viewport edge. */\n isStickyActive?: boolean;\n /** Whether this item is currently in a stuck state at the horizontal viewport edge. */\n isStickyActiveX?: boolean;\n /** Whether this item is currently in a stuck state at the vertical viewport edge. */\n isStickyActiveY?: boolean;\n /** The relative translation applied to the item for the sticky pushing effect in DU. */\n stickyOffset: Point;\n}\n\n/** Information about the currently visible range of columns and their paddings. */\nexport interface ColumnRange {\n /** Inclusive start index. */\n start: number;\n /** Exclusive end index. */\n end: number;\n /** Pixel padding to maintain at the start of the row in VU. */\n padStart: number;\n /** Pixel padding to maintain at the end of the row in VU. */\n padEnd: number;\n}\n\n/** Comprehensive state of the virtual scroll system. */\nexport interface ScrollDetails<T = unknown> {\n /** List of items currently rendered in the DOM buffer. */\n items: RenderedItem<T>[];\n /** Index of the first item visible below any sticky header in the viewport. */\n currentIndex: number;\n /** Index of the first column visible after any sticky column in the viewport (grid mode). */\n currentColIndex: number;\n /** Index of the last item visible above any sticky footer in the viewport. */\n currentEndIndex: number;\n /** Index of the last column visible before any sticky end column in the viewport (grid mode). */\n currentEndColIndex: number;\n /** Current relative pixel scroll position from the content start in VU. */\n scrollOffset: Point;\n /** Current display pixel scroll position (before scaling) in DU. */\n displayScrollOffset: Point;\n /** Current dimensions of the visible viewport area in VU. */\n viewportSize: Size;\n /** Current dimensions of the visible viewport area in display pixels (DU). */\n displayViewportSize: Size;\n /** Total calculated or estimated size of all items and gaps in VU. */\n totalSize: Size;\n /** Whether the container is currently being scrolled by the user or an animation. */\n isScrolling: boolean;\n /** Whether the current scroll operation was initiated programmatically. */\n isProgrammaticScroll: boolean;\n /** The range of item indices currently being rendered. */\n range: {\n /** Inclusive start index. */\n start: number;\n /** Exclusive end index. */\n end: number;\n };\n /** The range of column indices and associated paddings currently being rendered. */\n columnRange: ColumnRange;\n}\n\n/** Helper to get ARIA attributes for an item. */\nexport type GetItemAriaProps = (index: number) => Record<string, string | number | undefined>;\n\n/**\n * Configuration for Server-Side Rendering.\n * Defines which items are rendered statically on the server.\n */\nexport interface SSRRange {\n /** First row index (for list or grid). */\n start: number;\n /** Exclusive last row index (for list or grid). */\n end: number;\n /** First column index (for grid mode). */\n colStart?: number;\n /** Exclusive last column index (for grid mode). */\n colEnd?: number;\n}\n\n/** Pixel padding configuration in display pixels (DU). */\nexport type PaddingValue = number | { x?: number; y?: number; };\n\n/**\n * Snap mode for automatic alignment after scrolling stops.\n * - `false`: No snapping.\n * - `true`: Same as 'auto'.\n * - 'start': Aligns the first visible item to the viewport start if at least 50% visible, otherwise aligns the next item.\n * - 'center': Aligns the item that intersects the viewport center to the center.\n * - 'end': Aligns the last visible item to the viewport end if at least 50% visible, otherwise aligns the previous item.\n * - 'auto': Intelligent snapping based on scroll direction. Acts as 'end' when scrolling towards start, and 'start' when scrolling towards end.\n */\nexport type SnapMode = boolean | 'start' | 'center' | 'end' | 'auto';\n\n/** Base configuration properties shared between the component and the composable. */\nexport interface VirtualScrollBaseProps<T = unknown> {\n /** Array of data items to virtualize. */\n items: T[];\n\n /**\n * Fixed size of each item in virtual units (VU) or a function that returns the size of an item.\n * Pass `0`, `null` or `undefined` for automatic dynamic size detection via `ResizeObserver`.\n */\n itemSize?: number | ((item: T, index: number) => number) | null | undefined;\n\n /**\n * Direction of the virtual scroll.\n * @default 'vertical'\n */\n direction?: ScrollDirection | undefined;\n\n /**\n * Number of items to render before the visible viewport.\n * @default 5\n */\n bufferBefore?: number | undefined;\n\n /**\n * Number of items to render after the visible viewport.\n * @default 5\n */\n bufferAfter?: number | undefined;\n\n /**\n * The scrollable element or window object.\n * If not provided, virtualization usually happens relative to the `hostRef`.\n */\n container?: HTMLElement | Window | null | undefined;\n\n /**\n * Configuration for Server-Side Rendering.\n * Defines which items are rendered statically on the server.\n */\n ssrRange?: SSRRange | undefined;\n\n /**\n * Number of columns for bidirectional grid scrolling.\n */\n columnCount?: number | undefined;\n\n /**\n * Fixed width of columns in VU, an array of widths, or a function returning widths.\n * Pass `0`, `null` or `undefined` for dynamic column detection.\n */\n columnWidth?: number | number[] | ((index: number) => number) | null | undefined;\n\n /**\n * Pixel padding at the start of the scroll container in display pixels (DU).\n */\n scrollPaddingStart?: PaddingValue | undefined;\n\n /**\n * Pixel padding at the end of the scroll container in DU.\n */\n scrollPaddingEnd?: PaddingValue | undefined;\n\n /**\n * Gap between items in virtual units (VU).\n * Applied vertically in list/grid mode, horizontally in horizontal list mode.\n */\n gap?: number | undefined;\n\n /**\n * Gap between columns in virtual units (VU).\n * Applied in horizontal and bidirectional grid modes.\n */\n columnGap?: number | undefined;\n\n /**\n * List of indices that should stick to the viewport edge.\n */\n stickyIndices?: number[] | undefined;\n\n /**\n * Threshold distance from the end in display pixels (DU) to emit the 'load' event.\n * @default 200\n */\n loadDistance?: number | undefined;\n\n /**\n * Whether data is currently loading.\n */\n loading?: boolean | undefined;\n\n /**\n * Whether to automatically restore and maintain scroll position when items are prepended to the array.\n */\n restoreScrollOnPrepend?: boolean | undefined;\n\n /**\n * Initial row index to jump to on mount.\n */\n initialScrollIndex?: number | undefined;\n\n /**\n * Initial scroll alignment logic.\n * @default 'start'\n */\n initialScrollAlign?: ScrollAlignment | ScrollAlignmentOptions | undefined;\n\n /**\n * Default fallback size for items before they are measured in VU.\n */\n defaultItemSize?: number | undefined;\n\n /**\n * Default fallback width for columns before they are measured in VU.\n */\n defaultColumnWidth?: number | undefined;\n\n /**\n * Enable debug visualization of buffers and indices.\n */\n debug?: boolean | undefined;\n\n /**\n * ARIA role for the scroll container.\n * Defaults to 'list' for vertical/horizontal and 'grid' for both.\n */\n role?: string | undefined;\n\n /**\n * ARIA label for the scroll container.\n */\n ariaLabel?: string | undefined;\n\n /**\n * ID of the element that labels the scroll container.\n */\n ariaLabelledby?: string | undefined;\n\n /**\n * ARIA role for each rendered item.\n * Defaults to 'listitem' for list roles and 'row' for grid roles.\n * Set to 'none' or 'presentation' to disable automatic role assignment on the wrapper.\n */\n itemRole?: string | undefined;\n\n /**\n * Whether to snap to items after scrolling stops.\n * @default false\n */\n snap?: SnapMode | undefined;\n}\n\n/** Configuration properties for the `useVirtualScroll` composable. */\nexport interface VirtualScrollProps<T = unknown> extends VirtualScrollBaseProps<T> {\n /**\n * The host element that directly wraps the absolute-positioned items.\n * Used for calculating relative offsets in display pixels (DU).\n */\n hostElement?: HTMLElement | null | undefined;\n\n /**\n * The root element of the VirtualScroll component.\n * Used for calculating relative offsets in display pixels (DU).\n */\n hostRef?: HTMLElement | null | undefined;\n\n /**\n * Size of sticky elements at the start of the viewport (top or left) in DU.\n * Used to adjust the visible range and item positioning without increasing content size.\n */\n stickyStart?: PaddingValue | undefined;\n\n /**\n * Size of sticky elements at the end of the viewport (bottom or right) in DU.\n * Used to adjust the visible range without increasing content size.\n */\n stickyEnd?: PaddingValue | undefined;\n\n /**\n * Extra padding (display pixels - DU) at the start of the flow (e.g. non-sticky header).\n */\n flowPaddingStart?: PaddingValue | undefined;\n\n /**\n * Extra padding (DU) at the end of the flow (e.g. non-sticky footer).\n */\n flowPaddingEnd?: PaddingValue | undefined;\n}\n\n/** Help provide axis specific information to the scrollbar. */\nexport type ScrollAxis = 'vertical' | 'horizontal';\n\n/** Properties for the `VirtualScrollbar` component. */\nexport interface VirtualScrollbarProps {\n /**\n * The axis for this scrollbar.\n * - 'vertical': Vertical scrollbar.\n * - 'horizontal': Horizontal scrollbar.\n * @default 'vertical'\n */\n axis?: ScrollAxis;\n\n /**\n * Total size of the scrollable content in pixels.\n */\n totalSize: number;\n\n /**\n * Current scroll position in pixels.\n */\n position: number;\n\n /**\n * Viewport size in pixels.\n */\n viewportSize: number;\n\n /**\n * Function to scroll to a specific pixel offset on this axis.\n * @param offset - The pixel offset to scroll to.\n */\n scrollToOffset?: (offset: number) => void;\n\n /**\n * The ID of the container element this scrollbar controls.\n */\n containerId?: string;\n\n /**\n * Whether the scrollbar is in Right-to-Left (RTL) mode.\n * @default false\n */\n isRtl?: boolean;\n\n /**\n * Accessible label for the scrollbar.\n */\n ariaLabel?: string;\n}\n\n/** Properties passed to the 'scrollbar' scoped slot. */\nexport interface ScrollbarSlotProps {\n /** The axis for this scrollbar. */\n axis: ScrollAxis;\n /** Current scroll position as a percentage (0 to 1). */\n positionPercent: number;\n /** Viewport size as a percentage of total size (0 to 1). */\n viewportPercent: number;\n /** Calculated thumb size as a percentage of the track size (0 to 100). */\n thumbSizePercent: number;\n /** Calculated thumb position as a percentage of the track size (0 to 100). */\n thumbPositionPercent: number;\n\n /**\n * Attributes and event listeners to be bound to the scrollbar track element.\n * Use `v-bind=\"trackProps\"` on your track element.\n */\n trackProps: Record<string, unknown>;\n\n /**\n * Attributes and event listeners to be bound to the scrollbar thumb element.\n * Use `v-bind=\"thumbProps\"` on your thumb element.\n */\n thumbProps: Record<string, unknown>;\n\n /**\n * Grouped props for the `VirtualScrollbar` component.\n * Useful for passing directly to `<VirtualScrollbar v-bind=\"scrollbarProps\" />`.\n */\n scrollbarProps: VirtualScrollbarProps;\n\n /** Whether the thumb is currently being dragged. */\n isDragging: boolean;\n}\n\n/** Properties passed to the 'item' scoped slot. */\nexport interface ItemSlotProps<T = unknown> {\n /** The original data item being rendered. */\n item: T;\n /** The 0-based index of the item. */\n index: number;\n /** Helper to get ARIA attributes for the item. */\n getItemAriaProps: GetItemAriaProps;\n /** Information about the currently visible range of columns. */\n columnRange: ColumnRange;\n /** Helper to get the current calculated width of any column index. */\n getColumnWidth: (index: number) => number;\n /** Helper to get ARIA attributes for a cell. */\n getCellAriaProps: (colIndex: number) => Record<string, string | number | undefined>;\n /** Vertical gap between items. */\n gap: number;\n /** Horizontal gap between columns. */\n columnGap: number;\n /** Whether this item index is configured as sticky. */\n isSticky?: boolean | undefined;\n /** Whether this item is currently in a sticky state at the edge. */\n isStickyActive?: boolean | undefined;\n /** Whether this item is currently in a sticky state at the horizontal edge. */\n isStickyActiveX?: boolean | undefined;\n /** Whether this item is currently in a sticky state at the vertical edge. */\n isStickyActiveY?: boolean | undefined;\n /** The calculated pixel offset relative to the items wrapper in display pixels (DU). */\n offset: {\n /** Horizontal offset (left) in DU. */\n x: number;\n /** Vertical offset (top) in DU. */\n y: number;\n };\n}\n\n/** Configuration properties for the `VirtualScroll` component. */\nexport interface VirtualScrollComponentProps<T = unknown> extends VirtualScrollBaseProps<T> {\n /** The HTML tag to use for the root 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 /** Whether the content in the 'header' slot is sticky. */\n stickyHeader?: boolean;\n /** Whether the content in the 'footer' slot is sticky. */\n stickyFooter?: boolean;\n /** Whether to use virtual scrollbars for styling purposes. */\n virtualScrollbar?: boolean;\n}\n\n/** Exposed methods and properties of the `VirtualScroll` component instance. */\nexport interface VirtualScrollInstance<T = unknown> extends VirtualScrollComponentProps<T> {\n /** Detailed information about the current scroll state. */\n scrollDetails: ScrollDetails<T>;\n /** Information about the current visible range of columns. */\n columnRange: ScrollDetails<T>[ 'columnRange' ];\n /** Helper to get the width of a specific column. */\n getColumnWidth: (index: number) => number;\n /** Helper to get the height of a specific row. */\n getRowHeight: (index: number) => number;\n /** Helper to get ARIA attributes for a cell. */\n getCellAriaProps: (colIndex: number) => Record<string, string | number | undefined>;\n /** Helper to get ARIA attributes for an item. */\n getItemAriaProps: (index: number) => Record<string, string | number | undefined>;\n /** The ARIA role of the items wrapper. */\n wrapperRole: string | null;\n /** The ARIA role of each cell. */\n cellRole: string | null;\n /** Helper to get the virtual offset of a specific row. */\n getRowOffset: (index: number) => number;\n /** Helper to get the virtual offset of a specific column. */\n getColumnOffset: (index: number) => number;\n /** Helper to get the virtual offset of a specific item. */\n getItemOffset: (index: number) => number;\n /** Helper to get the size of a specific item along the scroll axis. */\n getItemSize: (index: number) => number;\n /** Programmatically scroll to a specific row and/or column. */\n scrollToIndex: (rowIndex?: number | null, colIndex?: number | null, options?: ScrollAlignment | ScrollAlignmentOptions | ScrollToIndexOptions) => void;\n /** Programmatically scroll to a specific pixel offset. */\n scrollToOffset: (x?: number | null, y?: number | null, options?: { behavior?: 'auto' | 'smooth'; }) => void;\n /** Resets all dynamic measurements and re-initializes from props. */\n refresh: () => void;\n /** Immediately stops any currently active smooth scroll animation and clears pending corrections. */\n stopProgrammaticScroll: () => void;\n /** Detects the current direction (LTR/RTL) of the scroll container. */\n updateDirection: () => void;\n /** Whether the scroll container is in Right-to-Left (RTL) mode. */\n isRtl: boolean;\n /** Whether the component has finished its first client - side mount and hydration. */\n isHydrated: boolean;\n /** Coordinate scaling factor for X axis. */\n scaleX: number;\n /** Coordinate scaling factor for Y axis. */\n scaleY: number;\n /** Physical width of the content in the DOM (clamped to browser limits). */\n renderedWidth: number;\n /** Physical height of the content in the DOM (clamped to browser limits). */\n renderedHeight: number;\n /** Absolute offset of the component within its container. */\n componentOffset: Point;\n /** Properties for the vertical scrollbar. */\n scrollbarPropsVertical: ScrollbarSlotProps | null;\n /** Properties for the horizontal scrollbar. */\n scrollbarPropsHorizontal: ScrollbarSlotProps | null;\n}\n\n/** Parameters for calculating the scroll target position. */\nexport interface ScrollTargetParams {\n /** Row index to target. */\n rowIndex: number | null | undefined;\n /** Column index to target. */\n colIndex: number | null | undefined;\n /** Scroll options. */\n options?: ScrollAlignment | ScrollAlignmentOptions | ScrollToIndexOptions | undefined;\n /** Current scroll direction. */\n direction: ScrollDirection;\n /** Current viewport width. */\n viewportWidth: number;\n /** Current viewport height. */\n viewportHeight: number;\n /** Current total estimated width. */\n totalWidth: number;\n /** Current total estimated height. */\n totalHeight: number;\n /** Item gap. */\n gap: number;\n /** Column gap. */\n columnGap: number;\n /** Fixed item size. */\n fixedSize: number | null;\n /** Fixed column width. */\n fixedWidth: number | null;\n /** Current relative X scroll. */\n relativeScrollX: number;\n /** Current relative Y scroll. */\n relativeScrollY: number;\n /** Resolver for item height. */\n getItemSizeY: (index: number) => number;\n /** Resolver for item width. */\n getItemSizeX: (index: number) => number;\n /** Prefix sum resolver for item height. */\n getItemQueryY: (index: number) => number;\n /** Prefix sum resolver for item width. */\n getItemQueryX: (index: number) => number;\n /** Resolver for column size. */\n getColumnSize: (index: number) => number;\n /** Prefix sum resolver for column width. */\n getColumnQuery: (index: number) => number;\n /** Coordinate scaling factor for X axis. */\n scaleX: number;\n /** Coordinate scaling factor for Y axis. */\n scaleY: number;\n /** Host offset on X axis in display pixels. */\n hostOffsetX: number;\n /** Host offset on Y axis in display pixels. */\n hostOffsetY: number;\n /** List of sticky indices. */\n stickyIndices?: number[] | undefined;\n /** Sticky start offset on X axis. */\n stickyStartX?: number | undefined;\n /** Sticky start offset on Y axis. */\n stickyStartY?: number | undefined;\n /** Sticky end offset on X axis. */\n stickyEndX?: number | undefined;\n /** Sticky end offset on Y axis. */\n stickyEndY?: number | undefined;\n /** Flow padding start on X axis. */\n flowPaddingStartX?: number | undefined;\n /** Flow padding start on Y axis. */\n flowPaddingStartY?: number | undefined;\n /** Scroll padding start on X axis. */\n paddingStartX?: number | undefined;\n /** Scroll padding start on Y axis. */\n paddingStartY?: number | undefined;\n /** Scroll padding end on X axis. */\n paddingEndX?: number | undefined;\n /** Scroll padding end on Y axis. */\n paddingEndY?: number | undefined;\n}\n\n/** Calculated scroll target result. */\nexport interface ScrollTargetResult {\n /** Target relative horizontal position. */\n targetX: number;\n /** Target relative vertical position. */\n targetY: number;\n /** Resolved width of the target item. */\n itemWidth: number;\n /** Resolved height of the target item. */\n itemHeight: number;\n /** Effective alignment used for X axis. */\n effectiveAlignX: ScrollAlignment;\n /** Effective alignment used for Y axis. */\n effectiveAlignY: ScrollAlignment;\n}\n\n/** Parameters for calculating the visible range of items. */\nexport interface RangeParams {\n /** Scroll direction. */\n direction: ScrollDirection;\n /** Relative horizontal scroll position. */\n relativeScrollX: number;\n /** Relative vertical scroll position. */\n relativeScrollY: number;\n /** Usable viewport width. */\n usableWidth: number;\n /** Usable viewport height. */\n usableHeight: number;\n /** Total item count. */\n itemsLength: number;\n /** Buffer items before. */\n bufferBefore: number;\n /** Buffer items after. */\n bufferAfter: number;\n /** Item gap. */\n gap: number;\n /** Column gap. */\n columnGap: number;\n /** Fixed item size. */\n fixedSize: number | null;\n /** Binary search for row index. */\n findLowerBoundY: (offset: number) => number;\n /** Binary search for row index (horizontal). */\n findLowerBoundX: (offset: number) => number;\n /** Prefix sum for row height. */\n queryY: (index: number) => number;\n /** Prefix sum for row width. */\n queryX: (index: number) => number;\n}\n\n/** Parameters for calculating the visible range of columns in grid mode. */\nexport interface ColumnRangeParams {\n /** Column count. */\n columnCount: number;\n /** Relative horizontal scroll position. */\n relativeScrollX: number;\n /** Usable viewport width. */\n usableWidth: number;\n /** Column buffer count. */\n colBuffer: number;\n /** Fixed column width. */\n fixedWidth: number | null;\n /** Column gap. */\n columnGap: number;\n /** Binary search for column index. */\n findLowerBound: (offset: number) => number;\n /** Prefix sum for column width. */\n query: (index: number) => number;\n /** Resolver for total column width. */\n totalColsQuery: () => number;\n}\n\n/** Parameters for calculating sticky item offsets. */\nexport interface StickyParams {\n /** Item index. */\n index: number;\n /** Is sticky configured. */\n isSticky: boolean;\n /** Scroll direction. */\n direction: ScrollDirection;\n /** Relative horizontal scroll. */\n relativeScrollX: number;\n /** Relative vertical scroll. */\n relativeScrollY: number;\n /** Original X offset. */\n originalX: number;\n /** Original Y offset. */\n originalY: number;\n /** Current width. */\n width: number;\n /** Current height. */\n height: number;\n /** All sticky indices. */\n stickyIndices: number[];\n /** Fixed item size. */\n fixedSize: number | null;\n /** Fixed column width. */\n fixedWidth: number | null;\n /** Item gap. */\n gap: number;\n /** Column gap. */\n columnGap: number;\n /** Prefix sum resolver for rows. */\n getItemQueryY: (index: number) => number;\n /** Prefix sum resolver for rows (horizontal). */\n getItemQueryX: (index: number) => number;\n}\n\n/** Parameters for calculating an item's position and size. */\nexport interface ItemPositionParams {\n /** Item index. */\n index: number;\n /** Scroll direction. */\n direction: ScrollDirection;\n /** Fixed item size. */\n fixedSize: number | null;\n /** Item gap. */\n gap: number;\n /** Column gap. */\n columnGap: number;\n /** Usable viewport width. */\n usableWidth: number;\n /** Usable viewport height. */\n usableHeight: number;\n /** Total estimated width. */\n totalWidth: number;\n /** Prefix sum for row height. */\n queryY: (idx: number) => number;\n /** Prefix sum for row width. */\n queryX: (idx: number) => number;\n /** Height resolver. */\n getSizeY: (idx: number) => number;\n /** Width resolver. */\n getSizeX: (idx: number) => number;\n /** Current column range (for grid mode). */\n columnRange?: ColumnRange | undefined;\n}\n\n/** Parameters for calculating an item's style object. */\nexport interface ItemStyleParams<T = unknown> {\n /** The rendered item state. */\n item: RenderedItem<T>;\n /** Scroll direction. */\n direction: ScrollDirection;\n /** Configured item size logic. */\n itemSize: number | ((item: T, index: number) => number) | null | undefined;\n /** Parent container tag. */\n containerTag: string;\n /** Padding start on X axis. */\n paddingStartX: number;\n /** Padding start on Y axis. */\n paddingStartY: number;\n /** Hydration state. */\n isHydrated: boolean;\n /** Whether the container is in Right-to-Left (RTL) mode. */\n isRtl: boolean;\n}\n\n/** Parameters for calculating the total size of the scrollable area. */\nexport interface TotalSizeParams {\n /** The scroll direction. */\n direction: ScrollDirection;\n /** The number of items in the list. */\n itemsLength: number;\n /** The number of columns (for grid mode). */\n columnCount: number;\n /** The fixed size of items, if applicable. */\n fixedSize: number | null;\n /** The fixed width of columns, if applicable. */\n fixedWidth: number | null;\n /** The gap between items. */\n gap: number;\n /** The gap between columns. */\n columnGap: number;\n /** Usable viewport width. */\n usableWidth: number;\n /** Usable viewport height. */\n usableHeight: number;\n /** Function to query the prefix sum of item heights. */\n queryY: (index: number) => number;\n /** Function to query the prefix sum of item widths. */\n queryX: (index: number) => number;\n /** Function to query the prefix sum of column widths. */\n queryColumn: (index: number) => number;\n}\n","/**\n * Utilities for scroll management and element type detection.\n * Provides helper functions for checking Window and Body elements,\n * and a universal scrollTo function.\n */\n\nimport type { ScrollDirection, ScrollToIndexOptions } from '../types';\n\n/* global ScrollToOptions */\n\n/**\n * Maximum size (in pixels) for an element that most browsers can handle reliably.\n * Beyond this size, we use scaling for the scrollable area.\n * @default 10000000\n */\nexport const BROWSER_MAX_SIZE = 10000000;\n\n/**\n * Checks if the container is the window object.\n *\n * @param container - The container element or window to check. Optional.\n * @returns `true` if the container is the global window object.\n */\nexport function isWindow(container?: HTMLElement | Window | null): container is Window {\n return container === null || container === document.documentElement || (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. Optional.\n * @returns `true` if the container is the `<body>` element.\n */\nexport function isBody(container?: HTMLElement | Window | null): container is HTMLElement {\n return container != null && 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. Optional.\n * @returns `true` if the container is window or body.\n */\nexport function isWindowLike(container?: HTMLElement | Window | null): 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. Optional.\n * @returns `true` if the container is an `HTMLElement`.\n */\nexport function isElement(container?: HTMLElement | Window | null): container is HTMLElement {\n return container != null && '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. Optional.\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 != null && 'scrollLeft' in target;\n}\n\n/**\n * Universal scroll function that handles both Window and HTMLElements.\n *\n * @param container - The container to scroll.\n * @param options - Scroll options.\n */\nexport function scrollTo(container: HTMLElement | Window | null | undefined, options: ScrollToOptions) {\n if (isWindow(container)) {\n window.scrollTo(options);\n } else if (container != null && isScrollableElement(container)) {\n if (typeof container.scrollTo === 'function') {\n container.scrollTo(options);\n } else {\n if (options.left !== undefined) {\n container.scrollLeft = options.left;\n }\n if (options.top !== undefined) {\n container.scrollTop = options.top;\n }\n }\n }\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). Optional.\n * @param direction - The current scroll direction.\n * @returns The horizontal padding in pixels.\n */\nexport function getPaddingX(p?: number | { x?: number; y?: number; } | null, 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). Optional.\n * @param direction - The current scroll direction.\n * @returns The vertical padding in pixels.\n */\nexport function getPaddingY(p?: number | { x?: number; y?: number; } | null, 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 ScrollDirection,\n ScrollTargetParams,\n ScrollTargetResult,\n ScrollToIndexOptions,\n SnapMode,\n SSRRange,\n StickyParams,\n TotalSizeParams,\n} from '../types';\n\nimport { BROWSER_MAX_SIZE, isScrollToIndexOptions } from './scroll';\n\n// --- Internal Helper Types ---\n\ninterface GenericRangeParams {\n scrollPos: number;\n containerSize: number;\n count: number;\n bufferBefore: number;\n bufferAfter: number;\n gap: number;\n fixedSize: number | null;\n findLowerBound: (offset: number) => number;\n query: (index: number) => number;\n}\n\ninterface AxisAlignmentParams {\n align: ScrollAlignment;\n targetPos: number;\n itemSize: number;\n scrollPos: number;\n viewSize: number;\n stickyOffsetStart: number;\n stickyOffsetEnd: number;\n}\n\n/** Snap result. */\nexport interface SnapResult {\n /** Target index. */\n index: number;\n /** Alignment. */\n align: 'start' | 'center' | 'end';\n}\n\n// --- Internal Helpers ---\n\n/**\n * Generic range calculation for a single axis (row or column).\n *\n * @param params - Range parameters.\n * @param params.scrollPos - Virtual scroll position.\n * @param params.containerSize - Usable viewport size.\n * @param params.count - Total item count.\n * @param params.bufferBefore - Buffer items before.\n * @param params.bufferAfter - Buffer items after.\n * @param params.gap - Item gap.\n * @param params.fixedSize - Fixed item size.\n * @param params.findLowerBound - Binary search for index.\n * @param params.query - Prefix sum for index.\n * @returns Start and end indices.\n */\nfunction calculateGenericRange({\n scrollPos,\n containerSize,\n count,\n bufferBefore,\n bufferAfter,\n gap,\n fixedSize,\n findLowerBound,\n query,\n}: GenericRangeParams) {\n let start = 0;\n let end = count;\n const endOffset = scrollPos + containerSize;\n\n if (fixedSize !== null) {\n const step = fixedSize + gap;\n start = Math.floor(scrollPos / step);\n end = Math.ceil(endOffset / step);\n } else {\n start = findLowerBound(scrollPos);\n end = findLowerBound(endOffset);\n if (end < count && query(end) < endOffset) {\n end++;\n }\n }\n\n return {\n start: Math.max(0, start - bufferBefore),\n end: Math.min(count, end + bufferAfter),\n };\n}\n\n/**\n * Binary search for the next sticky index after the current index.\n *\n * @param stickyIndices - Sorted array of sticky indices.\n * @param index - Current index.\n * @returns Next sticky index or undefined.\n */\nfunction findNextStickyIndex(stickyIndices: number[], index: number): number | undefined {\n let low = 0;\n let high = stickyIndices.length - 1;\n let nextStickyIdx: number | undefined;\n\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 return nextStickyIdx;\n}\n\n/**\n * Binary search for the previous sticky index before the current index.\n *\n * @param stickyIndices - Sorted array of sticky indices.\n * @param index - Current index.\n * @returns Previous sticky index or undefined.\n */\nexport function findPrevStickyIndex(stickyIndices: number[], index: number): number | undefined {\n let low = 0;\n let high = stickyIndices.length - 1;\n let prevStickyIdx: number | undefined;\n\n while (low <= high) {\n const mid = (low + high) >>> 1;\n if (stickyIndices[ mid ]! < index) {\n prevStickyIdx = stickyIndices[ mid ];\n low = mid + 1;\n } else {\n high = mid - 1;\n }\n }\n return prevStickyIdx;\n}\n\n/**\n * Generic alignment calculation for a single axis.\n *\n * @param params - Alignment parameters.\n * @param params.align - Desired alignment.\n * @param params.targetPos - Virtual item position.\n * @param params.itemSize - Virtual item size.\n * @param params.scrollPos - Virtual scroll position.\n * @param params.viewSize - Full viewport size.\n * @param params.stickyOffsetStart - Dynamic sticky offset at start.\n * @param params.stickyOffsetEnd - Sticky offset at end.\n * @returns Target scroll position and effective alignment.\n */\nfunction calculateAxisAlignment({\n align,\n targetPos,\n itemSize,\n scrollPos,\n viewSize,\n stickyOffsetStart,\n stickyOffsetEnd,\n}: AxisAlignmentParams) {\n const targetStart = targetPos - stickyOffsetStart;\n const targetEnd = targetPos - (viewSize - stickyOffsetEnd - itemSize);\n\n if (align === 'start') {\n return { target: targetStart, effectiveAlign: 'start' as const };\n }\n if (align === 'center') {\n return {\n target: targetPos - stickyOffsetStart - (viewSize - stickyOffsetStart - stickyOffsetEnd - itemSize) / 2,\n effectiveAlign: 'center' as const,\n };\n }\n if (align === 'end') {\n return { target: targetEnd, effectiveAlign: 'end' as const };\n }\n\n if (isItemVisible(targetPos, itemSize, scrollPos, viewSize, stickyOffsetStart, stickyOffsetEnd)) {\n return { target: scrollPos, effectiveAlign: 'auto' as const };\n }\n\n const usableSize = viewSize - stickyOffsetStart - stickyOffsetEnd;\n\n if (itemSize <= usableSize) {\n return targetPos < scrollPos + stickyOffsetStart\n ? {\n target: targetStart,\n effectiveAlign: 'start' as const,\n }\n : {\n target: targetEnd,\n effectiveAlign: 'end' as const,\n };\n }\n\n return Math.abs(targetStart - scrollPos) < Math.abs(targetEnd - scrollPos)\n ? {\n target: targetStart,\n effectiveAlign: 'start' as const,\n }\n : {\n target: targetEnd,\n effectiveAlign: 'end' as const,\n };\n}\n\n/**\n * Helper to calculate total size for a single axis.\n *\n * @param count - Item count.\n * @param fixedSize - Fixed size if any.\n * @param gap - Gap size.\n * @param query - Prefix sum resolver.\n * @returns Total size.\n */\nexport function calculateAxisSize(\n count: number,\n fixedSize: number | null,\n gap: number,\n query: (index: number) => number,\n): number {\n if (count <= 0) {\n return 0;\n }\n if (fixedSize !== null) {\n return Math.max(0, count * (fixedSize + gap) - gap);\n }\n return Math.max(0, query(count) - gap);\n}\n\n/**\n * Helper to calculate size for a range on a single axis.\n *\n * @param start - Start index.\n * @param end - End index.\n * @param fixedSize - Fixed size if any.\n * @param gap - Gap size.\n * @param query - Prefix sum resolver.\n * @returns Range size.\n */\nexport function calculateRangeSize(\n start: number,\n end: number,\n fixedSize: number | null,\n gap: number,\n query: (index: number) => number,\n): number {\n const count = end - start;\n if (count <= 0) {\n return 0;\n }\n if (fixedSize !== null) {\n return Math.max(0, count * (fixedSize + gap) - gap);\n }\n return Math.max(0, query(end) - query(start) - gap);\n}\n\n/**\n * Helper to calculate offset at a specific index.\n *\n * @param index - Item index.\n * @param fixedSize - Fixed size if any.\n * @param gap - Gap size.\n * @param query - Prefix sum resolver.\n * @returns Offset at index.\n */\nexport function calculateOffsetAt(\n index: number,\n fixedSize: number | null,\n gap: number,\n query: (index: number) => number,\n): number {\n if (fixedSize !== null) {\n return index * (fixedSize + gap);\n }\n return query(index);\n}\n\n/**\n * Helper to calculate target scroll position for a single axis.\n *\n * @param params - Axis target parameters.\n * @param params.index - Row/column index.\n * @param params.align - Desired alignment.\n * @param params.viewSize - Full viewport size.\n * @param params.scrollPos - Virtual scroll position.\n * @param params.fixedSize - Fixed item size.\n * @param params.gap - Item gap.\n * @param params.query - Prefix sum resolver.\n * @param params.getSize - Item size resolver.\n * @param params.stickyIndices - Sticky indices.\n * @param params.stickyStart - Sticky start element size.\n * @param params.stickyEnd - Sticky end element size.\n * @returns Target position, item size and effective alignment.\n */\nfunction calculateAxisTarget({\n index,\n align,\n viewSize,\n scrollPos,\n fixedSize,\n gap,\n query,\n getSize,\n stickyIndices,\n stickyStart,\n stickyEnd = 0,\n}: {\n index: number;\n align: ScrollAlignment;\n viewSize: number;\n scrollPos: number;\n fixedSize: number | null;\n gap: number;\n query: (idx: number) => number;\n getSize: (idx: number) => number;\n stickyIndices?: number[] | undefined;\n stickyStart: number;\n stickyEnd?: number;\n}) {\n let stickyOffsetStart = stickyStart;\n if (stickyIndices && stickyIndices.length > 0) {\n const activeStickyIdx = findPrevStickyIndex(stickyIndices, index);\n if (activeStickyIdx !== undefined) {\n stickyOffsetStart += calculateAxisSize(1, fixedSize, 0, () => getSize(activeStickyIdx));\n }\n }\n\n const itemPos = (fixedSize !== null ? index * (fixedSize + gap) : query(index));\n const itemSize = fixedSize !== null ? fixedSize : getSize(index) - gap;\n\n const { target, effectiveAlign } = calculateAxisAlignment({\n align,\n targetPos: itemPos,\n itemSize,\n scrollPos,\n viewSize,\n stickyOffsetStart,\n stickyOffsetEnd: stickyEnd,\n });\n\n return { target, itemSize, effectiveAlign };\n}\n\n/**\n * Helper to calculate sticky state for a single axis.\n *\n * @param scrollPos - Virtual scroll position.\n * @param originalPos - Original virtual item position.\n * @param size - Virtual item size.\n * @param index - Item index.\n * @param stickyIndices - All sticky indices.\n * @param getNextStickyPos - Resolver for the next sticky item's position.\n * @returns Sticky state for this axis.\n */\nfunction calculateAxisSticky(\n scrollPos: number,\n originalPos: number,\n size: number,\n index: number,\n stickyIndices: number[],\n getNextStickyPos: (idx: number) => number,\n) {\n if (scrollPos <= originalPos) {\n return { isActive: false, offset: 0 };\n }\n\n const nextStickyIdx = findNextStickyIndex(stickyIndices, index);\n if (nextStickyIdx === undefined) {\n return { isActive: true, offset: 0 };\n }\n\n const nextStickyPos = getNextStickyPos(nextStickyIdx);\n if (scrollPos >= nextStickyPos) {\n return { isActive: false, offset: 0 };\n }\n\n return {\n isActive: true,\n offset: Math.max(0, Math.min(size, nextStickyPos - scrollPos)) - size,\n };\n}\n\n// --- Exported Functions ---\n\n/**\n * Determines if an item is visible within the usable viewport.\n *\n * @param itemPos - Virtual start position of the item (VU).\n * @param itemSize - Virtual size of the item (VU).\n * @param scrollPos - Virtual scroll position (VU).\n * @param viewSize - Full size of the viewport (VU).\n * @param stickyOffsetStart - Dynamic offset from sticky items at start (VU).\n * @param stickyOffsetEnd - Offset from sticky items at end (VU).\n * @returns True if visible.\n */\nexport function isItemVisible(\n itemPos: number,\n itemSize: number,\n scrollPos: number,\n viewSize: number,\n stickyOffsetStart: number = 0,\n stickyOffsetEnd: number = 0,\n): boolean {\n const usableStart = scrollPos + stickyOffsetStart;\n const usableEnd = scrollPos + viewSize - stickyOffsetEnd;\n const usableSize = viewSize - stickyOffsetStart - stickyOffsetEnd;\n\n if (itemSize <= usableSize) {\n return itemPos >= usableStart - 0.5 && (itemPos + itemSize) <= usableEnd + 0.5;\n }\n return itemPos <= usableStart + 0.5 && (itemPos + itemSize) >= usableEnd - 0.5;\n}\n\n/**\n * Calculates the coordinate scaling factor between virtual and display units.\n *\n * @param isWindow - If the container is the window.\n * @param totalSize - Total virtual size (VU).\n * @param viewportSize - Viewport size (DU).\n * @returns Scaling factor (VU/DU).\n */\nexport function calculateScale(isWindow: boolean, totalSize: number, viewportSize: number): number {\n if (isWindow || totalSize <= BROWSER_MAX_SIZE) {\n return 1;\n }\n const displaySize = Math.min(totalSize, BROWSER_MAX_SIZE);\n const realRange = totalSize - viewportSize;\n const displayRange = displaySize - viewportSize;\n return displayRange > 0 ? realRange / displayRange : 1;\n}\n\n/**\n * Calculates the physical size to be rendered in the DOM.\n *\n * @param isWindow - If the container is the window.\n * @param totalSize - Total virtual size (VU).\n * @returns Physical size (DU).\n */\nexport function calculateRenderedSize(isWindow: boolean, totalSize: number): number {\n return isWindow ? totalSize : Math.min(totalSize, BROWSER_MAX_SIZE);\n}\n\n/**\n * Maps a display scroll position to a virtual content position.\n *\n * @param displayPos - Display pixel position (DU).\n * @param hostOffset - Offset of the host element in display pixels (DU).\n * @param scale - Coordinate scaling factor (VU/DU).\n * @returns Virtual content position (VU).\n */\nexport function displayToVirtual(displayPos: number, hostOffset: number, scale: number): number {\n return (displayPos - hostOffset) * scale;\n}\n\n/**\n * Maps a virtual content position to a display scroll position.\n *\n * @param virtualPos - Virtual content position (VU).\n * @param hostOffset - Offset of the host element in display pixels (DU).\n * @param scale - Coordinate scaling factor (VU/DU).\n * @returns Display pixel position (DU).\n */\nexport function virtualToDisplay(virtualPos: number, hostOffset: number, scale: number): number {\n return virtualPos / scale + hostOffset;\n}\n\n/**\n * Calculates the target scroll position (relative to content) for a given row/column index and alignment.\n *\n * @param params - Scroll target parameters.\n * @param params.rowIndex - Row index to target.\n * @param params.colIndex - Column index to target.\n * @param params.options - Scroll options including alignment.\n * @param params.direction - Current scroll direction.\n * @param params.viewportWidth - Full viewport width (DU).\n * @param params.viewportHeight - Full viewport height (DU).\n * @param params.totalWidth - Total estimated width (VU).\n * @param params.totalHeight - Total estimated height (VU).\n * @param params.gap - Item gap (VU).\n * @param params.columnGap - Column gap (VU).\n * @param params.fixedSize - Fixed item size (VU).\n * @param params.fixedWidth - Fixed column width (VU).\n * @param params.relativeScrollX - Current relative X scroll (VU).\n * @param params.relativeScrollY - Current relative Y scroll (VU).\n * @param params.getItemSizeY - Resolver for item height (VU).\n * @param params.getItemSizeX - Resolver for item width (VU).\n * @param params.getItemQueryY - Prefix sum resolver for item height (VU).\n * @param params.getItemQueryX - Prefix sum resolver for item width (VU).\n * @param params.getColumnSize - Resolver for column size (VU).\n * @param params.getColumnQuery - Prefix sum resolver for column width (VU).\n * @param params.scaleX - Coordinate scaling factor for X axis.\n * @param params.scaleY - Coordinate scaling factor for Y axis.\n * @param params.hostOffsetX - Display pixels offset of items wrapper on X axis (DU).\n * @param params.hostOffsetY - Display pixels offset of items wrapper on Y axis (DU).\n * @param params.flowPaddingStartX - Display pixels padding at flow start on X axis (DU).\n * @param params.flowPaddingStartY - Display pixels padding at flow start on Y axis (DU).\n * @param params.paddingStartX - Display pixels padding at scroll start on X axis (DU).\n * @param params.paddingStartY - Display pixels padding at scroll start on Y axis (DU).\n * @param params.paddingEndX - Display pixels padding at scroll end on X axis (DU).\n * @param params.paddingEndY - Display pixels padding at scroll end on Y axis (DU).\n * @param params.stickyIndices - List of sticky indices.\n * @param params.stickyStartX - Sticky start offset on X axis (DU).\n * @param params.stickyStartY - Sticky start offset on Y axis (DU).\n * @param params.stickyEndX - Sticky end offset on X axis (DU).\n * @param params.stickyEndY - Sticky end offset on Y axis (DU).\n * @returns The target X and Y positions (VU) and item dimensions (VU).\n * @see ScrollTargetParams\n * @see ScrollTargetResult\n */\nexport function calculateScrollTarget({\n rowIndex,\n colIndex,\n options,\n direction,\n viewportWidth,\n viewportHeight,\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 scaleX,\n scaleY,\n hostOffsetX,\n hostOffsetY,\n stickyIndices,\n stickyStartX = 0,\n stickyStartY = 0,\n stickyEndX = 0,\n stickyEndY = 0,\n flowPaddingStartX = 0,\n flowPaddingStartY = 0,\n paddingStartX = 0,\n paddingStartY = 0,\n paddingEndX = 0,\n paddingEndY = 0,\n}: ScrollTargetParams): ScrollTargetResult {\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 = (align && typeof align === 'object' ? align.x : align) || 'auto';\n const alignY = (align && typeof align === 'object' ? align.y : align) || 'auto';\n\n let targetX = relativeScrollX;\n let targetY = relativeScrollY;\n let itemWidth = 0;\n let itemHeight = 0;\n let effectiveAlignX: ScrollAlignment = 'auto';\n let effectiveAlignY: ScrollAlignment = 'auto';\n\n // Clamp to valid range\n const rWidth = scaleX === 1 ? totalWidth : BROWSER_MAX_SIZE;\n const rHeight = scaleY === 1 ? totalHeight : BROWSER_MAX_SIZE;\n\n const maxDisplayX = Math.max(0, hostOffsetX + rWidth - viewportWidth);\n const maxDisplayY = Math.max(0, hostOffsetY + rHeight - viewportHeight);\n\n // maxTarget should be in virtual internalScroll coordinates\n const maxTargetX = (maxDisplayX - hostOffsetX) * scaleX;\n const maxTargetY = (maxDisplayY - hostOffsetY) * scaleY;\n\n const itemsStartVirtualX = flowPaddingStartX + stickyStartX + paddingStartX;\n const itemsStartVirtualY = flowPaddingStartY + stickyStartY + paddingStartY;\n\n // Y calculation\n if (rowIndex != null) {\n const res = calculateAxisTarget({\n index: rowIndex,\n align: alignY as ScrollAlignment,\n viewSize: viewportHeight,\n scrollPos: relativeScrollY,\n fixedSize,\n gap,\n query: getItemQueryY,\n getSize: getItemSizeY,\n stickyIndices,\n stickyStart: stickyStartY + paddingStartY,\n stickyEnd: stickyEndY + paddingEndY,\n });\n targetY = res.target + itemsStartVirtualY;\n itemHeight = res.itemSize;\n effectiveAlignY = res.effectiveAlign;\n }\n\n // X calculation\n if (colIndex != null) {\n const isGrid = direction === 'both';\n const isHorizontal = direction === 'horizontal';\n const res = calculateAxisTarget({\n index: colIndex,\n align: alignX as ScrollAlignment,\n viewSize: viewportWidth,\n scrollPos: relativeScrollX,\n fixedSize: isGrid ? fixedWidth : fixedSize,\n gap: (isGrid || isHorizontal) ? columnGap : gap,\n query: isGrid ? getColumnQuery : getItemQueryX,\n getSize: isGrid ? getColumnSize : getItemSizeX,\n stickyIndices,\n stickyStart: stickyStartX + paddingStartX,\n stickyEnd: stickyEndX + paddingEndX,\n });\n targetX = res.target + itemsStartVirtualX;\n itemWidth = res.itemSize;\n effectiveAlignX = res.effectiveAlign;\n }\n\n targetX = Math.max(0, Math.min(targetX, maxTargetX));\n targetY = Math.max(0, Math.min(targetY, maxTargetY));\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 - Range parameters.\n * @param params.direction - Scroll direction.\n * @param params.relativeScrollX - Virtual horizontal position (VU).\n * @param params.relativeScrollY - Virtual vertical position (VU).\n * @param params.usableWidth - Usable viewport width (VU).\n * @param params.usableHeight - Usable viewport height (VU).\n * @param params.itemsLength - Total item count.\n * @param params.bufferBefore - Buffer items before.\n * @param params.bufferAfter - Buffer items after.\n * @param params.gap - Item gap (VU).\n * @param params.columnGap - Column gap (VU).\n * @param params.fixedSize - Fixed item size (VU).\n * @param params.findLowerBoundY - Resolver for vertical index.\n * @param params.findLowerBoundX - Resolver for horizontal index.\n * @param params.queryY - Resolver for vertical offset (VU).\n * @param params.queryX - Resolver for horizontal offset (VU).\n * @returns The start and end indices of the items to render.\n * @see RangeParams\n */\nexport function calculateRange({\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}: RangeParams) {\n const isVertical = direction === 'vertical' || direction === 'both';\n\n return calculateGenericRange({\n scrollPos: isVertical ? relativeScrollY : relativeScrollX,\n containerSize: isVertical ? usableHeight : usableWidth,\n count: itemsLength,\n bufferBefore,\n bufferAfter,\n gap: isVertical ? gap : columnGap,\n fixedSize,\n findLowerBound: isVertical ? findLowerBoundY : findLowerBoundX,\n query: isVertical ? queryY : queryX,\n });\n}\n\n/**\n * Calculates the range of columns to render for bidirectional scroll.\n *\n * @param params - Column range parameters.\n * @param params.columnCount - Total column count.\n * @param params.relativeScrollX - Virtual horizontal position (VU).\n * @param params.usableWidth - Usable viewport width (VU).\n * @param params.colBuffer - Column buffer size.\n * @param params.fixedWidth - Fixed column width (VU).\n * @param params.columnGap - Column gap (VU).\n * @param params.findLowerBound - Resolver for column index.\n * @param params.query - Resolver for column offset (VU).\n * @param params.totalColsQuery - Resolver for total width (VU).\n * @returns The start and end indices and paddings for columns (VU).\n * @see ColumnRangeParams\n * @see ColumnRange\n */\nexport function calculateColumnRange({\n columnCount,\n relativeScrollX,\n usableWidth,\n colBuffer,\n fixedWidth,\n columnGap,\n findLowerBound,\n query,\n totalColsQuery,\n}: ColumnRangeParams) {\n if (!columnCount) {\n return { start: 0, end: 0, padStart: 0, padEnd: 0 };\n }\n\n const { start, end } = calculateGenericRange({\n scrollPos: relativeScrollX,\n containerSize: usableWidth,\n count: columnCount,\n bufferBefore: colBuffer,\n bufferAfter: colBuffer,\n gap: columnGap,\n fixedSize: fixedWidth,\n findLowerBound,\n query,\n });\n\n const safeStart = start;\n const safeEnd = end;\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 contentEnd = fixedWidth !== null\n ? (safeEnd * (fixedWidth + columnGap) - (safeEnd > 0 ? columnGap : 0))\n : (query(safeEnd) - (safeEnd > 0 ? columnGap : 0));\n\n return {\n start: safeStart,\n end: safeEnd,\n padStart,\n padEnd: Math.max(0, totalWidth - contentEnd),\n };\n}\n\n/**\n * Calculates the sticky state and offset for a single item.\n *\n * @param params - Sticky item parameters.\n * @param params.index - Item index.\n * @param params.isSticky - If configured as sticky.\n * @param params.direction - Scroll direction.\n * @param params.relativeScrollX - Virtual horizontal position (VU).\n * @param params.relativeScrollY - Virtual vertical position (VU).\n * @param params.originalX - Virtual original X position (VU).\n * @param params.originalY - Virtual original Y position (VU).\n * @param params.width - Virtual item width (VU).\n * @param params.height - Virtual item height (VU).\n * @param params.stickyIndices - All sticky indices.\n * @param params.fixedSize - Fixed item size (VU).\n * @param params.gap - Item gap (VU).\n * @param params.columnGap - Column gap (VU).\n * @param params.getItemQueryY - Resolver for vertical offset (VU).\n * @param params.getItemQueryX - Resolver for horizontal offset (VU).\n * @returns Sticky state and offset (VU).\n * @see StickyParams\n */\nexport function calculateStickyItem({\n index,\n isSticky,\n direction,\n relativeScrollX,\n relativeScrollY,\n originalX,\n originalY,\n width,\n height,\n stickyIndices,\n fixedSize,\n gap,\n columnGap,\n getItemQueryY,\n getItemQueryX,\n}: StickyParams) {\n let isStickyActiveX = false;\n let isStickyActiveY = false;\n const stickyOffset = { x: 0, y: 0 };\n\n if (!isSticky) {\n return { isStickyActiveX, isStickyActiveY, isStickyActive: false, stickyOffset };\n }\n\n // Y Axis (Sticky Rows)\n if (direction === 'vertical' || direction === 'both') {\n const res = calculateAxisSticky(\n relativeScrollY,\n originalY,\n height,\n index,\n stickyIndices,\n (nextIdx) => (fixedSize !== null ? nextIdx * (fixedSize + gap) : getItemQueryY(nextIdx)),\n );\n isStickyActiveY = res.isActive;\n stickyOffset.y = res.offset;\n }\n\n // X Axis (Sticky Columns / Items)\n if (direction === 'horizontal') {\n const res = calculateAxisSticky(\n relativeScrollX,\n originalX,\n width,\n index,\n stickyIndices,\n (nextIdx) => (fixedSize !== null ? nextIdx * (fixedSize + columnGap) : getItemQueryX(nextIdx)),\n );\n\n if (res.isActive) {\n isStickyActiveX = true;\n stickyOffset.x = res.offset;\n }\n }\n\n return {\n isStickyActiveX,\n isStickyActiveY,\n isStickyActive: isStickyActiveX || isStickyActiveY,\n stickyOffset,\n };\n}\n\n/**\n * Calculates the position and size of a single item.\n *\n * @param params - Item position parameters.\n * @param params.index - Item index.\n * @param params.direction - Scroll direction.\n * @param params.fixedSize - Fixed item size (VU).\n * @param params.gap - Item gap (VU).\n * @param params.columnGap - Column gap (VU).\n * @param params.usableWidth - Usable viewport width (VU).\n * @param params.usableHeight - Usable viewport height (VU).\n * @param params.totalWidth - Total estimated width (VU).\n * @param params.queryY - Resolver for vertical offset (VU).\n * @param params.queryX - Resolver for horizontal offset (VU).\n * @param params.getSizeY - Resolver for height (VU).\n * @param params.getSizeX - Resolver for width (VU).\n * @param params.columnRange - Current column range (for grid mode).\n * @returns Item position and size (VU).\n * @see ItemPositionParams\n */\nexport function calculateItemPosition({\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 columnRange,\n}: ItemPositionParams) {\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 if (direction === 'both' && columnRange) {\n y = fixedSize !== null ? index * (fixedSize + gap) : queryY(index);\n height = fixedSize !== null ? fixedSize : getSizeY(index) - gap;\n x = columnRange.padStart;\n width = Math.max(0, totalWidth - columnRange.padStart - columnRange.padEnd);\n } else {\n y = 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 - Item style parameters.\n * @param params.item - Rendered item state.\n * @param params.direction - Scroll direction.\n * @param params.itemSize - Virtual item size (VU).\n * @param params.containerTag - Container HTML tag.\n * @param params.paddingStartX - Horizontal virtual padding (DU).\n * @param params.paddingStartY - Vertical virtual padding (DU).\n * @param params.isHydrated - If mounted and hydrated.\n * @param params.isRtl - If in RTL mode.\n * @returns Style object.\n * @see ItemStyleParams\n */\nexport function calculateItemStyle<T = unknown>({\n item,\n direction,\n itemSize,\n containerTag,\n paddingStartX,\n paddingStartY,\n isHydrated,\n isRtl,\n}: ItemStyleParams<T>) {\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 const isStickingVertically = item.isStickyActiveY ?? (item.isStickyActive && (isVertical || isBoth));\n const isStickingHorizontally = item.isStickyActiveX ?? (item.isStickyActive && isHorizontal);\n\n const tx = isRtl\n ? -(isStickingHorizontally ? item.stickyOffset.x : item.offset.x)\n : (isStickingHorizontally ? item.stickyOffset.x : item.offset.x);\n const ty = isStickingVertically ? item.stickyOffset.y : item.offset.y;\n\n if (item.isStickyActive || item.isStickyActiveX || item.isStickyActiveY) {\n style.insetBlockStart = isStickingVertically ? `${ paddingStartY }px` : 'auto';\n style.insetInlineStart = isStickingHorizontally ? `${ paddingStartX }px` : 'auto';\n style.transform = `translate(${ tx }px, ${ ty }px)`;\n } else {\n style.transform = `translate(${ tx }px, ${ item.offset.y }px)`;\n }\n }\n\n return style;\n}\n\n/**\n * Calculates the SSR offsets for items based on the configured SSR range and direction.\n *\n * @param direction - Scroll direction.\n * @param ssrRange - SSR configuration.\n * @param fixedItemSize - Fixed item size (VU).\n * @param fixedColumnWidth - Fixed column width (VU).\n * @param gap - Item gap (VU).\n * @param columnGap - Column gap (VU).\n * @param queryY - Resolver for vertical offset (VU).\n * @param queryX - Resolver for horizontal offset (VU).\n * @param queryColumn - Resolver for column offset (VU).\n * @returns Offset X and Y (VU).\n */\nexport function calculateSSROffsets(\n direction: ScrollDirection,\n ssrRange: SSRRange,\n fixedItemSize: number | null,\n fixedColumnWidth: number | null,\n gap: number,\n columnGap: number,\n queryY: (index: number) => number,\n queryX: (index: number) => number,\n queryColumn: (index: number) => number,\n): { x: number; y: number; } {\n const ssrStartRow = ssrRange.start || 0;\n const ssrStartCol = ssrRange.colStart || 0;\n\n let x = 0;\n const y = (direction !== 'horizontal')\n ? calculateOffsetAt(ssrStartRow, fixedItemSize, gap, queryY)\n : 0;\n\n if (direction === 'horizontal') {\n x = calculateOffsetAt(ssrStartCol, fixedItemSize, columnGap, queryX);\n } else if (direction === 'both') {\n x = calculateOffsetAt(ssrStartCol, fixedColumnWidth, columnGap, queryColumn);\n }\n\n return { x, y };\n}\n\n/**\n * Detects how many items were prepended to the list based on item identity.\n *\n * @param oldItems - Previous items list.\n * @param newItems - Current items list.\n * @returns Number of prepended items.\n */\nexport function calculatePrependCount<T>(oldItems: T[], newItems: T[]): number {\n if (oldItems.length === 0 || newItems.length <= oldItems.length) {\n return 0;\n }\n const oldFirstItem = oldItems[ 0 ];\n if (oldFirstItem === undefined) {\n return 0;\n }\n const limit = newItems.length - oldItems.length;\n for (let i = 1; i <= limit; i++) {\n if (newItems[ i ] === oldFirstItem) {\n return i;\n }\n }\n return 0;\n}\n\n/**\n * Calculates the total width and height of the virtualized content.\n *\n * @param params - Total size parameters.\n * @param params.direction - Scroll direction.\n * @param params.itemsLength - Total item count.\n * @param params.columnCount - Column count.\n * @param params.fixedSize - Fixed item size (VU).\n * @param params.fixedWidth - Fixed column width (VU).\n * @param params.gap - Item gap (VU).\n * @param params.columnGap - Column gap (VU).\n * @param params.usableWidth - Usable viewport width (VU).\n * @param params.usableHeight - Usable viewport height (VU).\n * @param params.queryY - Resolver for vertical offset (VU).\n * @param params.queryX - Resolver for horizontal offset (VU).\n * @param params.queryColumn - Resolver for column offset (VU).\n * @returns Total width and height (VU).\n * @see TotalSizeParams\n */\nexport function calculateTotalSize({\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}: TotalSizeParams) {\n const isBoth = direction === 'both';\n const isHorizontal = direction === 'horizontal';\n\n let width = 0;\n let height = 0;\n\n if (isBoth) {\n width = calculateAxisSize(columnCount, fixedWidth, columnGap, queryColumn);\n height = calculateAxisSize(itemsLength, fixedSize, gap, queryY);\n } else if (isHorizontal) {\n width = calculateAxisSize(itemsLength, fixedSize, columnGap, queryX);\n height = usableHeight;\n } else {\n width = usableWidth;\n height = calculateAxisSize(itemsLength, fixedSize, gap, queryY);\n }\n\n return {\n width: isBoth ? Math.max(width, usableWidth) : width,\n height: isBoth ? Math.max(height, usableHeight) : height,\n };\n}\n\n/**\n * Calculates the next step in an inertia animation.\n *\n * @param velocity - Current velocity.\n * @param velocity.x - Horizontal velocity.\n * @param velocity.y - Vertical velocity.\n * @param friction - Friction coefficient (0-1).\n * @param frameTime - Time elapsed since last frame in ms (default 16ms).\n * @returns Next velocity and distance to move.\n */\nexport function calculateInertiaStep(\n velocity: { x: number; y: number; },\n friction: number,\n frameTime: number = 16,\n) {\n const nextVelocity = {\n x: velocity.x * friction,\n y: velocity.y * friction,\n };\n\n return {\n nextVelocity,\n delta: {\n x: nextVelocity.x * frameTime,\n y: nextVelocity.y * frameTime,\n },\n };\n}\n\n/**\n * Calculates instantaneous velocity from position change.\n *\n * @param lastPos - Previous position.\n * @param lastPos.x - Horizontal position.\n * @param lastPos.y - Vertical position.\n * @param currentPos - Current position.\n * @param currentPos.x - Horizontal position.\n * @param currentPos.y - Vertical position.\n * @param dt - Time delta in ms.\n * @returns Calculated velocity {x, y}.\n */\nexport function calculateInstantaneousVelocity(\n lastPos: { x: number; y: number; },\n currentPos: { x: number; y: number; },\n dt: number,\n) {\n if (dt <= 0) {\n return { x: 0, y: 0 };\n }\n return {\n x: (lastPos.x - currentPos.x) / dt,\n y: (lastPos.y - currentPos.y) / dt,\n };\n}\n\n/**\n * Resolves the target index and alignment for snapping on a single axis.\n *\n * @param mode - The snap mode ('start', 'center', 'end', 'auto').\n * @param dir - The scroll direction on this axis.\n * @param currentIdx - The current first visible index.\n * @param currentEndIdx - The current last visible index.\n * @param relScroll - The current virtual scroll offset.\n * @param viewSize - The viewport dimension.\n * @param count - Total number of items/columns.\n * @param getSize - Resolver for item size.\n * @param getQuery - Resolver for item prefix sum.\n * @param getIndexAt - Resolver for index at a virtual offset.\n * @returns Snap result or null.\n */\nexport function resolveSnap(\n mode: SnapMode,\n dir: 'start' | 'end' | null,\n currentIdx: number,\n currentEndIdx: number,\n relScroll: number,\n viewSize: number,\n count: number,\n getSize: (i: number) => number,\n getQuery: (i: number) => number,\n getIndexAt: (o: number) => number,\n): SnapResult | null {\n let effectiveMode = mode;\n if (mode === 'auto') {\n if (dir === 'start') {\n effectiveMode = 'end';\n } else if (dir === 'end') {\n effectiveMode = 'start';\n } else {\n return null;\n }\n }\n\n if (effectiveMode === 'start') {\n const size = getSize(currentIdx);\n // Ignore items larger than viewport to prevent jarring jumps\n if (size > viewSize) {\n return null;\n }\n const visibleAmount = getQuery(currentIdx) + size - relScroll;\n return {\n index: Math.min(count - 1, visibleAmount / size >= 0.5 ? currentIdx : currentIdx + 1),\n align: 'start' as const,\n };\n }\n if (effectiveMode === 'end') {\n const size = getSize(currentEndIdx);\n if (size > viewSize) {\n return null;\n }\n const visibleAmount = relScroll + viewSize - getQuery(currentEndIdx);\n return {\n index: Math.max(0, visibleAmount / size >= 0.5 ? currentEndIdx : currentEndIdx - 1),\n align: 'end' as const,\n };\n }\n if (effectiveMode === 'center') {\n const center = relScroll + viewSize / 2;\n const idx = Math.max(0, Math.min(count - 1, getIndexAt(center)));\n const size = getSize(idx);\n if (size > viewSize) {\n return null;\n }\n return { index: idx, align: 'center' as const };\n }\n return null;\n}\n\n/**\n * Helper to get the index at a virtual offset.\n *\n * @param offset - Virtual offset.\n * @param fixedSize - Fixed size if any.\n * @param gap - Gap size.\n * @param findLowerBound - Binary search resolver.\n * @returns Index at offset.\n */\nexport function calculateIndexAt(\n offset: number,\n fixedSize: number | null,\n gap: number,\n findLowerBound: (offset: number) => number,\n): number {\n const step = (fixedSize || 0) + gap;\n if (fixedSize !== null && step > 0) {\n return Math.floor(offset / step);\n }\n return findLowerBound(offset);\n}\n","/**\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 { VirtualScrollProps } from '../types';\nimport type { MaybeRefOrGetter } from 'vue';\n\nimport { computed, ref, shallowRef, toValue } from 'vue';\n\nimport { DEFAULT_COLUMN_WIDTH } from '../types';\nimport { FenwickTree } from '../utils/fenwick-tree';\nimport { calculatePrependCount } from '../utils/virtual-scroll-logic';\n\n/**\n * Configuration properties for the `useVirtualScrollSizes` composable.\n */\nexport interface UseVirtualScrollSizesProps<T> {\n /** Reactive reference to the virtual scroll configuration. */\n props: VirtualScrollProps<T>;\n /** Whether items have dynamic heights/widths. */\n isDynamicItemSize: boolean;\n /** Whether columns have dynamic widths. */\n isDynamicColumnWidth: boolean;\n /** Fallback size for items before they are measured. */\n defaultSize: number;\n /** Fixed item size if applicable. */\n fixedItemSize: number | null;\n /** Scroll direction. */\n direction: 'vertical' | 'horizontal' | 'both';\n}\n\n/**\n * Composable for managing item and column sizes using Fenwick Trees.\n * Handles prefix sum calculations, size updates, and scroll correction adjustments.\n */\nexport function useVirtualScrollSizes<T>(\n propsInput: MaybeRefOrGetter<UseVirtualScrollSizesProps<T>>,\n) {\n const props = computed(() => toValue(propsInput));\n\n /** Fenwick Tree for item widths (horizontal mode). */\n const itemSizesX = new FenwickTree(props.value.props.items?.length || 0);\n /** Fenwick Tree for item heights (vertical/both mode). */\n const itemSizesY = new FenwickTree(props.value.props.items?.length || 0);\n /** Fenwick Tree for column widths (grid mode). */\n const columnSizes = new FenwickTree(props.value.props.columnCount || 0);\n\n /** Track which columns have been measured (Uint8Array for memory efficiency). */\n const measuredColumns = shallowRef(new Uint8Array(0));\n /** Track which item widths have been measured. */\n const measuredItemsX = shallowRef(new Uint8Array(0));\n /** Track which item heights have been measured. */\n const measuredItemsY = shallowRef(new Uint8Array(0));\n\n /** Reactive flag to trigger re-computations when trees update. */\n const treeUpdateFlag = ref(0);\n /** Whether the initial sizes have been calculated. */\n const sizesInitialized = ref(false);\n\n /** Cached list of previous items to detect prepending and shift measurements. */\n let lastItems: T[] = [];\n\n const getItemBaseSize = (item: T, index: number) => (typeof props.value.props.itemSize === 'function' ? (props.value.props.itemSize as (item: T, index: number) => number)(item, index) : props.value.defaultSize);\n\n /**\n * Internal helper to get the size of an item or column at a specific index.\n *\n * @param index - The item/column index.\n * @param sizeProp - The size property from props (number, array, or function).\n * @param defaultSize - Fallback size.\n * @param gap - Spacing between items.\n * @param tree - FenwickTree for this axis.\n * @param isX - True for horizontal axis.\n * @returns The calculated size in VU.\n */\n const getSizeAt = (\n index: number,\n sizeProp: number | number[] | ((...args: any[]) => number) | null | undefined,\n defaultSize: number,\n gap: number,\n tree: FenwickTree,\n isX: boolean,\n ) => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n if (typeof sizeProp === 'number' && sizeProp > 0) {\n return sizeProp;\n }\n if (isX && Array.isArray(sizeProp) && sizeProp.length > 0) {\n const val = sizeProp[ index % sizeProp.length ];\n return (val != null && val > 0) ? val : defaultSize;\n }\n if (typeof sizeProp === 'function') {\n const item = props.value.props.items[ index ];\n return (isX && props.value.direction !== 'both') || !isX\n ? (item !== undefined ? sizeProp(item, index) : defaultSize)\n : (sizeProp as (i: number) => number)(index);\n }\n const val = tree.get(index);\n return val > 0 ? val - gap : defaultSize;\n };\n\n /**\n * Resizes internal arrays and Fenwick Trees while preserving existing measurements.\n *\n * @param len - New item count.\n * @param colCount - New column count.\n */\n const resizeMeasurements = (len: number, colCount: number) => {\n itemSizesX.resize(len);\n itemSizesY.resize(len);\n columnSizes.resize(colCount);\n\n if (measuredItemsX.value.length !== len) {\n const newMeasuredX = new Uint8Array(len);\n newMeasuredX.set(measuredItemsX.value.subarray(0, Math.min(len, measuredItemsX.value.length)));\n measuredItemsX.value = newMeasuredX;\n }\n if (measuredItemsY.value.length !== len) {\n const newMeasuredY = new Uint8Array(len);\n newMeasuredY.set(measuredItemsY.value.subarray(0, Math.min(len, measuredItemsY.value.length)));\n measuredItemsY.value = newMeasuredY;\n }\n if (measuredColumns.value.length !== colCount) {\n const newMeasuredCols = new Uint8Array(colCount);\n newMeasuredCols.set(measuredColumns.value.subarray(0, Math.min(colCount, measuredColumns.value.length)));\n measuredColumns.value = newMeasuredCols;\n }\n };\n\n /**\n * Initializes prefix sum trees from props (fixed sizes, width arrays, or functions).\n */\n const initializeMeasurements = () => {\n const propsVal = props.value.props;\n const newItems = propsVal.items;\n const len = newItems.length;\n const colCount = propsVal.columnCount || 0;\n const gap = propsVal.gap || 0;\n const columnGap = propsVal.columnGap || 0;\n const cw = propsVal.columnWidth;\n\n let colNeedsRebuild = false;\n let itemsNeedRebuild = false;\n\n // Initialize columns\n if (colCount > 0) {\n for (let i = 0; i < colCount; i++) {\n const currentW = columnSizes.get(i);\n const isMeasured = measuredColumns.value[ i ] === 1;\n\n if (!props.value.isDynamicColumnWidth || (!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 ] || propsVal.defaultColumnWidth || DEFAULT_COLUMN_WIDTH;\n } else if (typeof cw === 'function') {\n baseWidth = cw(i);\n } else {\n baseWidth = propsVal.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.value[ i ] = props.value.isDynamicColumnWidth ? 0 : 1;\n colNeedsRebuild = true;\n } else if (!props.value.isDynamicColumnWidth) {\n measuredColumns.value[ i ] = 1;\n }\n }\n }\n }\n\n // Initialize items\n for (let i = 0; i < len; i++) {\n const item = propsVal.items[ i ];\n const currentX = itemSizesX.get(i);\n const currentY = itemSizesY.get(i);\n const isMeasuredX = measuredItemsX.value[ i ] === 1;\n const isMeasuredY = measuredItemsY.value[ i ] === 1;\n\n if (props.value.direction === 'horizontal') {\n if (!props.value.isDynamicItemSize || (!isMeasuredX && currentX === 0)) {\n const baseSize = getItemBaseSize(item as T, i);\n const targetX = baseSize + columnGap;\n if (Math.abs(currentX - targetX) > 0.5) {\n itemSizesX.set(i, targetX);\n measuredItemsX.value[ i ] = props.value.isDynamicItemSize ? 0 : 1;\n itemsNeedRebuild = true;\n } else if (!props.value.isDynamicItemSize) {\n measuredItemsX.value[ i ] = 1;\n }\n }\n } else if (currentX !== 0) {\n itemSizesX.set(i, 0);\n measuredItemsX.value[ i ] = 0;\n itemsNeedRebuild = true;\n }\n\n if (props.value.direction !== 'horizontal') {\n if (!props.value.isDynamicItemSize || (!isMeasuredY && currentY === 0)) {\n const baseSize = getItemBaseSize(item as T, i);\n const targetY = baseSize + gap;\n if (Math.abs(currentY - targetY) > 0.5) {\n itemSizesY.set(i, targetY);\n measuredItemsY.value[ i ] = props.value.isDynamicItemSize ? 0 : 1;\n itemsNeedRebuild = true;\n } else if (!props.value.isDynamicItemSize) {\n measuredItemsY.value[ i ] = 1;\n }\n }\n } else if (currentY !== 0) {\n itemSizesY.set(i, 0);\n measuredItemsY.value[ i ] = 0;\n itemsNeedRebuild = true;\n }\n }\n\n if (colNeedsRebuild) {\n columnSizes.rebuild();\n }\n if (itemsNeedRebuild) {\n itemSizesX.rebuild();\n itemSizesY.rebuild();\n }\n };\n\n /**\n * Initializes or updates sizes based on current props and items.\n * Handles prepending of items by shifting existing measurements.\n *\n * @param onScrollCorrection - Callback to adjust scroll position when items are prepended.\n */\n const initializeSizes = (onScrollCorrection?: (addedX: number, addedY: number) => void) => {\n const propsVal = props.value.props;\n const newItems = propsVal.items;\n const len = newItems.length;\n const colCount = propsVal.columnCount || 0;\n\n resizeMeasurements(len, colCount);\n\n const prependCount = propsVal.restoreScrollOnPrepend\n ? calculatePrependCount(lastItems, newItems)\n : 0;\n\n if (prependCount > 0) {\n itemSizesX.shift(prependCount);\n itemSizesY.shift(prependCount);\n\n const newMeasuredX = new Uint8Array(len);\n const newMeasuredY = new Uint8Array(len);\n newMeasuredX.set(measuredItemsX.value.subarray(0, Math.min(len - prependCount, measuredItemsX.value.length)), prependCount);\n newMeasuredY.set(measuredItemsY.value.subarray(0, Math.min(len - prependCount, measuredItemsY.value.length)), prependCount);\n measuredItemsX.value = newMeasuredX;\n measuredItemsY.value = newMeasuredY;\n\n // Calculate added size\n const gap = propsVal.gap || 0;\n const columnGap = propsVal.columnGap || 0;\n let addedX = 0;\n let addedY = 0;\n\n for (let i = 0; i < prependCount; i++) {\n const size = getItemBaseSize(newItems[ i ] as T, i);\n if (props.value.direction === 'horizontal') {\n addedX += size + columnGap;\n } else { addedY += size + gap; }\n }\n\n if ((addedX > 0 || addedY > 0) && onScrollCorrection) {\n onScrollCorrection(addedX, addedY);\n }\n }\n\n initializeMeasurements();\n\n lastItems = [ ...newItems ];\n sizesInitialized.value = true;\n treeUpdateFlag.value++;\n };\n\n /**\n * Updates the size of multiple items in the Fenwick tree.\n *\n * @param updates - Array of updates.\n * @param getRowIndexAt - Helper to get row index at offset (for scroll correction check).\n * @param getColIndexAt - Helper to get col index at offset.\n * @param relativeScrollX - Current relative scroll X.\n * @param relativeScrollY - Current relative scroll Y.\n * @param onScrollCorrection - Callback to adjust scroll position.\n */\n const updateItemSizes = (\n updates: Array<{ index: number; inlineSize: number; blockSize: number; element?: HTMLElement | undefined; }>,\n getRowIndexAt: (offset: number) => number,\n getColIndexAt: (offset: number) => number,\n relativeScrollX: number,\n relativeScrollY: number,\n onScrollCorrection: (deltaX: number, deltaY: number) => void,\n ) => {\n let needUpdate = false;\n let deltaX = 0;\n let deltaY = 0;\n const propsVal = props.value.props;\n const gap = propsVal.gap || 0;\n const columnGap = propsVal.columnGap || 0;\n\n const firstRowIndex = getRowIndexAt(props.value.direction === 'horizontal' ? relativeScrollX : relativeScrollY);\n const firstColIndex = getColIndexAt(relativeScrollX);\n\n const isHorizontalMode = props.value.direction === 'horizontal';\n const isBothMode = props.value.direction === 'both';\n\n const processedRows = new Set<number>();\n const processedCols = new Set<number>();\n\n const tryUpdateColumn = (colIdx: number, width: number) => {\n if (colIdx >= 0 && colIdx < (propsVal.columnCount || 0) && !processedCols.has(colIdx)) {\n processedCols.add(colIdx);\n const oldW = columnSizes.get(colIdx);\n const targetW = width + columnGap;\n\n if (!measuredColumns.value[ colIdx ] || Math.abs(oldW - targetW) > 0.1) {\n const d = targetW - oldW;\n if (Math.abs(d) > 0.1) {\n columnSizes.update(colIdx, d);\n needUpdate = true;\n if (colIdx < firstColIndex && oldW > 0) {\n deltaX += d;\n }\n }\n measuredColumns.value[ colIdx ] = 1;\n }\n }\n };\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 = props.value.isDynamicItemSize || typeof propsVal.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.value[ index ] || Math.abs(targetWidth - oldWidth) > 0.1) {\n const d = targetWidth - oldWidth;\n itemSizesX.update(index, d);\n measuredItemsX.value[ index ] = 1;\n needUpdate = true;\n if (index < firstRowIndex && oldWidth > 0) {\n deltaX += d;\n }\n }\n }\n if (!isHorizontalMode) {\n const oldHeight = itemSizesY.get(index);\n const targetHeight = blockSize + gap;\n\n if (!measuredItemsY.value[ index ] || Math.abs(targetHeight - oldHeight) > 0.1) {\n const d = targetHeight - oldHeight;\n itemSizesY.update(index, d);\n measuredItemsY.value[ index ] = 1;\n needUpdate = true;\n if (index < firstRowIndex && oldHeight > 0) {\n deltaY += d;\n }\n }\n }\n }\n\n // Dynamic column width measurement\n const isColMeasurable = props.value.isDynamicColumnWidth || typeof propsVal.columnWidth === 'function';\n if (\n isBothMode\n && element\n && propsVal.columnCount\n && isColMeasurable\n && (inlineSize > 0 || element.dataset.colIndex === undefined)\n ) {\n const colIndexAttr = element.dataset.colIndex;\n if (colIndexAttr != null) {\n tryUpdateColumn(Number.parseInt(colIndexAttr, 10), inlineSize);\n } else {\n // If the element is a row, try to find cells with data-col-index\n const cells = 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 tryUpdateColumn(colIndex, child.getBoundingClientRect().width);\n }\n }\n }\n }\n\n if (needUpdate) {\n treeUpdateFlag.value++;\n if (deltaX !== 0 || deltaY !== 0) {\n onScrollCorrection(deltaX, deltaY);\n }\n }\n };\n\n /**\n * Resets all dynamic measurements and re-initializes from current props.\n *\n * @param onScrollCorrection - Callback to adjust scroll position.\n */\n const refresh = (onScrollCorrection?: (addedX: number, addedY: number) => void) => {\n itemSizesX.resize(0);\n itemSizesY.resize(0);\n columnSizes.resize(0);\n measuredColumns.value.fill(0);\n measuredItemsX.value.fill(0);\n measuredItemsY.value.fill(0);\n initializeSizes(onScrollCorrection);\n };\n\n return {\n /** Fenwick Tree for horizontal item sizes. */\n itemSizesX,\n /** Fenwick Tree for vertical item sizes. */\n itemSizesY,\n /** Fenwick Tree for column widths. */\n columnSizes,\n /** Measured item widths. */\n measuredItemsX,\n /** Measured item heights. */\n measuredItemsY,\n /** Measured column widths. */\n measuredColumns,\n /** Flag that updates when any tree changes. */\n treeUpdateFlag,\n /** Whether sizes have been initialized. */\n sizesInitialized,\n /** Base size of an item from props. */\n getItemBaseSize,\n /** Helper to get current size at index. */\n getSizeAt,\n /** Initialize or update sizes from props. */\n initializeSizes,\n /** Update sizes of multiple items from measurements. */\n updateItemSizes,\n /** Reset all measurements. */\n refresh,\n };\n}\n","import type {\n RenderedItem,\n ScrollAlignment,\n ScrollAlignmentOptions,\n ScrollDetails,\n ScrollDirection,\n ScrollToIndexOptions,\n VirtualScrollProps,\n} from '../types';\nimport type { MaybeRefOrGetter } from 'vue';\n\n/* global ScrollToOptions */\nimport { computed, getCurrentInstance, nextTick, onMounted, onUnmounted, reactive, ref, toValue, watch } from 'vue';\n\nimport {\n DEFAULT_BUFFER,\n DEFAULT_COLUMN_WIDTH,\n DEFAULT_ITEM_SIZE,\n} from '../types';\nimport { getPaddingX, getPaddingY, isElement, isScrollableElement, isScrollToIndexOptions, isWindowLike, scrollTo } from '../utils/scroll';\nimport {\n calculateColumnRange,\n calculateIndexAt,\n calculateItemPosition,\n calculateOffsetAt,\n calculateRange,\n calculateRangeSize,\n calculateRenderedSize,\n calculateScale,\n calculateScrollTarget,\n calculateSSROffsets,\n calculateStickyItem,\n calculateTotalSize,\n displayToVirtual,\n findPrevStickyIndex,\n resolveSnap,\n virtualToDisplay,\n} from '../utils/virtual-scroll-logic';\nimport { useVirtualScrollSizes } from './useVirtualScrollSizes';\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 propsInput - The configuration properties. Can be a plain object, a Ref, or a getter function.\n * @see VirtualScrollProps\n */\nexport function useVirtualScroll<T = unknown>(propsInput: MaybeRefOrGetter<VirtualScrollProps<T>>) {\n const props = computed(() => toValue(propsInput));\n\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 isRtl = ref(false);\n const viewportWidth = ref(0);\n const viewportHeight = ref(0);\n const hostOffset = reactive({ x: 0, y: 0 });\n const hostRefOffset = reactive({ x: 0, y: 0 });\n let scrollTimeout: ReturnType<typeof setTimeout> | undefined;\n\n const isProgrammaticScroll = ref(false);\n const internalScrollX = ref(0);\n const internalScrollY = ref(0);\n /** The last recorded virtual X position, used to detect scroll direction for snapping. */\n let lastInternalX = 0;\n /** The last recorded virtual Y position, used to detect scroll direction for snapping. */\n let lastInternalY = 0;\n /** The current horizontal scroll direction ('start' towards left/logical start, 'end' towards right/logical end). */\n let scrollDirectionX: 'start' | 'end' | null = null;\n /** The current vertical scroll direction ('start' towards top, 'end' towards bottom). */\n let scrollDirectionY: 'start' | 'end' | null = null;\n\n let computedStyle: CSSStyleDeclaration | null = null;\n\n /**\n * Detects the current direction (LTR/RTL) of the scroll container.\n */\n const updateDirection = () => {\n if (typeof window === 'undefined') {\n return;\n }\n const container = props.value.container || props.value.hostRef || window;\n const el = isElement(container) ? container : document.documentElement;\n\n computedStyle = window.getComputedStyle(el);\n\n const newRtl = computedStyle.direction === 'rtl';\n if (isRtl.value !== newRtl) {\n isRtl.value = newRtl;\n }\n };\n\n // --- Computed Config ---\n const direction = computed(() => [ 'vertical', 'horizontal', 'both' ].includes(props.value.direction as string) ? props.value.direction as ScrollDirection : 'vertical' as ScrollDirection);\n\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 // --- Size Management ---\n const {\n itemSizesX,\n itemSizesY,\n columnSizes,\n measuredColumns,\n measuredItemsY,\n treeUpdateFlag,\n getSizeAt,\n initializeSizes,\n updateItemSizes: coreUpdateItemSizes,\n refresh: coreRefresh,\n } = useVirtualScrollSizes(computed(() => ({\n props: props.value,\n isDynamicItemSize: isDynamicItemSize.value,\n isDynamicColumnWidth: isDynamicColumnWidth.value,\n defaultSize: defaultSize.value,\n fixedItemSize: fixedItemSize.value,\n direction: direction.value,\n })));\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 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 stickyStartX = computed(() => getPaddingX(props.value.stickyStart, props.value.direction));\n const stickyEndX = computed(() => getPaddingX(props.value.stickyEnd, props.value.direction));\n const stickyStartY = computed(() => getPaddingY(props.value.stickyStart, props.value.direction));\n const stickyEndY = computed(() => getPaddingY(props.value.stickyEnd, props.value.direction));\n\n const flowStartX = computed(() => getPaddingX(props.value.flowPaddingStart, props.value.direction));\n const flowEndX = computed(() => getPaddingX(props.value.flowPaddingEnd, props.value.direction));\n const flowStartY = computed(() => getPaddingY(props.value.flowPaddingStart, props.value.direction));\n const flowEndY = computed(() => getPaddingY(props.value.flowPaddingEnd, props.value.direction));\n\n const usableWidth = computed(() => viewportWidth.value - (direction.value !== 'vertical' ? (stickyStartX.value + stickyEndX.value) : 0));\n\n const usableHeight = computed(() => viewportHeight.value - (direction.value !== 'horizontal' ? (stickyStartY.value + stickyEndY.value) : 0));\n\n // --- Size Calculations ---\n /**\n * Total size (width and height) of all items in the scrollable area.\n */\n const totalSize = computed(() => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n return calculateTotalSize({\n direction: direction.value,\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 });\n });\n\n const isWindowContainer = computed(() => isWindowLike(props.value.container));\n\n const virtualWidth = computed(() => totalSize.value.width + paddingStartX.value + paddingEndX.value);\n const virtualHeight = computed(() => totalSize.value.height + paddingStartY.value + paddingEndY.value);\n\n const totalWidth = computed(() => (flowStartX.value + stickyStartX.value + stickyEndX.value + flowEndX.value + virtualWidth.value));\n\n const totalHeight = computed(() => (flowStartY.value + stickyStartY.value + stickyEndY.value + flowEndY.value + virtualHeight.value));\n\n const componentOffset = reactive({\n x: computed(() => Math.max(0, hostOffset.x - (flowStartX.value + stickyStartX.value))),\n y: computed(() => Math.max(0, hostOffset.y - (flowStartY.value + stickyStartY.value))),\n });\n\n const renderedWidth = computed(() => calculateRenderedSize(isWindowContainer.value, totalWidth.value));\n const renderedHeight = computed(() => calculateRenderedSize(isWindowContainer.value, totalHeight.value));\n\n const renderedVirtualWidth = computed(() => calculateRenderedSize(isWindowContainer.value, virtualWidth.value));\n const renderedVirtualHeight = computed(() => calculateRenderedSize(isWindowContainer.value, virtualHeight.value));\n\n const scaleX = computed(() => calculateScale(isWindowContainer.value, totalWidth.value, viewportWidth.value));\n const scaleY = computed(() => calculateScale(isWindowContainer.value, totalHeight.value, viewportHeight.value));\n\n const relativeScrollX = computed(() => {\n if (direction.value === 'vertical') {\n return 0;\n }\n const flowPaddingX = flowStartX.value + stickyStartX.value + paddingStartX.value;\n return internalScrollX.value - flowPaddingX;\n });\n\n const relativeScrollY = computed(() => {\n if (direction.value === 'horizontal') {\n return 0;\n }\n const flowPaddingY = flowStartY.value + stickyStartY.value + paddingStartY.value;\n return internalScrollY.value - flowPaddingY;\n });\n\n /**\n * Returns the currently calculated width for a specific column index, taking measurements and gaps into account.\n *\n * @param index - The column index.\n * @returns The width in pixels (excluding gap).\n */\n const getColumnWidth = (index: number) => {\n if (direction.value === 'both') {\n return getSizeAt(\n index,\n props.value.columnWidth,\n props.value.defaultColumnWidth || DEFAULT_COLUMN_WIDTH,\n props.value.columnGap || 0,\n columnSizes,\n true,\n );\n }\n return getSizeAt(\n index,\n props.value.itemSize,\n props.value.defaultItemSize || DEFAULT_ITEM_SIZE,\n props.value.columnGap || 0,\n itemSizesX,\n true,\n );\n };\n\n /**\n * Returns the currently calculated height for a specific row index, taking measurements and gaps into account.\n *\n * @param index - The row index.\n * @returns The height in pixels (excluding gap).\n */\n const getRowHeight = (index: number) => {\n if (direction.value === 'horizontal') {\n return usableHeight.value;\n }\n\n return getSizeAt(\n index,\n props.value.itemSize,\n props.value.defaultItemSize || DEFAULT_ITEM_SIZE,\n props.value.gap || 0,\n itemSizesY,\n false,\n );\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. Optional.\n * @param colIndex - The column index to scroll to. Pass null to only scroll vertically. Optional.\n * @param options - Scroll options including alignment ('start', 'center', 'end', 'auto') and behavior ('auto', 'smooth').\n * Defaults to { align: 'auto', behavior: 'auto' }.\n */\n function scrollToIndex(\n rowIndex?: number | null,\n colIndex?: number | null,\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 { targetX, targetY, effectiveAlignX, effectiveAlignY } = calculateScrollTarget({\n rowIndex,\n colIndex,\n options,\n direction: direction.value,\n viewportWidth: viewportWidth.value,\n viewportHeight: viewportHeight.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 scaleX: scaleX.value,\n scaleY: scaleY.value,\n hostOffsetX: componentOffset.x,\n hostOffsetY: componentOffset.y,\n stickyIndices: sortedStickyIndices.value,\n stickyStartX: stickyStartX.value,\n stickyStartY: stickyStartY.value,\n stickyEndX: stickyEndX.value,\n stickyEndY: stickyEndY.value,\n flowPaddingStartX: flowStartX.value,\n flowPaddingStartY: flowStartY.value,\n paddingStartX: paddingStartX.value,\n paddingStartY: paddingStartY.value,\n paddingEndX: paddingEndX.value,\n paddingEndY: paddingEndY.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 displayTargetX = virtualToDisplay(targetX, componentOffset.x, scaleX.value);\n const displayTargetY = virtualToDisplay(targetY, componentOffset.y, scaleY.value);\n\n const finalX = isRtl.value ? -displayTargetX : displayTargetX;\n const finalY = displayTargetY;\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 const scrollOptions: ScrollToOptions = {\n behavior: scrollBehavior,\n };\n if (colIndex !== null && colIndex !== undefined) {\n scrollOptions.left = isRtl.value ? finalX : Math.max(0, finalX);\n }\n if (rowIndex !== null && rowIndex !== undefined) {\n scrollOptions.top = Math.max(0, finalY);\n }\n\n scrollTo(container, scrollOptions);\n\n if (scrollBehavior === 'auto' || scrollBehavior === undefined) {\n if (colIndex !== null && colIndex !== undefined) {\n scrollX.value = (isRtl.value ? finalX : Math.max(0, finalX));\n internalScrollX.value = targetX;\n }\n if (rowIndex !== null && rowIndex !== undefined) {\n scrollY.value = Math.max(0, finalY);\n internalScrollY.value = targetY;\n }\n\n if (pendingScroll.value) {\n const currentOptions = pendingScroll.value.options;\n if (isScrollToIndexOptions(currentOptions)) {\n currentOptions.behavior = 'auto';\n }\n }\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'). 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 pendingScroll.value = null;\n\n const clampedX = (x !== null && x !== undefined)\n ? Math.max(0, Math.min(x, totalWidth.value - viewportWidth.value))\n : null;\n const clampedY = (y !== null && y !== undefined)\n ? Math.max(0, Math.min(y, totalHeight.value - viewportHeight.value))\n : null;\n\n if (clampedX !== null) {\n internalScrollX.value = clampedX;\n }\n if (clampedY !== null) {\n internalScrollY.value = clampedY;\n }\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 displayTargetX = (clampedX !== null) ? virtualToDisplay(clampedX, componentOffset.x, scaleX.value) : null;\n const displayTargetY = (clampedY !== null) ? virtualToDisplay(clampedY, componentOffset.y, scaleY.value) : null;\n\n const targetX = (displayTargetX !== null)\n ? (isRtl.value ? -displayTargetX : displayTargetX)\n : currentX;\n const targetY = (displayTargetY !== null) ? displayTargetY : currentY;\n\n const scrollOptions: ScrollToOptions = {\n behavior: options?.behavior || 'auto',\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 scrollTo(container, scrollOptions);\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 handleScrollCorrection = (addedX: number, addedY: number) => {\n nextTick(() => {\n scrollToOffset(\n addedX > 0 ? relativeScrollX.value + addedX : null,\n addedY > 0 ? relativeScrollY.value + addedY : null,\n { behavior: 'auto', isCorrection: true } as ScrollToIndexOptions,\n );\n });\n };\n\n const initialize = () => initializeSizes(handleScrollCorrection);\n\n /**\n * Updates the host element's offset relative to the scroll container.\n */\n const updateHostOffset = () => {\n if (typeof window === 'undefined') {\n return;\n }\n const container = props.value.container || window;\n\n const calculateOffset = (el: HTMLElement) => {\n const rect = el.getBoundingClientRect();\n if (container === window) {\n return {\n x: isRtl.value\n ? document.documentElement.clientWidth - rect.right - window.scrollX\n : rect.left + window.scrollX,\n y: rect.top + window.scrollY,\n };\n }\n if (container === el) {\n return { x: 0, y: 0 };\n }\n if (isElement(container)) {\n const containerRect = container.getBoundingClientRect();\n return {\n x: isRtl.value\n ? containerRect.right - rect.right - container.scrollLeft\n : rect.left - containerRect.left + container.scrollLeft,\n y: rect.top - containerRect.top + container.scrollTop,\n };\n }\n return { x: 0, y: 0 };\n };\n\n if (props.value.hostElement) {\n const newOffset = calculateOffset(props.value.hostElement);\n if (Math.abs(hostOffset.x - newOffset.x) > 0.1 || Math.abs(hostOffset.y - newOffset.y) > 0.1) {\n hostOffset.x = newOffset.x;\n hostOffset.y = newOffset.y;\n }\n }\n\n if (props.value.hostRef) {\n const newOffset = calculateOffset(props.value.hostRef);\n if (Math.abs(hostRefOffset.x - newOffset.x) > 0.1 || Math.abs(hostRefOffset.y - newOffset.y) > 0.1) {\n hostRefOffset.x = newOffset.x;\n hostRefOffset.y = newOffset.y;\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 ], initialize, { immediate: true });\n\n watch(() => [ props.value.container, props.value.hostElement ], () => {\n updateHostOffset();\n });\n\n watch(isRtl, (newRtl, oldRtl) => {\n if (oldRtl === undefined || newRtl === oldRtl || !isMounted.value) {\n return;\n }\n\n // Use the oldRtl to correctly interpret the current scrollX\n if (direction.value === 'vertical') {\n updateHostOffset();\n return;\n }\n\n const scrollValue = oldRtl ? Math.abs(scrollX.value) : scrollX.value;\n const oldRelativeScrollX = displayToVirtual(scrollValue, hostOffset.x, scaleX.value);\n\n // Update host offset for the new direction\n updateHostOffset();\n\n // Maintain logical horizontal position when direction changes\n scrollToOffset(oldRelativeScrollX, null, { behavior: 'auto' });\n }, { flush: 'sync' });\n\n watch([ scaleX, scaleY ], () => {\n if (!isMounted.value || isScrolling.value || isProgrammaticScroll.value) {\n return;\n }\n // Sync display scroll to maintain logical position\n scrollToOffset(internalScrollX.value, internalScrollY.value, { behavior: 'auto' });\n });\n\n watch([ () => props.value.items.length, () => props.value.columnCount ], ([ newLen, newColCount ], [ oldLen, oldColCount ]) => {\n nextTick(() => {\n const maxRelX = Math.max(0, totalWidth.value - viewportWidth.value);\n const maxRelY = Math.max(0, totalHeight.value - viewportHeight.value);\n\n if (internalScrollX.value > maxRelX || internalScrollY.value > maxRelY) {\n scrollToOffset(\n Math.min(internalScrollX.value, maxRelX),\n Math.min(internalScrollY.value, maxRelY),\n { behavior: 'auto' },\n );\n } else if ((newLen !== oldLen && scaleY.value !== 1) || (newColCount !== oldColCount && scaleX.value !== 1)) {\n // Even if within bounds, we must sync the display scroll position\n // because the coordinate scaling factor changed.\n scrollToOffset(internalScrollX.value, internalScrollY.value, { behavior: 'auto' });\n }\n updateHostOffset();\n });\n });\n\n // --- Range & Visible Items ---\n /**\n * Helper to get the row index (or item index in list mode) at a specific virtual offset.\n *\n * @param offset - The virtual pixel offset (VU).\n * @returns The index at that position.\n */\n const getRowIndexAt = (offset: number) => {\n const isHorizontal = direction.value === 'horizontal';\n return calculateIndexAt(\n offset,\n fixedItemSize.value,\n isHorizontal ? (props.value.columnGap || 0) : (props.value.gap || 0),\n (off) => (isHorizontal ? itemSizesX.findLowerBound(off) : itemSizesY.findLowerBound(off)),\n );\n };\n\n /**\n * Helper to get the column index at a specific virtual offset.\n *\n * @param offset - The virtual pixel offset (VU).\n * @returns The column index at that position.\n */\n const getColIndexAt = (offset: number) => {\n if (direction.value === 'both') {\n return calculateIndexAt(\n offset,\n fixedColumnWidth.value,\n props.value.columnGap || 0,\n (off) => columnSizes.findLowerBound(off),\n );\n }\n if (direction.value === 'horizontal') {\n return getRowIndexAt(offset);\n }\n return 0;\n };\n\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: direction.value,\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 offsetX = relativeScrollX.value + stickyStartX.value;\n const offsetY = relativeScrollY.value + stickyStartY.value;\n const offset = direction.value === 'horizontal' ? offsetX : offsetY;\n return getRowIndexAt(offset);\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\n return calculateColumnRange({\n columnCount: totalCols,\n relativeScrollX: calculateOffsetAt(safeStart, fixedColumnWidth.value, props.value.columnGap || 0, (idx) => columnSizes.query(idx)),\n usableWidth: calculateRangeSize(safeStart, safeEnd, fixedColumnWidth.value, props.value.columnGap || 0, (idx) => columnSizes.query(idx)),\n colBuffer: 0,\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 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 * 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 stickyIndices = sortedStickyIndices.value;\n const stickySet = stickyIndicesSet.value;\n\n const sortedIndices: number[] = [];\n\n if (isHydrated.value || !props.value.ssrRange) {\n const activeIdx = currentIndex.value;\n const prevStickyIdx = findPrevStickyIndex(stickyIndices, activeIdx);\n\n if (prevStickyIdx !== undefined && prevStickyIdx < start) {\n sortedIndices.push(prevStickyIdx);\n }\n }\n\n for (let i = start; i < end; i++) {\n sortedIndices.push(i);\n }\n\n const { x: ssrOffsetX, y: ssrOffsetY } = (!isHydrated.value && props.value.ssrRange)\n ? calculateSSROffsets(\n direction.value,\n props.value.ssrRange,\n fixedItemSize.value,\n fixedColumnWidth.value,\n props.value.gap || 0,\n props.value.columnGap || 0,\n (idx) => itemSizesY.query(idx),\n (idx) => itemSizesX.query(idx),\n (idx) => columnSizes.query(idx),\n )\n : { x: 0, y: 0 };\n\n const lastItemsMap = new Map(lastRenderedItems.map((it) => [ it.index, it ]));\n\n // Optimization: Cache sequential queries to avoid O(log N) tree traversal for every item\n let lastIndexX = -1;\n let lastOffsetX = 0;\n let lastIndexY = -1;\n let lastOffsetY = 0;\n\n const queryXCached = (idx: number) => {\n if (idx === lastIndexX + 1) {\n lastOffsetX += itemSizesX.get(lastIndexX);\n lastIndexX = idx;\n return lastOffsetX;\n }\n lastOffsetX = itemSizesX.query(idx);\n lastIndexX = idx;\n return lastOffsetX;\n };\n\n const queryYCached = (idx: number) => {\n if (idx === lastIndexY + 1) {\n lastOffsetY += itemSizesY.get(lastIndexY);\n lastIndexY = idx;\n return lastOffsetY;\n }\n lastOffsetY = itemSizesY.query(idx);\n lastIndexY = idx;\n return lastOffsetY;\n };\n\n const itemsStartVU_X = flowStartX.value + stickyStartX.value + paddingStartX.value;\n const itemsStartVU_Y = flowStartY.value + stickyStartY.value + paddingStartY.value;\n const wrapperStartDU_X = flowStartX.value + stickyStartX.value;\n const wrapperStartDU_Y = flowStartY.value + stickyStartY.value;\n\n const colRange = columnRange.value;\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: direction.value,\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: totalSize.value.width,\n queryY: queryYCached,\n queryX: queryXCached,\n getSizeY: (idx) => itemSizesY.get(idx),\n getSizeX: (idx) => itemSizesX.get(idx),\n columnRange: colRange,\n });\n\n const isSticky = stickySet.has(i);\n const originalX = x;\n const originalY = y;\n\n const { isStickyActive, isStickyActiveX, isStickyActiveY, stickyOffset } = calculateStickyItem({\n index: i,\n isSticky,\n direction: direction.value,\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: queryYCached,\n getItemQueryX: queryXCached,\n });\n\n const offsetX = isHydrated.value\n ? (internalScrollX.value / scaleX.value + (x + itemsStartVU_X - internalScrollX.value)) - wrapperStartDU_X\n : (x - ssrOffsetX);\n const offsetY = isHydrated.value\n ? (internalScrollY.value / scaleY.value + (y + itemsStartVU_Y - internalScrollY.value)) - wrapperStartDU_Y\n : (y - ssrOffsetY);\n\n const last = lastItemsMap.get(i);\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.isStickyActiveX === isStickyActiveX\n && last.isStickyActiveY === isStickyActiveY\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 isStickyActiveX,\n isStickyActiveY,\n stickyOffset,\n });\n }\n }\n\n lastRenderedItems = items;\n\n return items;\n });\n\n const scrollDetails = computed<ScrollDetails<T>>(() => {\n // eslint-disable-next-line ts/no-unused-expressions\n treeUpdateFlag.value;\n\n const currentScrollX = relativeScrollX.value + stickyStartX.value;\n const currentScrollY = relativeScrollY.value + stickyStartY.value;\n\n const currentEndScrollX = relativeScrollX.value + (viewportWidth.value - stickyEndX.value) - 1;\n const currentEndScrollY = relativeScrollY.value + (viewportHeight.value - stickyEndY.value) - 1;\n\n const currentColIndex = getColIndexAt(currentScrollX);\n const currentRowIndex = getRowIndexAt(currentScrollY);\n const currentEndIndex = getRowIndexAt(direction.value === 'horizontal' ? currentEndScrollX : currentEndScrollY);\n const currentEndColIndex = getColIndexAt(currentEndScrollX);\n\n return {\n items: renderedItems.value,\n currentIndex: currentRowIndex,\n currentColIndex,\n currentEndIndex,\n currentEndColIndex,\n scrollOffset: {\n x: internalScrollX.value,\n y: internalScrollY.value,\n },\n displayScrollOffset: {\n x: isRtl.value ? Math.abs(scrollX.value + hostRefOffset.x) : Math.max(0, scrollX.value - hostRefOffset.x),\n y: Math.max(0, scrollY.value - hostRefOffset.y),\n },\n viewportSize: {\n width: viewportWidth.value,\n height: viewportHeight.value,\n },\n displayViewportSize: {\n width: viewportWidth.value,\n height: viewportHeight.value,\n },\n totalSize: {\n width: totalWidth.value,\n height: totalHeight.value,\n },\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 updateDirection();\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 const scrollValueX = isRtl.value ? Math.abs(scrollX.value) : scrollX.value;\n const virtualX = displayToVirtual(scrollValueX, componentOffset.x, scaleX.value);\n const virtualY = displayToVirtual(scrollY.value, componentOffset.y, scaleY.value);\n\n if (Math.abs(virtualX - lastInternalX) > 0.5) {\n scrollDirectionX = virtualX > lastInternalX ? 'end' : 'start';\n lastInternalX = virtualX;\n }\n if (Math.abs(virtualY - lastInternalY) > 0.5) {\n scrollDirectionY = virtualY > lastInternalY ? 'end' : 'start';\n lastInternalY = virtualY;\n }\n\n internalScrollX.value = virtualX;\n internalScrollY.value = virtualY;\n\n if (!isProgrammaticScroll.value) {\n pendingScroll.value = null;\n }\n\n if (!isScrolling.value) {\n isScrolling.value = true;\n }\n clearTimeout(scrollTimeout);\n scrollTimeout = setTimeout(() => {\n const wasProgrammatic = isProgrammaticScroll.value;\n isScrolling.value = false;\n isProgrammaticScroll.value = false;\n\n // Only perform snapping if enabled and the last scroll was user-initiated\n if (props.value.snap && !wasProgrammatic) {\n const snapProp = props.value.snap;\n const snapMode = snapProp === true ? 'auto' : snapProp;\n const details = scrollDetails.value;\n const itemsLen = props.value.items.length;\n\n let targetRow: number | null = details.currentIndex;\n let targetCol: number | null = details.currentColIndex;\n let alignY: ScrollAlignment = 'start';\n let alignX: ScrollAlignment = 'start';\n let shouldSnap = false;\n\n // Handle Y Axis (Vertical)\n if (direction.value !== 'horizontal') {\n const res = resolveSnap(\n snapMode,\n scrollDirectionY,\n details.currentIndex,\n details.currentEndIndex,\n relativeScrollY.value,\n viewportHeight.value,\n itemsLen,\n (i) => itemSizesY.get(i),\n (i) => itemSizesY.query(i),\n getRowIndexAt,\n );\n if (res) {\n targetRow = res.index;\n alignY = res.align;\n shouldSnap = true;\n }\n }\n\n // Handle X Axis (Horizontal)\n if (direction.value !== 'vertical') {\n const isGrid = direction.value === 'both';\n const colCount = isGrid ? (props.value.columnCount || 0) : itemsLen;\n const res = resolveSnap(\n snapMode,\n scrollDirectionX,\n details.currentColIndex,\n details.currentEndColIndex,\n relativeScrollX.value,\n viewportWidth.value,\n colCount,\n (i) => (isGrid ? columnSizes.get(i) : itemSizesX.get(i)),\n (i) => (isGrid ? columnSizes.query(i) : itemSizesX.query(i)),\n getColIndexAt,\n );\n if (res) {\n targetCol = res.index;\n alignX = res.align;\n shouldSnap = true;\n }\n }\n\n if (shouldSnap) {\n scrollToIndex(targetRow, targetCol, {\n align: { x: alignX, y: alignY },\n behavior: 'smooth',\n });\n }\n }\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 coreUpdateItemSizes(\n updates,\n getRowIndexAt,\n getColIndexAt,\n relativeScrollX.value,\n relativeScrollY.value,\n (dx, dy) => {\n const hasPendingScroll = pendingScroll.value !== null || isProgrammaticScroll.value;\n if (!hasPendingScroll) {\n handleScrollCorrection(dx, dy);\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 function 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 container = props.value.container || window;\n const actualScrollX = (typeof window !== 'undefined' && container === window ? window.scrollX : (container as HTMLElement).scrollLeft);\n const actualScrollY = (typeof window !== 'undefined' && container === window ? window.scrollY : (container as HTMLElement).scrollTop);\n\n const scrollValueX = isRtl.value ? Math.abs(actualScrollX) : actualScrollX;\n const scrollValueY = actualScrollY;\n\n const currentRelX = displayToVirtual(scrollValueX, 0, scaleX.value);\n const currentRelY = displayToVirtual(scrollValueY, 0, scaleY.value);\n\n const { targetX, targetY } = calculateScrollTarget({\n rowIndex,\n colIndex,\n options,\n direction: direction.value,\n viewportWidth: viewportWidth.value,\n viewportHeight: viewportHeight.value,\n totalWidth: virtualWidth.value,\n totalHeight: virtualHeight.value,\n gap: props.value.gap || 0,\n columnGap: props.value.columnGap || 0,\n fixedSize: fixedItemSize.value,\n fixedWidth: fixedColumnWidth.value,\n relativeScrollX: currentRelX,\n relativeScrollY: currentRelY,\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 scaleX: scaleX.value,\n scaleY: scaleY.value,\n hostOffsetX: componentOffset.x,\n hostOffsetY: componentOffset.y,\n stickyIndices: sortedStickyIndices.value,\n stickyStartX: stickyStartX.value,\n stickyStartY: stickyStartY.value,\n stickyEndX: stickyEndX.value,\n stickyEndY: stickyEndY.value,\n flowPaddingStartX: flowStartX.value,\n flowPaddingStartY: flowStartY.value,\n paddingStartX: paddingStartX.value,\n paddingStartY: paddingStartY.value,\n paddingEndX: paddingEndX.value,\n paddingEndY: paddingEndY.value,\n });\n\n const toleranceX = 2;\n const toleranceY = 2;\n const reachedX = (colIndex === null || colIndex === undefined) || Math.abs(currentRelX - targetX) < toleranceX;\n const reachedY = (rowIndex === null || rowIndex === undefined) || Math.abs(currentRelY - targetY) < toleranceY;\n\n const isMeasuredX = colIndex == null || colIndex === undefined || measuredColumns.value[ colIndex ] === 1;\n const isMeasuredY = rowIndex == null || rowIndex === undefined || measuredItemsY.value[ rowIndex ] === 1;\n\n if (reachedX && reachedY) {\n if (isMeasuredX && isMeasuredY && !isScrolling.value && !isProgrammaticScroll.value) {\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 let directionObserver: MutationObserver | null = null;\n let directionInterval: ReturnType<typeof setInterval> | undefined;\n\n const attachEvents = (container: HTMLElement | Window | null) => {\n if (typeof window === 'undefined') {\n return;\n }\n const effectiveContainer = container || window;\n const scrollTarget = (effectiveContainer === window || (isElement(effectiveContainer) && effectiveContainer === document.documentElement)) ? document : effectiveContainer;\n\n scrollTarget.addEventListener('scroll', handleScroll, { passive: true });\n\n computedStyle = null;\n updateDirection();\n\n if (isElement(effectiveContainer)) {\n directionObserver = new MutationObserver(() => updateDirection());\n directionObserver.observe(effectiveContainer, { attributes: true, attributeFilter: [ 'dir', 'style' ] });\n }\n\n directionInterval = setInterval(updateDirection, 1000);\n\n if (effectiveContainer === 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 updateDirection();\n viewportWidth.value = document.documentElement.clientWidth;\n viewportHeight.value = document.documentElement.clientHeight;\n updateHostOffset();\n };\n window.addEventListener('resize', onResize);\n\n return () => {\n scrollTarget.removeEventListener('scroll', handleScroll);\n window.removeEventListener('resize', onResize);\n directionObserver?.disconnect();\n clearInterval(directionInterval);\n computedStyle = null;\n };\n } else {\n viewportWidth.value = (effectiveContainer as HTMLElement).clientWidth;\n viewportHeight.value = (effectiveContainer as HTMLElement).clientHeight;\n scrollX.value = (effectiveContainer as HTMLElement).scrollLeft;\n scrollY.value = (effectiveContainer as HTMLElement).scrollTop;\n\n resizeObserver = new ResizeObserver(() => {\n updateDirection();\n viewportWidth.value = (effectiveContainer as HTMLElement).clientWidth;\n viewportHeight.value = (effectiveContainer as HTMLElement).clientHeight;\n updateHostOffset();\n });\n resizeObserver.observe(effectiveContainer as HTMLElement);\n\n return () => {\n scrollTarget.removeEventListener('scroll', handleScroll);\n resizeObserver?.disconnect();\n directionObserver?.disconnect();\n clearInterval(directionInterval);\n computedStyle = null;\n };\n }\n };\n\n let cleanup: (() => void) | undefined;\n\n if (getCurrentInstance()) {\n onMounted(() => {\n isMounted.value = true;\n updateDirection();\n\n watch(() => props.value.container, (newContainer) => {\n cleanup?.();\n cleanup = attachEvents(newContainer || null);\n }, { immediate: true });\n\n updateHostOffset();\n\n // Ensure we have a layout cycle before considering it hydrated\n // and starting virtualization. This avoids issues with 0-size viewports.\n nextTick(() => {\n updateHostOffset();\n if (props.value.ssrRange || props.value.initialScrollIndex !== undefined) {\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 } else {\n isHydrated.value = true;\n }\n });\n });\n\n onUnmounted(() => {\n cleanup?.();\n });\n }\n\n /**\n * The list of items currently rendered in the DOM.\n */\n /**\n * Resets all dynamic measurements and re-initializes from current props.\n * Useful if item source data has changed in a way that affects sizes without changing the items array reference.\n */\n const refresh = () => {\n coreRefresh(handleScrollCorrection);\n };\n\n return {\n /**\n * Array of items currently rendered in the DOM with their calculated offsets and sizes.\n * Offsets are in Display Units (DU), sizes are in Virtual Units (VU).\n * @see RenderedItem\n */\n renderedItems,\n\n /**\n * Total calculated width of all items including gaps (in VU).\n */\n totalWidth,\n\n /**\n * Total calculated height of all items including gaps (in VU).\n */\n totalHeight,\n\n /**\n * Total width to be rendered in the DOM (clamped to browser limits, in DU).\n */\n renderedWidth,\n\n /**\n * Total height to be rendered in the DOM (clamped to browser limits, in DU).\n */\n renderedHeight,\n\n /**\n * Detailed information about the current scroll state.\n * Includes currentIndex, scrollOffset (VU), displayScrollOffset (DU), viewportSize (DU), totalSize (VU), and scrolling status.\n * @see ScrollDetails\n */\n scrollDetails,\n\n /**\n * Helper to get the height of a specific row based on current configuration and measurements.\n *\n * @param index - The row index.\n * @returns The height in VU (excluding gap).\n */\n getRowHeight,\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 * @returns The width in VU (excluding gap).\n */\n getColumnWidth,\n\n /**\n * Helper to get the virtual offset of a specific row.\n *\n * @param index - The row index.\n * @returns The virtual offset in VU.\n */\n getRowOffset: (index: number) => (flowStartY.value + stickyStartY.value + paddingStartY.value) + calculateOffsetAt(index, fixedItemSize.value, props.value.gap || 0, (idx) => itemSizesY.query(idx)),\n\n /**\n * Helper to get the virtual offset of a specific column.\n *\n * @param index - The column index.\n * @returns The virtual offset in VU.\n */\n getColumnOffset: (index: number) => {\n const itemsStartVU_X = flowStartX.value + stickyStartX.value + paddingStartX.value;\n if (direction.value === 'both') {\n return itemsStartVU_X + calculateOffsetAt(index, fixedColumnWidth.value, props.value.columnGap || 0, (idx) => columnSizes.query(idx));\n }\n return itemsStartVU_X + calculateOffsetAt(index, fixedItemSize.value, props.value.columnGap || 0, (idx) => itemSizesX.query(idx));\n },\n\n /**\n * Helper to get the virtual offset of a specific item along the scroll axis.\n *\n * @param index - The item index.\n * @returns The virtual offset in VU.\n */\n getItemOffset: (index: number) => (direction.value === 'horizontal' ? (flowStartX.value + stickyStartX.value + paddingStartX.value) + calculateOffsetAt(index, fixedItemSize.value, props.value.columnGap || 0, (idx) => itemSizesX.query(idx)) : (flowStartY.value + stickyStartY.value + paddingStartY.value) + calculateOffsetAt(index, fixedItemSize.value, props.value.gap || 0, (idx) => itemSizesY.query(idx))),\n\n /**\n * Helper to get the size of a specific item along the scroll axis.\n *\n * @param index - The item index.\n * @returns The size in VU (excluding gap).\n */\n getItemSize: (index: number) => (direction.value === 'horizontal'\n ? getColumnWidth(index)\n : getRowHeight(index)),\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 (VU). Pass null to keep current position.\n * @param y - The pixel offset to scroll to on the Y axis (VU). 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 in DU).\n * @param height - The measured blockSize (height in DU).\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 (sizes in DU).\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 * Detects the current direction (LTR/RTL) of the scroll container.\n */\n updateDirection,\n\n /**\n * Information about the current visible range of columns and their paddings.\n * @see ColumnRange\n */\n columnRange,\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 * Whether the container is the window or body.\n */\n isWindowContainer,\n\n /**\n * Whether the scroll container is in Right-to-Left (RTL) mode.\n */\n isRtl,\n\n /**\n * Coordinate scaling factor for X axis (VU/DU).\n */\n scaleX,\n\n /**\n * Coordinate scaling factor for Y axis (VU/DU).\n */\n scaleY,\n\n /**\n * Absolute offset of the component within its container (DU).\n */\n componentOffset,\n\n /**\n * Physical width of the items wrapper in the DOM (clamped to browser limits, in DU).\n */\n renderedVirtualWidth,\n\n /**\n * Physical height of the items wrapper in the DOM (clamped to browser limits, in DU).\n */\n renderedVirtualHeight,\n\n /**\n * Helper to get the row index at a specific virtual offset.\n */\n getRowIndexAt,\n\n /**\n * Helper to get the column index at a specific virtual offset.\n */\n getColIndexAt,\n };\n}\n","/**\n * Composable for virtual scrollbar logic.\n * Handles calculation of thumb position and size, track interactions, and dragging.\n */\n\nimport type { ScrollAxis } from '../types';\nimport type { MaybeRefOrGetter } from 'vue';\n\nimport { computed, getCurrentInstance, onUnmounted, ref, toValue } from 'vue';\n\n/** Configuration properties for the `useVirtualScrollbar` composable. */\nexport interface UseVirtualScrollbarProps {\n /** The axis for this scrollbar. */\n axis: ScrollAxis;\n /** Total size of the scrollable content area in display pixels (DU). */\n totalSize: number;\n /** Current scroll position in display pixels (DU). */\n position: number;\n /** Viewport size in display pixels (DU). */\n viewportSize: number;\n /**\n * Function to scroll to a specific display pixel offset (DU) on this axis.\n * @param offset - The display pixel offset to scroll to.\n */\n scrollToOffset: (offset: number) => void;\n /** The ID of the container element this scrollbar controls. */\n containerId?: string | undefined;\n /** Whether the scrollbar is in Right-to-Left (RTL) mode. */\n isRtl?: boolean;\n /** Accessible label for the scrollbar. */\n ariaLabel?: string | undefined;\n}\n\n/**\n * Composable for virtual scrollbar logic.\n * Provides attributes and event listeners for track and thumb elements.\n *\n * @param propsInput - Configuration properties.\n */\nexport function useVirtualScrollbar(propsInput: MaybeRefOrGetter<UseVirtualScrollbarProps>) {\n const props = computed(() => toValue(propsInput));\n\n const isHorizontal = computed(() => props.value.axis === 'horizontal');\n\n const viewportPercent = computed(() => {\n if (props.value.totalSize <= 0) {\n return 0;\n }\n return Math.min(1, props.value.viewportSize / props.value.totalSize);\n });\n\n const positionPercent = computed(() => {\n const scrollableRange = props.value.totalSize - props.value.viewportSize;\n if (scrollableRange <= 0) {\n return 0;\n }\n return Math.max(0, Math.min(1, props.value.position / scrollableRange));\n });\n\n const thumbSizePercent = computed(() => {\n // Minimum thumb size in pixels (32px for better touch targets and visibility)\n const minThumbSize = 32;\n const minPercent = props.value.viewportSize > 0 ? (minThumbSize / props.value.viewportSize) : 0.1;\n return Math.max(Math.min(minPercent, 0.1), viewportPercent.value) * 100;\n });\n /** Calculated thumb position as a percentage of the track size (0 to 100). */\n const thumbPositionPercent = computed(() => positionPercent.value * (100 - thumbSizePercent.value));\n\n /** Reactive style object for the scrollbar thumb. */\n const thumbStyle = computed(() => {\n if (isHorizontal.value) {\n return {\n inlineSize: `${ thumbSizePercent.value }%`,\n insetInlineStart: `${ thumbPositionPercent.value }%`,\n };\n }\n return {\n blockSize: `${ thumbSizePercent.value }%`,\n insetBlockStart: `${ thumbPositionPercent.value }%`,\n };\n });\n\n /** Reactive style object for the scrollbar track. */\n const trackStyle = computed(() => {\n const displayViewportSize = props.value.viewportSize;\n const scrollbarGap = 'var(--vs-scrollbar-has-cross-gap, var(--vsi-scrollbar-has-cross-gap, 0)) * var(--vs-scrollbar-cross-gap, var(--vsi-scrollbar-size, 8px))';\n\n return isHorizontal.value\n ? {\n inlineSize: `calc(${ Math.max(0, displayViewportSize - 4) }px - ${ scrollbarGap })`,\n }\n : {\n blockSize: `calc(${ Math.max(0, displayViewportSize - 4) }px - ${ scrollbarGap })`,\n };\n });\n\n const isDragging = ref(false);\n let startPos = 0;\n let startScrollPos = 0;\n\n function handleTrackClick(event: MouseEvent) {\n const track = event.currentTarget as HTMLElement;\n if (event.target !== track) {\n return;\n }\n\n const rect = track.getBoundingClientRect();\n const trackSize = isHorizontal.value ? rect.width : rect.height;\n let clickPos = 0;\n\n if (isHorizontal.value) {\n clickPos = props.value.isRtl ? rect.right - event.clientX : event.clientX - rect.left;\n } else {\n clickPos = event.clientY - rect.top;\n }\n\n const thumbSize = (thumbSizePercent.value / 100) * trackSize;\n const targetPercent = (clickPos - thumbSize / 2) / (trackSize - thumbSize);\n const scrollableRange = props.value.totalSize - props.value.viewportSize;\n\n let targetOffset = targetPercent * scrollableRange;\n if (targetOffset > scrollableRange - 1) {\n targetOffset = scrollableRange;\n }\n\n props.value.scrollToOffset(Math.max(0, Math.min(scrollableRange, targetOffset)));\n }\n\n function handleThumbPointerDown(event: PointerEvent) {\n isDragging.value = true;\n startPos = isHorizontal.value\n ? (props.value.isRtl ? -event.clientX : event.clientX)\n : event.clientY;\n startScrollPos = props.value.position;\n\n const thumb = event.currentTarget as HTMLElement;\n thumb.setPointerCapture(event.pointerId);\n event.preventDefault();\n event.stopPropagation();\n }\n\n function handleThumbPointerMove(event: PointerEvent) {\n if (!isDragging.value) {\n return;\n }\n\n const thumb = event.currentTarget as HTMLElement;\n const track = thumb.parentElement;\n if (!track) {\n return;\n }\n\n const currentPos = isHorizontal.value\n ? (props.value.isRtl ? -event.clientX : event.clientX)\n : event.clientY;\n const delta = currentPos - startPos;\n const rect = track.getBoundingClientRect();\n const trackSize = isHorizontal.value ? rect.width : rect.height;\n const thumbSize = (thumbSizePercent.value / 100) * trackSize;\n\n const scrollableTrackRange = trackSize - thumbSize;\n if (scrollableTrackRange <= 0) {\n return;\n }\n\n const scrollableContentRange = props.value.totalSize - props.value.viewportSize;\n let targetOffset = startScrollPos + (delta / scrollableTrackRange) * scrollableContentRange;\n\n if (targetOffset > scrollableContentRange - 1) {\n targetOffset = scrollableContentRange;\n }\n\n props.value.scrollToOffset(Math.max(0, Math.min(scrollableContentRange, targetOffset)));\n }\n\n function handleThumbPointerUp(event: PointerEvent) {\n if (!isDragging.value) {\n return;\n }\n isDragging.value = false;\n (event.currentTarget as HTMLElement).releasePointerCapture(event.pointerId);\n }\n\n if (getCurrentInstance()) {\n onUnmounted(() => {\n isDragging.value = false;\n });\n }\n\n const trackProps = computed(() => ({\n class: [\n 'virtual-scrollbar-track',\n `virtual-scrollbar-track--${ isHorizontal.value ? 'horizontal' : 'vertical' }`,\n ],\n style: trackStyle.value,\n role: 'scrollbar',\n 'aria-label': props.value.ariaLabel,\n 'aria-orientation': props.value.axis,\n 'aria-valuenow': Math.round(props.value.position),\n 'aria-valuemin': 0,\n 'aria-valuemax': Math.round(props.value.totalSize - props.value.viewportSize),\n 'aria-controls': props.value.containerId,\n tabindex: -1,\n onMousedown: handleTrackClick,\n }));\n\n const thumbProps = computed(() => ({\n class: [\n 'virtual-scrollbar-thumb',\n `virtual-scrollbar-thumb--${ isHorizontal.value ? 'horizontal' : 'vertical' }`,\n {\n 'virtual-scrollbar-thumb--active': isDragging.value,\n },\n ],\n style: thumbStyle.value,\n onPointerdown: handleThumbPointerDown,\n onPointermove: handleThumbPointerMove,\n onPointerup: handleThumbPointerUp,\n onPointercancel: handleThumbPointerUp,\n }));\n\n return {\n /** Viewport size as a percentage of total size (0 to 1). */\n viewportPercent,\n /** Current scroll position as a percentage of the scrollable range (0 to 1). */\n positionPercent,\n /** Calculated thumb size as a percentage of the track size (0 to 100). */\n thumbSizePercent,\n /** Calculated thumb position as a percentage of the track size (0 to 100). */\n thumbPositionPercent,\n /** Reactive style object for the scrollbar track. */\n trackStyle,\n /** Reactive style object for the scrollbar thumb. */\n thumbStyle,\n /** Attributes and event listeners to be bound to the track element. */\n trackProps,\n /** Attributes and event listeners to be bound to the thumb element. */\n thumbProps,\n /** Whether the thumb is currently being dragged. */\n isDragging,\n };\n}\n","<script setup lang=\"ts\">\n/**\n * A cross-browser consistent virtual scrollbar component.\n * Can be used independently or as part of the VirtualScroll component.\n * Supports both vertical and horizontal axes and RTL layouts.\n */\nimport type { VirtualScrollbarProps } from '../types';\n\nimport { useVirtualScrollbar } from '../composables/useVirtualScrollbar';\n\nexport interface Props extends VirtualScrollbarProps {}\n\nconst props = withDefaults(defineProps<Props>(), {\n axis: 'vertical',\n isRtl: false,\n});\n\nconst emit = defineEmits<{\n (e: 'scrollToOffset', offset: number): void;\n}>();\n\nconst { trackProps, thumbProps } = useVirtualScrollbar(() => ({\n axis: props.axis,\n totalSize: props.totalSize,\n position: props.position,\n viewportSize: props.viewportSize,\n containerId: props.containerId,\n isRtl: props.isRtl,\n scrollToOffset: (offset: number) => {\n props.scrollToOffset?.(offset);\n emit('scrollToOffset', offset);\n },\n}));\n</script>\n\n<template>\n <div v-bind=\"trackProps\">\n <div v-bind=\"thumbProps\" />\n </div>\n</template>\n\n<style>\n@layer components {\n .virtual-scrollbar-track {\n --vsi-scrollbar-bg: var(--vs-scrollbar-bg, rgba(230, 230, 230, 0.9));\n --vsi-scrollbar-thumb-bg: var(--vs-scrollbar-thumb-bg, rgba(0, 0, 0, 0.3));\n --vsi-scrollbar-thumb-hover-bg: var(--vs-scrollbar-thumb-hover-bg, rgba(0, 0, 0, 0.6));\n\n --vsi-scrollbar-bg: var(--vs-scrollbar-bg, light-dark(rgba(230, 230, 230, 0.9), rgba(30, 30, 30, 0.9)));\n --vsi-scrollbar-thumb-bg: var(--vs-scrollbar-thumb-bg, light-dark(rgba(0, 0, 0, 0.3), rgba(255, 255, 255, 0.3)));\n --vsi-scrollbar-thumb-hover-bg: var(--vs-scrollbar-thumb-hover-bg, light-dark(rgba(0, 0, 0, 0.6), rgba(255, 255, 255, 0.6)));\n\n --vsi-scrollbar-radius: var(--vs-scrollbar-radius, 4px);\n --vsi-scrollbar-size: var(--vs-scrollbar-size, 8px);\n\n position: absolute;\n contain: layout;\n background-color: var(--vsi-scrollbar-bg);\n border-radius: var(--vsi-scrollbar-radius);\n z-index: 30;\n transition: opacity 0.2s;\n user-select: none;\n -webkit-user-select: none;\n pointer-events: auto;\n\n &.virtual-scrollbar-track--vertical {\n inline-size: var(--vsi-scrollbar-size);\n inset-block-start: 2px;\n inset-inline-end: 2px;\n }\n\n &.virtual-scrollbar-track--horizontal {\n block-size: var(--vsi-scrollbar-size);\n inset-inline-start: 2px;\n inset-block-end: 2px;\n }\n }\n\n .virtual-scrollbar-thumb {\n position: absolute;\n background-color: var(--vsi-scrollbar-thumb-bg);\n border-radius: var(--vsi-scrollbar-radius);\n touch-action: none;\n pointer-events: auto;\n cursor: pointer;\n\n &:hover,\n &:active,\n &.virtual-scrollbar-thumb--active {\n background-color: var(--vsi-scrollbar-thumb-hover-bg);\n }\n\n &.virtual-scrollbar-thumb--vertical {\n inline-size: 100%;\n }\n\n &.virtual-scrollbar-thumb--horizontal {\n block-size: 100%;\n }\n }\n}\n</style>\n","<script setup lang=\"ts\">\n/**\n * A cross-browser consistent virtual scrollbar component.\n * Can be used independently or as part of the VirtualScroll component.\n * Supports both vertical and horizontal axes and RTL layouts.\n */\nimport type { VirtualScrollbarProps } from '../types';\n\nimport { useVirtualScrollbar } from '../composables/useVirtualScrollbar';\n\nexport interface Props extends VirtualScrollbarProps {}\n\nconst props = withDefaults(defineProps<Props>(), {\n axis: 'vertical',\n isRtl: false,\n});\n\nconst emit = defineEmits<{\n (e: 'scrollToOffset', offset: number): void;\n}>();\n\nconst { trackProps, thumbProps } = useVirtualScrollbar(() => ({\n axis: props.axis,\n totalSize: props.totalSize,\n position: props.position,\n viewportSize: props.viewportSize,\n containerId: props.containerId,\n isRtl: props.isRtl,\n scrollToOffset: (offset: number) => {\n props.scrollToOffset?.(offset);\n emit('scrollToOffset', offset);\n },\n}));\n</script>\n\n<template>\n <div v-bind=\"trackProps\">\n <div v-bind=\"thumbProps\" />\n </div>\n</template>\n\n<style>\n@layer components {\n .virtual-scrollbar-track {\n --vsi-scrollbar-bg: var(--vs-scrollbar-bg, rgba(230, 230, 230, 0.9));\n --vsi-scrollbar-thumb-bg: var(--vs-scrollbar-thumb-bg, rgba(0, 0, 0, 0.3));\n --vsi-scrollbar-thumb-hover-bg: var(--vs-scrollbar-thumb-hover-bg, rgba(0, 0, 0, 0.6));\n\n --vsi-scrollbar-bg: var(--vs-scrollbar-bg, light-dark(rgba(230, 230, 230, 0.9), rgba(30, 30, 30, 0.9)));\n --vsi-scrollbar-thumb-bg: var(--vs-scrollbar-thumb-bg, light-dark(rgba(0, 0, 0, 0.3), rgba(255, 255, 255, 0.3)));\n --vsi-scrollbar-thumb-hover-bg: var(--vs-scrollbar-thumb-hover-bg, light-dark(rgba(0, 0, 0, 0.6), rgba(255, 255, 255, 0.6)));\n\n --vsi-scrollbar-radius: var(--vs-scrollbar-radius, 4px);\n --vsi-scrollbar-size: var(--vs-scrollbar-size, 8px);\n\n position: absolute;\n contain: layout;\n background-color: var(--vsi-scrollbar-bg);\n border-radius: var(--vsi-scrollbar-radius);\n z-index: 30;\n transition: opacity 0.2s;\n user-select: none;\n -webkit-user-select: none;\n pointer-events: auto;\n\n &.virtual-scrollbar-track--vertical {\n inline-size: var(--vsi-scrollbar-size);\n inset-block-start: 2px;\n inset-inline-end: 2px;\n }\n\n &.virtual-scrollbar-track--horizontal {\n block-size: var(--vsi-scrollbar-size);\n inset-inline-start: 2px;\n inset-block-end: 2px;\n }\n }\n\n .virtual-scrollbar-thumb {\n position: absolute;\n background-color: var(--vsi-scrollbar-thumb-bg);\n border-radius: var(--vsi-scrollbar-radius);\n touch-action: none;\n pointer-events: auto;\n cursor: pointer;\n\n &:hover,\n &:active,\n &.virtual-scrollbar-thumb--active {\n background-color: var(--vsi-scrollbar-thumb-hover-bg);\n }\n\n &.virtual-scrollbar-thumb--vertical {\n inline-size: 100%;\n }\n\n &.virtual-scrollbar-thumb--horizontal {\n block-size: 100%;\n }\n }\n}\n</style>\n","<script setup lang=\"ts\" generic=\"T\">\n/**\n * A high-performance virtual scrolling component for Vue 3.\n * Supports large lists and grids by only rendering visible items and using coordinate scaling.\n * Features include sticky headers/footers, RTL support, custom scrollbars, and scroll restoration.\n */\nimport type {\n ItemSlotProps,\n RenderedItem,\n ScrollAlignment,\n ScrollbarSlotProps,\n ScrollDetails,\n ScrollToIndexOptions,\n VirtualScrollComponentProps,\n VirtualScrollProps,\n} from '../types';\nimport type { VNodeChild } from 'vue';\n\nimport { computed, nextTick, onMounted, onUnmounted, ref, toRefs, useId, watch } from 'vue';\n\nimport {\n useVirtualScroll,\n} from '../composables/useVirtualScroll';\nimport { useVirtualScrollbar } from '../composables/useVirtualScrollbar';\nimport { getPaddingX, getPaddingY, isWindowLike } from '../utils/scroll';\nimport {\n calculateInertiaStep,\n calculateInstantaneousVelocity,\n calculateItemStyle,\n displayToVirtual,\n} from '../utils/virtual-scroll-logic';\nimport VirtualScrollbar from './VirtualScrollbar.vue';\n\nexport interface Props<T = unknown> extends VirtualScrollComponentProps<T> {}\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 virtualScrollbar: false,\n itemRole: undefined,\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: ItemSlotProps<T>) => 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 /**\n * Scoped slot for rendering custom scrollbars.\n * If provided, the default VirtualScrollbar is not rendered.\n */\n scrollbar?: (props: ScrollbarSlotProps) => 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 instanceId = useId();\n\n/**\n * Unique ID for the scrollable container.\n * Used for accessibility (aria-controls) and to target the element in DOM.\n */\nconst containerId = computed(() => `vs-container-${ instanceId }`);\n\nconst measuredPaddingStart = ref(0);\nconst measuredPaddingEnd = ref(0);\n\nconst effectiveContainer = computed(() => (props.container === undefined ? hostRef.value : props.container));\n\nconst isHeaderFooterInsideContainer = computed(() => {\n const container = effectiveContainer.value;\n return container === hostRef.value || isWindowLike(container);\n});\n\nconst virtualScrollProps = computed(() => {\n /* Trigger re-evaluation on items array mutations */\n // eslint-disable-next-line ts/no-unused-expressions\n props.items.length;\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: effectiveContainer.value,\n hostElement: wrapperRef.value,\n hostRef: hostRef.value,\n ssrRange: props.ssrRange,\n columnCount: props.columnCount,\n columnWidth: props.columnWidth,\n scrollPaddingStart: {\n x: getPaddingX(props.scrollPaddingStart, props.direction),\n y: getPaddingY(props.scrollPaddingStart, props.direction),\n },\n scrollPaddingEnd: {\n x: getPaddingX(props.scrollPaddingEnd, props.direction),\n y: getPaddingY(props.scrollPaddingEnd, props.direction),\n },\n flowPaddingStart: {\n x: 0,\n y: props.stickyHeader ? 0 : measuredPaddingStart.value,\n },\n flowPaddingEnd: {\n x: 0,\n y: props.stickyFooter ? 0 : measuredPaddingEnd.value,\n },\n stickyStart: {\n x: 0,\n y: props.stickyHeader && isHeaderFooterInsideContainer.value ? measuredPaddingStart.value : 0,\n },\n stickyEnd: {\n x: 0,\n y: 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 snap: props.snap,\n } as VirtualScrollProps<T>;\n});\n\nconst {\n isHydrated,\n isRtl,\n columnRange,\n renderedItems,\n scrollDetails,\n renderedHeight,\n renderedWidth,\n getColumnWidth,\n getRowHeight,\n scrollToIndex,\n scrollToOffset,\n updateHostOffset,\n updateItemSizes,\n updateDirection,\n getItemOffset,\n getRowOffset,\n getColumnOffset,\n getItemSize,\n refresh: coreRefresh,\n stopProgrammaticScroll,\n scaleX,\n scaleY,\n isWindowContainer,\n componentOffset,\n renderedVirtualWidth,\n renderedVirtualHeight,\n getRowIndexAt,\n getColIndexAt,\n} = useVirtualScroll(virtualScrollProps);\n\nconst useVirtualScrolling = computed(() => scaleX.value !== 1 || scaleY.value !== 1);\n\nconst showVirtualScrollbars = computed(() => {\n if (isWindowContainer.value) {\n return false;\n }\n return props.virtualScrollbar === true || scaleX.value !== 1 || scaleY.value !== 1;\n});\n\nfunction handleVerticalScrollbarScrollToOffset(offset: number) {\n const { displayViewportSize } = scrollDetails.value;\n const scrollableRange = renderedHeight.value - displayViewportSize.height;\n if (offset >= scrollableRange - 0.5) {\n scrollToOffset(null, Number.POSITIVE_INFINITY);\n } else {\n const virtualOffset = displayToVirtual(offset, componentOffset.y, scaleY.value);\n scrollToOffset(null, virtualOffset);\n }\n}\n\nfunction handleHorizontalScrollbarScrollToOffset(offset: number) {\n const { displayViewportSize } = scrollDetails.value;\n const scrollableRange = renderedWidth.value - displayViewportSize.width;\n if (offset >= scrollableRange - 0.5) {\n scrollToOffset(Number.POSITIVE_INFINITY, null);\n } else {\n const virtualOffset = displayToVirtual(offset, componentOffset.x, scaleX.value);\n scrollToOffset(virtualOffset, null);\n }\n}\n\nconst verticalScrollbar = useVirtualScrollbar(computed(() => ({\n axis: 'vertical' as const,\n totalSize: renderedHeight.value,\n position: scrollDetails.value.displayScrollOffset.y,\n viewportSize: scrollDetails.value.displayViewportSize.height,\n scrollToOffset: handleVerticalScrollbarScrollToOffset,\n containerId: containerId.value,\n isRtl: isRtl.value,\n})));\n\nconst horizontalScrollbar = useVirtualScrollbar(computed(() => ({\n axis: 'horizontal' as const,\n totalSize: renderedWidth.value,\n position: scrollDetails.value.displayScrollOffset.x,\n viewportSize: scrollDetails.value.displayViewportSize.width,\n scrollToOffset: handleHorizontalScrollbarScrollToOffset,\n containerId: containerId.value,\n isRtl: isRtl.value,\n})));\n\nconst slotColumnRange = computed(() => {\n if (props.direction !== 'both') {\n return columnRange.value;\n }\n return {\n ...columnRange.value,\n padStart: 0,\n padEnd: 0,\n };\n});\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 updateDirection();\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 || !details) {\n return;\n }\n emit('scroll', details);\n\n if (\n !oldDetails\n || !oldDetails.range\n || !oldDetails.columnRange\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' && details.totalSize) {\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' && details.totalSize) {\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 && scrollDetails.value?.range && scrollDetails.value?.columnRange) {\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\nfunction watchExtraRef(refEl: typeof headerRef, measuredValue: typeof measuredPaddingStart) {\n watch(refEl, (newEl, oldEl) => {\n if (oldEl) {\n extraResizeObserver?.unobserve(oldEl);\n }\n if (newEl) {\n extraResizeObserver?.observe(newEl);\n } else {\n measuredValue.value = 0;\n }\n }, { immediate: true });\n}\n\nwatchExtraRef(headerRef, measuredPaddingStart);\nwatchExtraRef(footerRef, measuredPaddingEnd);\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 observeItem(el, true);\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\nwatch([ hostRef, useVirtualScrolling ], ([ host, virtual ], [ oldHost, oldVirtual ]) => {\n const needsUpdate = host !== oldHost || virtual !== oldVirtual;\n if (oldHost && needsUpdate) {\n oldHost.removeEventListener('wheel', handleWheel);\n }\n if (host && needsUpdate) {\n host.addEventListener('wheel', handleWheel, { passive: !virtual });\n }\n}, { immediate: true });\n\n/**\n * Helper to manage ResizeObserver for an item and its optional cells.\n *\n * @param el - The item element.\n * @param isObserve - True to observe, false to unobserve.\n */\nfunction observeItem(el: HTMLElement, isObserve: boolean) {\n const method = isObserve ? 'observe' : 'unobserve';\n itemResizeObserver?.[ method ](el);\n if (props.direction === 'both' && el.children.length > 0) {\n el.querySelectorAll('[data-col-index]').forEach((c) => itemResizeObserver?.[ method ](c));\n }\n}\n\n/**\n * Callback ref to track and measure item elements.\n *\n * @param el - The element or null if unmounting.\n * @param index - The original index of the item.\n */\nfunction setItemRef(el: unknown, index: number) {\n if (el) {\n const htmlEl = el as HTMLElement;\n itemRefs.set(index, htmlEl);\n observeItem(htmlEl, true);\n } else {\n const oldEl = itemRefs.get(index);\n if (oldEl) {\n observeItem(oldEl, false);\n itemRefs.delete(index);\n }\n }\n}\n\n/**\n * State for inertia scrolling\n */\nconst isPointerScrolling = ref(false);\nlet startPointerPos = { x: 0, y: 0 };\nlet startScrollOffset = { x: 0, y: 0 };\nlet lastPointerPos = { x: 0, y: 0 };\nlet lastPointerTime = 0;\nlet velocity = { x: 0, y: 0 };\nlet inertiaAnimationFrame: number | null = null;\n\n// Friction constant (0.9 to 0.98 is usually best)\nconst FRICTION = 0.95;\n// Minimum velocity to continue the animation\nconst MIN_VELOCITY = 0.1;\n\n/**\n * Recursively animates the scroll offset based on velocity and friction.\n */\nfunction startInertiaAnimation() {\n const step = () => {\n const { nextVelocity, delta } = calculateInertiaStep(velocity, FRICTION);\n velocity.x = nextVelocity.x;\n velocity.y = nextVelocity.y;\n\n // Calculate the new scroll offset\n const { x: currentX, y: currentY } = scrollDetails.value.scrollOffset;\n\n // Move the scroll position by the current velocity\n scrollToOffset(\n currentX + delta.x,\n currentY + delta.y,\n { behavior: 'auto' },\n );\n\n // Continue animation if we haven't slowed down to a halt\n if (Math.abs(velocity.x) > MIN_VELOCITY || Math.abs(velocity.y) > MIN_VELOCITY) {\n inertiaAnimationFrame = requestAnimationFrame(step);\n } else {\n stopInertia();\n }\n };\n\n inertiaAnimationFrame = requestAnimationFrame(step);\n}\n\n/**\n * Stops any ongoing inertia animation\n */\nfunction stopInertia() {\n if (inertiaAnimationFrame !== null) {\n cancelAnimationFrame(inertiaAnimationFrame);\n inertiaAnimationFrame = null;\n }\n velocity = { x: 0, y: 0 };\n}\n\n/**\n * Handles pointer down events on the container to start emulated scrolling when scaling is active.\n *\n * @param event - The pointer down event.\n */\nfunction handlePointerDown(event: PointerEvent) {\n stopProgrammaticScroll();\n stopInertia(); // Stop any existing momentum\n\n if (!useVirtualScrolling.value) {\n return;\n }\n\n // Only handle primary button or touch\n if (event.pointerType === 'mouse' && event.button !== 0) {\n return;\n }\n\n isPointerScrolling.value = true;\n startPointerPos = { x: event.clientX, y: event.clientY };\n lastPointerPos = { x: event.clientX, y: event.clientY };\n lastPointerTime = performance.now();\n startScrollOffset = {\n x: scrollDetails.value.scrollOffset.x,\n y: scrollDetails.value.scrollOffset.y,\n };\n\n (event.currentTarget as HTMLElement).setPointerCapture(event.pointerId);\n}\n\n/**\n * Handles pointer move events on the container to perform emulated scrolling.\n *\n * @param event - The pointer move event.\n */\nfunction handlePointerMove(event: PointerEvent) {\n if (!isPointerScrolling.value) {\n return;\n }\n\n const now = performance.now();\n const dt = now - lastPointerTime;\n\n if (dt > 0) {\n // Calculate instantaneous velocity (pixels per millisecond)\n const instantVelocity = calculateInstantaneousVelocity(lastPointerPos, { x: event.clientX, y: event.clientY }, dt);\n\n // Use a moving average for smoother velocity tracking\n velocity.x = velocity.x * 0.2 + instantVelocity.x * 0.8;\n velocity.y = velocity.y * 0.2 + instantVelocity.y * 0.8;\n }\n\n lastPointerPos = { x: event.clientX, y: event.clientY };\n lastPointerTime = now;\n\n const deltaX = startPointerPos.x - event.clientX;\n const deltaY = startPointerPos.y - event.clientY;\n\n requestAnimationFrame(() => {\n scrollToOffset(\n startScrollOffset.x + deltaX,\n startScrollOffset.y + deltaY,\n { behavior: 'auto' },\n );\n });\n}\n\n/**\n * Handles pointer up and cancel events to end emulated scrolling.\n *\n * @param event - The pointer event.\n */\nfunction handlePointerUp(event: PointerEvent) {\n if (!isPointerScrolling.value) {\n return;\n }\n\n isPointerScrolling.value = false;\n (event.currentTarget as HTMLElement).releasePointerCapture(event.pointerId);\n\n // If the user was moving fast enough, start the inertia loop\n if (Math.abs(velocity.x) > MIN_VELOCITY || Math.abs(velocity.y) > MIN_VELOCITY) {\n // avoid unwanted cross-axis drift\n if (Math.abs(velocity.x) > 4 * Math.abs(velocity.y)) {\n velocity.y = 0;\n } else if (Math.abs(velocity.y) > 4 * Math.abs(velocity.x)) {\n velocity.x = 0;\n }\n\n startInertiaAnimation();\n }\n}\n\n/**\n * Handles mouse wheel events to support high-precision scrolling for large content or virtual scrollbars.\n *\n * @param event - The wheel event.\n */\nfunction handleWheel(event: WheelEvent) {\n const { scrollOffset } = scrollDetails.value;\n stopProgrammaticScroll();\n\n if (useVirtualScrolling.value) {\n // Prevent default browser scroll as we are handling it manually\n event.preventDefault();\n\n // For large content we manually scroll to keep precision/control\n let { deltaX, deltaY } = event;\n\n if (event.shiftKey && deltaX === 0) {\n deltaX = deltaY;\n deltaY = 0;\n }\n\n scrollToOffset(scrollOffset.x + deltaX, scrollOffset.y + deltaY, { behavior: 'auto' });\n }\n}\n\n/**\n * Handles keyboard events for navigation (Home, End, Arrows, PageUp/Down).\n *\n * @param event - The keyboard event.\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 const vProps = virtualScrollProps.value;\n const sStart = vProps.stickyStart as { x: number; y: number; };\n const sEnd = vProps.stickyEnd as { x: number; y: number; };\n const pStart = vProps.scrollPaddingStart as { x: number; y: number; };\n const pEnd = vProps.scrollPaddingEnd as { x: number; y: number; };\n\n const snapModeProp = props.snap === true ? 'auto' : props.snap;\n const snapMode = (snapModeProp && snapModeProp !== 'auto')\n ? snapModeProp as 'start' | 'center' | 'end'\n : null;\n\n /**\n * Helper to find center index.\n */\n const getCenterIndex = (isX: boolean) => {\n const centerPos = (isX ? scrollOffset.x : scrollOffset.y) + (isX ? viewportSize.width : viewportSize.height) / 2;\n return isX ? getColIndexAt(centerPos) : getRowIndexAt(centerPos);\n };\n\n const { currentIndex, currentEndIndex, currentColIndex, currentEndColIndex } = scrollDetails.value;\n\n /**\n * Helper to calculate the target index for PageUp/PageDown.\n *\n * @param isVerticalAxis - True for vertical, false for horizontal.\n * @param isForward - True for forward (PageDown), false for backward (PageUp).\n */\n const getPageTarget = (isVerticalAxis: boolean, isForward: boolean) => {\n const isHorizontalAxis = !isVerticalAxis;\n const startIdx = isVerticalAxis ? currentIndex : currentColIndex;\n const endIdx = isVerticalAxis ? currentEndIndex : currentEndColIndex;\n const pageSize = Math.max(1, endIdx - startIdx);\n const maxIdx = isVerticalAxis\n ? props.items.length - 1\n : (props.columnCount ? props.columnCount - 1 : props.items.length - 1);\n\n if (isForward) {\n if (snapMode === 'center') {\n return Math.min(maxIdx, getCenterIndex(isHorizontalAxis) + pageSize);\n }\n if (snapMode === 'end') {\n return Math.min(maxIdx, endIdx + pageSize);\n }\n return endIdx; // default or snapMode === 'start'\n } else {\n // backward\n if (snapMode === 'center') {\n return Math.max(0, getCenterIndex(isHorizontalAxis) - pageSize);\n }\n if (snapMode === 'start') {\n return Math.max(0, startIdx - pageSize);\n }\n return startIdx; // default or snapMode === 'end'\n }\n };\n\n /**\n * Performs keyboard navigation for arrow keys.\n *\n * @param isVerticalAxis - True for vertical, false for horizontal.\n * @param isForward - True for forward direction (Down/Right), false for backward.\n */\n const navigate = (isVerticalAxis: boolean, isForward: boolean) => {\n const isHorizontalAxis = !isVerticalAxis;\n\n if (snapMode === 'center') {\n const centerIdx = getCenterIndex(isHorizontalAxis);\n const maxIdx = isHorizontalAxis\n ? (props.columnCount ? props.columnCount - 1 : props.items.length - 1)\n : props.items.length - 1;\n const targetIdx = isForward ? Math.min(maxIdx, centerIdx + 1) : Math.max(0, centerIdx - 1);\n scrollToIndex(isVerticalAxis ? targetIdx : null, isHorizontalAxis ? targetIdx : null, { align: 'center' });\n return;\n }\n\n if (isVerticalAxis) {\n if (isForward) {\n if (snapMode === 'start') {\n scrollToIndex(Math.min(props.items.length - 1, currentIndex + 1), null, { align: 'start' });\n } else {\n const align = snapMode || 'end';\n const viewportBottom = scrollOffset.y + viewportSize.height - (sEnd.y + pEnd.y);\n const itemBottom = getRowOffset(currentEndIndex) + getRowHeight(currentEndIndex);\n\n if (itemBottom > viewportBottom + 1) {\n scrollToIndex(currentEndIndex, null, { align });\n } else if (currentEndIndex < props.items.length - 1) {\n scrollToIndex(currentEndIndex + 1, null, { align });\n }\n }\n } else {\n // backward\n if (snapMode === 'end') {\n scrollToIndex(Math.max(0, currentEndIndex - 1), null, { align: 'end' });\n } else {\n const align = snapMode || 'start';\n const viewportTop = scrollOffset.y + sStart.y + pStart.y;\n const itemPos = getRowOffset(currentIndex);\n\n if (itemPos < viewportTop - 1) {\n scrollToIndex(currentIndex, null, { align });\n } else if (currentIndex > 0) {\n scrollToIndex(currentIndex - 1, null, { align });\n }\n }\n }\n } else {\n // Horizontal axis\n const maxColIdx = props.columnCount ? props.columnCount - 1 : props.items.length - 1;\n const isLogicalForward = isRtl.value ? !isForward : isForward;\n\n if (isLogicalForward) {\n if (snapMode === 'start') {\n scrollToIndex(null, Math.min(maxColIdx, currentColIndex + 1), { align: 'start' });\n } else {\n const align = snapMode || 'end';\n const viewportRight = scrollOffset.x + viewportSize.width - (sEnd.x + pEnd.x);\n const colEndPos = (props.columnCount ? getColumnOffset(currentEndColIndex) + getColumnWidth(currentEndColIndex) : getItemOffset(currentEndColIndex) + getItemSize(currentEndColIndex));\n\n if (colEndPos > viewportRight + 1) {\n scrollToIndex(null, currentEndColIndex, { align });\n } else if (currentEndColIndex < maxColIdx) {\n scrollToIndex(null, currentEndColIndex + 1, { align });\n }\n }\n } else {\n // backward\n if (snapMode === 'end') {\n scrollToIndex(null, Math.max(0, currentEndColIndex - 1), { align: 'end' });\n } else {\n const align = snapMode || 'start';\n const viewportLeft = scrollOffset.x + sStart.x + pStart.x;\n const colStartPos = (props.columnCount ? getColumnOffset(currentColIndex) : getItemOffset(currentColIndex));\n\n if (colStartPos < viewportLeft - 1) {\n scrollToIndex(null, currentColIndex, { align });\n } else if (currentColIndex > 0) {\n scrollToIndex(null, currentColIndex - 1, { align });\n }\n }\n }\n }\n };\n\n switch (event.key) {\n case 'Home': {\n event.preventDefault();\n stopProgrammaticScroll();\n const distance = Math.max(scrollOffset.x, scrollOffset.y);\n const viewport = props.direction === 'horizontal' ? viewportSize.width : viewportSize.height;\n const behavior = distance > 10 * viewport ? 'auto' : 'smooth';\n\n scrollToIndex(0, 0, { behavior, align: 'start' });\n break;\n }\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 const { totalSize } = scrollDetails.value;\n const distance = Math.max(\n totalSize.width - scrollOffset.x - viewportSize.width,\n totalSize.height - scrollOffset.y - viewportSize.height,\n );\n const viewport = props.direction === 'horizontal' ? viewportSize.width : viewportSize.height;\n const behavior = distance > 10 * viewport ? 'auto' : 'smooth';\n\n if (props.direction === 'both') {\n scrollToIndex(lastItemIndex, lastColIndex, { behavior, align: 'end' });\n } else {\n scrollToIndex(\n props.direction === 'vertical' ? lastItemIndex : 0,\n props.direction === 'horizontal' ? lastItemIndex : 0,\n { behavior, align: 'end' },\n );\n }\n break;\n }\n case 'ArrowUp':\n event.preventDefault();\n stopProgrammaticScroll();\n if (isVertical) {\n navigate(true, false);\n }\n break;\n case 'ArrowDown':\n event.preventDefault();\n stopProgrammaticScroll();\n if (isVertical) {\n navigate(true, true);\n }\n break;\n case 'ArrowLeft':\n event.preventDefault();\n stopProgrammaticScroll();\n if (isHorizontal) {\n navigate(false, false);\n }\n break;\n case 'ArrowRight':\n event.preventDefault();\n stopProgrammaticScroll();\n if (isHorizontal) {\n navigate(false, true);\n }\n break;\n case 'PageUp':\n event.preventDefault();\n stopProgrammaticScroll();\n if (props.direction === 'horizontal') {\n scrollToIndex(null, getPageTarget(false, false), { align: snapMode || 'end' });\n } else {\n scrollToIndex(getPageTarget(true, false), null, { align: snapMode || 'end' });\n }\n break;\n case 'PageDown':\n event.preventDefault();\n stopProgrammaticScroll();\n if (props.direction === 'horizontal') {\n scrollToIndex(null, getPageTarget(false, true), { align: snapMode || 'start' });\n } else {\n scrollToIndex(getPageTarget(true, true), null, { align: snapMode || 'start' });\n }\n break;\n }\n}\n\nonUnmounted(() => {\n hostResizeObserver?.disconnect();\n itemResizeObserver?.disconnect();\n extraResizeObserver?.disconnect();\n});\n\nconst containerStyle = computed(() => {\n const base: Record<string, string | number | undefined> = {\n ...(props.direction !== 'vertical' ? { whiteSpace: 'nowrap' as const } : {}),\n };\n\n if (showVirtualScrollbars.value || !isWindowContainer.value) {\n base.overflow = 'auto';\n }\n\n if (useVirtualScrolling.value) {\n base.touchAction = 'none';\n }\n\n if (isWindowContainer.value) {\n return base;\n }\n\n if (props.containerTag === 'table') {\n return {\n ...base,\n display: 'block',\n minInlineSize: props.direction === 'vertical' ? '100%' : 'auto',\n };\n }\n\n return base;\n});\n\n/**\n * Internal helper to generate consistent ScrollbarSlotProps.\n *\n * @param axis - The scroll axis.\n * @param totalSize - Total scrollable size (DU).\n * @param position - Current scroll position (DU).\n * @param viewportSize - Current viewport size (DU).\n * @param scrollToOffsetCallback - Callback to perform scroll.\n * @param scrollbar - Scrollbar state from useVirtualScrollbar.\n * @returns Props for the scrollbar slot or null if content fits.\n */\nfunction getScrollbarSlotProps(\n axis: 'vertical' | 'horizontal',\n totalSize: number,\n position: number,\n viewportSize: number,\n scrollToOffsetCallback: (offset: number) => void,\n scrollbar: ReturnType<typeof useVirtualScrollbar>,\n): ScrollbarSlotProps | null {\n if (totalSize <= viewportSize) {\n return null;\n }\n\n return {\n axis,\n positionPercent: scrollbar.positionPercent.value,\n viewportPercent: scrollbar.viewportPercent.value,\n thumbSizePercent: scrollbar.thumbSizePercent.value,\n thumbPositionPercent: scrollbar.thumbPositionPercent.value,\n trackProps: scrollbar.trackProps.value,\n thumbProps: scrollbar.thumbProps.value,\n scrollbarProps: {\n axis,\n totalSize,\n position,\n viewportSize,\n scrollToOffset: scrollToOffsetCallback,\n containerId: containerId.value,\n isRtl: isRtl.value,\n ariaLabel: `${ axis === 'vertical' ? 'Vertical' : 'Horizontal' } scroll`,\n },\n isDragging: scrollbar.isDragging.value,\n };\n}\n\nconst verticalScrollbarProps = computed(() => {\n if (props.direction === 'horizontal') {\n return null;\n }\n const { displayViewportSize, displayScrollOffset } = scrollDetails.value;\n return getScrollbarSlotProps(\n 'vertical',\n renderedHeight.value,\n displayScrollOffset.y,\n displayViewportSize.height,\n handleVerticalScrollbarScrollToOffset,\n verticalScrollbar,\n );\n});\n\nconst horizontalScrollbarProps = computed(() => {\n if (props.direction === 'vertical') {\n return null;\n }\n const { displayViewportSize, displayScrollOffset } = scrollDetails.value;\n return getScrollbarSlotProps(\n 'horizontal',\n renderedWidth.value,\n displayScrollOffset.x,\n displayViewportSize.width,\n handleHorizontalScrollbarScrollToOffset,\n horizontalScrollbar,\n );\n});\n\nconst wrapperStyle = computed(() => {\n const isHorizontal = props.direction === 'horizontal';\n const isVertical = props.direction === 'vertical';\n const isBoth = props.direction === 'both';\n\n const style: Record<string, string | number | undefined> = {\n inlineSize: isVertical ? '100%' : `${ renderedVirtualWidth.value }px`,\n blockSize: isHorizontal ? '100%' : `${ renderedVirtualHeight.value }px`,\n };\n\n if (!isHydrated.value) {\n style.display = 'flex';\n style.flexDirection = isHorizontal ? 'row' : 'column';\n if ((isHorizontal || isBoth) && props.columnGap) {\n style.columnGap = `${ props.columnGap }px`;\n }\n if ((isVertical || isBoth) && props.gap) {\n style.rowGap = `${ props.gap }px`;\n }\n }\n\n return style;\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' : `${ renderedVirtualWidth.value }px`,\n blockSize: props.direction === 'horizontal' ? '1px' : `${ renderedVirtualHeight.value }px`,\n}));\n\n/**\n * Calculates the final style object for an item, including position and dimensions.\n *\n * @param item - The rendered item state.\n * @returns CSS style object.\n */\nfunction getItemStyle(item: RenderedItem<T>) {\n const style = calculateItemStyle({\n containerTag: props.containerTag || 'div',\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 isRtl: isRtl.value,\n });\n\n if (!isHydrated.value && props.direction === 'both') {\n style.display = 'flex';\n if (props.columnGap) {\n style.columnGap = `${ props.columnGap }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\nconst effectiveRole = computed(() => {\n if (props.role) {\n return props.role;\n }\n return isTable.value ? null : (props.direction === 'both' ? 'grid' : 'list');\n});\n\nconst isGrid = computed(() => effectiveRole.value === 'grid' || isTable.value);\n\nconst containerRole = computed(() => isTable.value\n ? null\n : ((props.ariaLabel || props.ariaLabelledby) ? 'region' : 'none'));\nconst wrapperRole = computed(() => isTable.value ? null : effectiveRole.value);\nconst internalItemRole = computed(() => {\n if (isGrid.value) {\n return 'row';\n }\n\n const role = effectiveRole.value;\n if (role === 'tree') {\n return 'treeitem';\n }\n if (role === 'listbox') {\n return 'option';\n }\n if (role === 'menu') {\n return 'menuitem';\n }\n return 'listitem';\n});\nconst itemRole = computed(() => props.itemRole != null ? props.itemRole : internalItemRole.value);\nconst cellRole = computed(() => {\n if (props.role === 'grid' || (!props.role && props.direction === 'both')) {\n return 'gridcell';\n }\n return isTable.value ? 'cell' : null;\n});\n\nconst shouldBindItemAria = computed(() => {\n const role = itemRole.value;\n return role == null || (role !== 'none' && role !== 'presentation');\n});\n\nconst rootAriaProps = computed(() => ({\n 'aria-label': props.ariaLabel,\n 'aria-labelledby': props.ariaLabelledby,\n 'aria-busy': props.loading ? 'true' : undefined,\n}));\n\nconst wrapperAriaProps = computed(() => {\n const aria: Record<string, string | number | undefined> = {};\n\n const role = effectiveRole.value;\n const supportsOrientation = role && [ 'grid', 'tree', 'listbox', 'menu', 'tablist' ].includes(role);\n\n if (supportsOrientation) {\n aria[ 'aria-orientation' ] = props.direction === 'both' ? undefined : props.direction;\n }\n\n if (isGrid.value) {\n aria[ 'aria-rowcount' ] = props.items.length;\n if (props.columnCount > 0) {\n aria[ 'aria-colcount' ] = props.columnCount;\n }\n }\n\n return aria;\n});\n\nfunction getItemAriaProps(index: number) {\n const aria: Record<string, string | number | undefined> = {};\n\n if (isGrid.value) {\n aria[ 'aria-rowindex' ] = index + 1;\n } else {\n aria[ 'aria-setsize' ] = props.items.length;\n aria[ 'aria-posinset' ] = index + 1;\n }\n\n const role = itemRole.value;\n if (role !== null) {\n aria.role = (role === 'none' || role === 'presentation')\n ? internalItemRole.value\n : role;\n }\n\n return aria;\n}\n\nfunction getCellAriaProps(colIndex: number) {\n const role = cellRole.value;\n if (!role) {\n return {};\n }\n\n const aria: Record<string, string | number | undefined> = {\n role,\n };\n\n if (isGrid.value) {\n aria[ 'aria-colindex' ] = colIndex + 1;\n }\n\n return aria;\n}\n\ndefineExpose({\n ...toRefs(props),\n\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 * Helper to get the height of a specific row.\n * @param index - The row index.\n * @see useVirtualScroll\n */\n getRowHeight,\n\n /**\n * Helper to get ARIA attributes for a cell.\n * @param colIndex - The column index.\n */\n getCellAriaProps,\n\n /**\n * Helper to get ARIA attributes for an item.\n * @param index - The item index.\n */\n getItemAriaProps,\n\n /**\n * Helper to get the virtual offset of a specific row.\n * @param index - The row index.\n * @see useVirtualScroll\n */\n getRowOffset,\n\n /**\n * Helper to get the virtual offset of a specific column.\n * @param index - The column index.\n * @see useVirtualScroll\n */\n getColumnOffset,\n\n /**\n * Helper to get the virtual offset of a specific item.\n * @param index - The item index.\n * @see useVirtualScroll\n */\n getItemOffset,\n\n /**\n * Helper to get the size of a specific item along the scroll axis.\n * @param index - The item index.\n * @see useVirtualScroll\n */\n getItemSize,\n\n /**\n * Helper to get the row (or item) index at a specific vertical (or horizontal in horizontal mode) virtual offset (VU).\n * @param offset - The virtual pixel offset.\n * @see useVirtualScroll\n */\n getRowIndexAt,\n\n /**\n * Helper to get the column index at a specific horizontal virtual offset (VU).\n * @param offset - The virtual pixel offset.\n * @see useVirtualScroll\n */\n getColIndexAt,\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. Optional.\n * @param colIndex - The column index to scroll to. Pass null to only scroll vertically. Optional.\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 stopProgrammaticScroll();\n stopInertia();\n },\n\n /**\n * Detects the current direction (LTR/RTL) of the scroll container.\n */\n updateDirection,\n\n /**\n * Whether the scroll container is in Right-to-Left (RTL) mode.\n */\n isRtl,\n\n /**\n * Whether the component has finished its first client-side mount and hydration.\n */\n isHydrated,\n\n /**\n * Coordinate scaling factor for X axis.\n */\n scaleX,\n\n /**\n * Coordinate scaling factor for Y axis.\n */\n scaleY,\n\n /**\n * Physical width of the content in the DOM (clamped to browser limits).\n */\n renderedWidth,\n\n /**\n * Physical height of the content in the DOM (clamped to browser limits).\n */\n renderedHeight,\n\n /**\n * Absolute offset of the component within its container.\n */\n componentOffset,\n\n /**\n * Properties for the vertical scrollbar.\n * Useful when building custom scrollbar interfaces.\n */\n scrollbarPropsVertical: verticalScrollbarProps,\n\n /**\n * Properties for the horizontal scrollbar.\n * Useful when building custom scrollbar interfaces.\n */\n scrollbarPropsHorizontal: horizontalScrollbarProps,\n});\n</script>\n\n<template>\n <component\n :is=\"containerTag\"\n :id=\"containerId\"\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 'virtual-scroll--hide-scrollbar': showVirtualScrollbars,\n },\n ]\"\n :style=\"containerStyle\"\n tabindex=\"0\"\n :role=\"isTable ? undefined : containerRole\"\n v-bind=\"isTable ? { ...rootAriaProps, ...wrapperAriaProps } : rootAriaProps\"\n @keydown=\"handleKeyDown\"\n @pointerdown=\"handlePointerDown\"\n @pointermove=\"handlePointerMove\"\n @pointerup=\"handlePointerUp\"\n @pointercancel=\"handlePointerUp\"\n >\n <div\n v-if=\"showVirtualScrollbars\"\n class=\"virtual-scroll-scrollbar-container\"\n aria-hidden=\"true\"\n >\n <div\n class=\"virtual-scroll-scrollbar-viewport\"\n :style=\"{\n 'inlineSize': `${ scrollDetails.displayViewportSize.width }px`,\n 'blockSize': `${ scrollDetails.displayViewportSize.height }px`,\n '--vsi-scrollbar-has-cross-gap': direction === 'both' ? 1 : 0,\n }\"\n >\n <slot v-if=\"slots.scrollbar && verticalScrollbarProps\" name=\"scrollbar\" v-bind=\"verticalScrollbarProps\" />\n <VirtualScrollbar v-else-if=\"verticalScrollbarProps\" v-bind=\"verticalScrollbarProps.scrollbarProps\" />\n\n <slot v-if=\"slots.scrollbar && horizontalScrollbarProps\" name=\"scrollbar\" v-bind=\"horizontalScrollbarProps\" />\n <VirtualScrollbar v-else-if=\"horizontalScrollbarProps\" v-bind=\"horizontalScrollbarProps.scrollbarProps\" />\n </div>\n </div>\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 :role=\"isTable ? undefined : 'none'\"\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 :role=\"isTable ? undefined : wrapperRole\"\n v-bind=\"isTable ? {} : wrapperAriaProps\"\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 v-bind=\"shouldBindItemAria ? getItemAriaProps(renderedItem.index) : { role: 'none' }\"\n >\n <slot\n name=\"item\"\n :item=\"renderedItem.item\"\n :index=\"renderedItem.index\"\n :get-item-aria-props=\"getItemAriaProps\"\n :column-range=\"slotColumnRange\"\n :get-column-width=\"getColumnWidth\"\n :get-cell-aria-props=\"getCellAriaProps\"\n :gap=\"props.gap\"\n :column-gap=\"props.columnGap\"\n :is-sticky=\"renderedItem.isSticky\"\n :is-sticky-active=\"renderedItem.isStickyActive\"\n :is-sticky-active-x=\"renderedItem.isStickyActiveX\"\n :is-sticky-active-y=\"renderedItem.isStickyActiveY\"\n :offset=\"renderedItem.offset\"\n />\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 aria-live=\"polite\"\n aria-atomic=\"true\"\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 :role=\"isTable ? undefined : 'none'\"\n >\n <slot name=\"footer\" />\n </component>\n </component>\n</template>\n\n<style scoped>\n@layer components {\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 &.virtual-scroll--hide-scrollbar {\n scrollbar-width: none;\n -ms-overflow-style: none;\n\n &::-webkit-scrollbar {\n display: none;\n }\n }\n\n &.virtual-scroll--horizontal,\n &.virtual-scroll--both {\n white-space: nowrap;\n }\n }\n\n .virtual-scroll-scrollbar-container {\n position: sticky;\n inset-block-start: 0;\n inset-inline-start: 0;\n inline-size: 100%;\n block-size: 0;\n z-index: 30;\n pointer-events: none;\n overflow: visible;\n }\n\n .virtual-scroll-scrollbar-viewport {\n position: absolute;\n inset-block-start: 0;\n inset-inline-start: 0;\n pointer-events: none;\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}\n</style>\n","<script setup lang=\"ts\" generic=\"T\">\n/**\n * A high-performance virtual scrolling component for Vue 3.\n * Supports large lists and grids by only rendering visible items and using coordinate scaling.\n * Features include sticky headers/footers, RTL support, custom scrollbars, and scroll restoration.\n */\nimport type {\n ItemSlotProps,\n RenderedItem,\n ScrollAlignment,\n ScrollbarSlotProps,\n ScrollDetails,\n ScrollToIndexOptions,\n VirtualScrollComponentProps,\n VirtualScrollProps,\n} from '../types';\nimport type { VNodeChild } from 'vue';\n\nimport { computed, nextTick, onMounted, onUnmounted, ref, toRefs, useId, watch } from 'vue';\n\nimport {\n useVirtualScroll,\n} from '../composables/useVirtualScroll';\nimport { useVirtualScrollbar } from '../composables/useVirtualScrollbar';\nimport { getPaddingX, getPaddingY, isWindowLike } from '../utils/scroll';\nimport {\n calculateInertiaStep,\n calculateInstantaneousVelocity,\n calculateItemStyle,\n displayToVirtual,\n} from '../utils/virtual-scroll-logic';\nimport VirtualScrollbar from './VirtualScrollbar.vue';\n\nexport interface Props<T = unknown> extends VirtualScrollComponentProps<T> {}\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 virtualScrollbar: false,\n itemRole: undefined,\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: ItemSlotProps<T>) => 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 /**\n * Scoped slot for rendering custom scrollbars.\n * If provided, the default VirtualScrollbar is not rendered.\n */\n scrollbar?: (props: ScrollbarSlotProps) => 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 instanceId = useId();\n\n/**\n * Unique ID for the scrollable container.\n * Used for accessibility (aria-controls) and to target the element in DOM.\n */\nconst containerId = computed(() => `vs-container-${ instanceId }`);\n\nconst measuredPaddingStart = ref(0);\nconst measuredPaddingEnd = ref(0);\n\nconst effectiveContainer = computed(() => (props.container === undefined ? hostRef.value : props.container));\n\nconst isHeaderFooterInsideContainer = computed(() => {\n const container = effectiveContainer.value;\n return container === hostRef.value || isWindowLike(container);\n});\n\nconst virtualScrollProps = computed(() => {\n /* Trigger re-evaluation on items array mutations */\n // eslint-disable-next-line ts/no-unused-expressions\n props.items.length;\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: effectiveContainer.value,\n hostElement: wrapperRef.value,\n hostRef: hostRef.value,\n ssrRange: props.ssrRange,\n columnCount: props.columnCount,\n columnWidth: props.columnWidth,\n scrollPaddingStart: {\n x: getPaddingX(props.scrollPaddingStart, props.direction),\n y: getPaddingY(props.scrollPaddingStart, props.direction),\n },\n scrollPaddingEnd: {\n x: getPaddingX(props.scrollPaddingEnd, props.direction),\n y: getPaddingY(props.scrollPaddingEnd, props.direction),\n },\n flowPaddingStart: {\n x: 0,\n y: props.stickyHeader ? 0 : measuredPaddingStart.value,\n },\n flowPaddingEnd: {\n x: 0,\n y: props.stickyFooter ? 0 : measuredPaddingEnd.value,\n },\n stickyStart: {\n x: 0,\n y: props.stickyHeader && isHeaderFooterInsideContainer.value ? measuredPaddingStart.value : 0,\n },\n stickyEnd: {\n x: 0,\n y: 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 snap: props.snap,\n } as VirtualScrollProps<T>;\n});\n\nconst {\n isHydrated,\n isRtl,\n columnRange,\n renderedItems,\n scrollDetails,\n renderedHeight,\n renderedWidth,\n getColumnWidth,\n getRowHeight,\n scrollToIndex,\n scrollToOffset,\n updateHostOffset,\n updateItemSizes,\n updateDirection,\n getItemOffset,\n getRowOffset,\n getColumnOffset,\n getItemSize,\n refresh: coreRefresh,\n stopProgrammaticScroll,\n scaleX,\n scaleY,\n isWindowContainer,\n componentOffset,\n renderedVirtualWidth,\n renderedVirtualHeight,\n getRowIndexAt,\n getColIndexAt,\n} = useVirtualScroll(virtualScrollProps);\n\nconst useVirtualScrolling = computed(() => scaleX.value !== 1 || scaleY.value !== 1);\n\nconst showVirtualScrollbars = computed(() => {\n if (isWindowContainer.value) {\n return false;\n }\n return props.virtualScrollbar === true || scaleX.value !== 1 || scaleY.value !== 1;\n});\n\nfunction handleVerticalScrollbarScrollToOffset(offset: number) {\n const { displayViewportSize } = scrollDetails.value;\n const scrollableRange = renderedHeight.value - displayViewportSize.height;\n if (offset >= scrollableRange - 0.5) {\n scrollToOffset(null, Number.POSITIVE_INFINITY);\n } else {\n const virtualOffset = displayToVirtual(offset, componentOffset.y, scaleY.value);\n scrollToOffset(null, virtualOffset);\n }\n}\n\nfunction handleHorizontalScrollbarScrollToOffset(offset: number) {\n const { displayViewportSize } = scrollDetails.value;\n const scrollableRange = renderedWidth.value - displayViewportSize.width;\n if (offset >= scrollableRange - 0.5) {\n scrollToOffset(Number.POSITIVE_INFINITY, null);\n } else {\n const virtualOffset = displayToVirtual(offset, componentOffset.x, scaleX.value);\n scrollToOffset(virtualOffset, null);\n }\n}\n\nconst verticalScrollbar = useVirtualScrollbar(computed(() => ({\n axis: 'vertical' as const,\n totalSize: renderedHeight.value,\n position: scrollDetails.value.displayScrollOffset.y,\n viewportSize: scrollDetails.value.displayViewportSize.height,\n scrollToOffset: handleVerticalScrollbarScrollToOffset,\n containerId: containerId.value,\n isRtl: isRtl.value,\n})));\n\nconst horizontalScrollbar = useVirtualScrollbar(computed(() => ({\n axis: 'horizontal' as const,\n totalSize: renderedWidth.value,\n position: scrollDetails.value.displayScrollOffset.x,\n viewportSize: scrollDetails.value.displayViewportSize.width,\n scrollToOffset: handleHorizontalScrollbarScrollToOffset,\n containerId: containerId.value,\n isRtl: isRtl.value,\n})));\n\nconst slotColumnRange = computed(() => {\n if (props.direction !== 'both') {\n return columnRange.value;\n }\n return {\n ...columnRange.value,\n padStart: 0,\n padEnd: 0,\n };\n});\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 updateDirection();\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 || !details) {\n return;\n }\n emit('scroll', details);\n\n if (\n !oldDetails\n || !oldDetails.range\n || !oldDetails.columnRange\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' && details.totalSize) {\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' && details.totalSize) {\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 && scrollDetails.value?.range && scrollDetails.value?.columnRange) {\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\nfunction watchExtraRef(refEl: typeof headerRef, measuredValue: typeof measuredPaddingStart) {\n watch(refEl, (newEl, oldEl) => {\n if (oldEl) {\n extraResizeObserver?.unobserve(oldEl);\n }\n if (newEl) {\n extraResizeObserver?.observe(newEl);\n } else {\n measuredValue.value = 0;\n }\n }, { immediate: true });\n}\n\nwatchExtraRef(headerRef, measuredPaddingStart);\nwatchExtraRef(footerRef, measuredPaddingEnd);\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 observeItem(el, true);\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\nwatch([ hostRef, useVirtualScrolling ], ([ host, virtual ], [ oldHost, oldVirtual ]) => {\n const needsUpdate = host !== oldHost || virtual !== oldVirtual;\n if (oldHost && needsUpdate) {\n oldHost.removeEventListener('wheel', handleWheel);\n }\n if (host && needsUpdate) {\n host.addEventListener('wheel', handleWheel, { passive: !virtual });\n }\n}, { immediate: true });\n\n/**\n * Helper to manage ResizeObserver for an item and its optional cells.\n *\n * @param el - The item element.\n * @param isObserve - True to observe, false to unobserve.\n */\nfunction observeItem(el: HTMLElement, isObserve: boolean) {\n const method = isObserve ? 'observe' : 'unobserve';\n itemResizeObserver?.[ method ](el);\n if (props.direction === 'both' && el.children.length > 0) {\n el.querySelectorAll('[data-col-index]').forEach((c) => itemResizeObserver?.[ method ](c));\n }\n}\n\n/**\n * Callback ref to track and measure item elements.\n *\n * @param el - The element or null if unmounting.\n * @param index - The original index of the item.\n */\nfunction setItemRef(el: unknown, index: number) {\n if (el) {\n const htmlEl = el as HTMLElement;\n itemRefs.set(index, htmlEl);\n observeItem(htmlEl, true);\n } else {\n const oldEl = itemRefs.get(index);\n if (oldEl) {\n observeItem(oldEl, false);\n itemRefs.delete(index);\n }\n }\n}\n\n/**\n * State for inertia scrolling\n */\nconst isPointerScrolling = ref(false);\nlet startPointerPos = { x: 0, y: 0 };\nlet startScrollOffset = { x: 0, y: 0 };\nlet lastPointerPos = { x: 0, y: 0 };\nlet lastPointerTime = 0;\nlet velocity = { x: 0, y: 0 };\nlet inertiaAnimationFrame: number | null = null;\n\n// Friction constant (0.9 to 0.98 is usually best)\nconst FRICTION = 0.95;\n// Minimum velocity to continue the animation\nconst MIN_VELOCITY = 0.1;\n\n/**\n * Recursively animates the scroll offset based on velocity and friction.\n */\nfunction startInertiaAnimation() {\n const step = () => {\n const { nextVelocity, delta } = calculateInertiaStep(velocity, FRICTION);\n velocity.x = nextVelocity.x;\n velocity.y = nextVelocity.y;\n\n // Calculate the new scroll offset\n const { x: currentX, y: currentY } = scrollDetails.value.scrollOffset;\n\n // Move the scroll position by the current velocity\n scrollToOffset(\n currentX + delta.x,\n currentY + delta.y,\n { behavior: 'auto' },\n );\n\n // Continue animation if we haven't slowed down to a halt\n if (Math.abs(velocity.x) > MIN_VELOCITY || Math.abs(velocity.y) > MIN_VELOCITY) {\n inertiaAnimationFrame = requestAnimationFrame(step);\n } else {\n stopInertia();\n }\n };\n\n inertiaAnimationFrame = requestAnimationFrame(step);\n}\n\n/**\n * Stops any ongoing inertia animation\n */\nfunction stopInertia() {\n if (inertiaAnimationFrame !== null) {\n cancelAnimationFrame(inertiaAnimationFrame);\n inertiaAnimationFrame = null;\n }\n velocity = { x: 0, y: 0 };\n}\n\n/**\n * Handles pointer down events on the container to start emulated scrolling when scaling is active.\n *\n * @param event - The pointer down event.\n */\nfunction handlePointerDown(event: PointerEvent) {\n stopProgrammaticScroll();\n stopInertia(); // Stop any existing momentum\n\n if (!useVirtualScrolling.value) {\n return;\n }\n\n // Only handle primary button or touch\n if (event.pointerType === 'mouse' && event.button !== 0) {\n return;\n }\n\n isPointerScrolling.value = true;\n startPointerPos = { x: event.clientX, y: event.clientY };\n lastPointerPos = { x: event.clientX, y: event.clientY };\n lastPointerTime = performance.now();\n startScrollOffset = {\n x: scrollDetails.value.scrollOffset.x,\n y: scrollDetails.value.scrollOffset.y,\n };\n\n (event.currentTarget as HTMLElement).setPointerCapture(event.pointerId);\n}\n\n/**\n * Handles pointer move events on the container to perform emulated scrolling.\n *\n * @param event - The pointer move event.\n */\nfunction handlePointerMove(event: PointerEvent) {\n if (!isPointerScrolling.value) {\n return;\n }\n\n const now = performance.now();\n const dt = now - lastPointerTime;\n\n if (dt > 0) {\n // Calculate instantaneous velocity (pixels per millisecond)\n const instantVelocity = calculateInstantaneousVelocity(lastPointerPos, { x: event.clientX, y: event.clientY }, dt);\n\n // Use a moving average for smoother velocity tracking\n velocity.x = velocity.x * 0.2 + instantVelocity.x * 0.8;\n velocity.y = velocity.y * 0.2 + instantVelocity.y * 0.8;\n }\n\n lastPointerPos = { x: event.clientX, y: event.clientY };\n lastPointerTime = now;\n\n const deltaX = startPointerPos.x - event.clientX;\n const deltaY = startPointerPos.y - event.clientY;\n\n requestAnimationFrame(() => {\n scrollToOffset(\n startScrollOffset.x + deltaX,\n startScrollOffset.y + deltaY,\n { behavior: 'auto' },\n );\n });\n}\n\n/**\n * Handles pointer up and cancel events to end emulated scrolling.\n *\n * @param event - The pointer event.\n */\nfunction handlePointerUp(event: PointerEvent) {\n if (!isPointerScrolling.value) {\n return;\n }\n\n isPointerScrolling.value = false;\n (event.currentTarget as HTMLElement).releasePointerCapture(event.pointerId);\n\n // If the user was moving fast enough, start the inertia loop\n if (Math.abs(velocity.x) > MIN_VELOCITY || Math.abs(velocity.y) > MIN_VELOCITY) {\n // avoid unwanted cross-axis drift\n if (Math.abs(velocity.x) > 4 * Math.abs(velocity.y)) {\n velocity.y = 0;\n } else if (Math.abs(velocity.y) > 4 * Math.abs(velocity.x)) {\n velocity.x = 0;\n }\n\n startInertiaAnimation();\n }\n}\n\n/**\n * Handles mouse wheel events to support high-precision scrolling for large content or virtual scrollbars.\n *\n * @param event - The wheel event.\n */\nfunction handleWheel(event: WheelEvent) {\n const { scrollOffset } = scrollDetails.value;\n stopProgrammaticScroll();\n\n if (useVirtualScrolling.value) {\n // Prevent default browser scroll as we are handling it manually\n event.preventDefault();\n\n // For large content we manually scroll to keep precision/control\n let { deltaX, deltaY } = event;\n\n if (event.shiftKey && deltaX === 0) {\n deltaX = deltaY;\n deltaY = 0;\n }\n\n scrollToOffset(scrollOffset.x + deltaX, scrollOffset.y + deltaY, { behavior: 'auto' });\n }\n}\n\n/**\n * Handles keyboard events for navigation (Home, End, Arrows, PageUp/Down).\n *\n * @param event - The keyboard event.\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 const vProps = virtualScrollProps.value;\n const sStart = vProps.stickyStart as { x: number; y: number; };\n const sEnd = vProps.stickyEnd as { x: number; y: number; };\n const pStart = vProps.scrollPaddingStart as { x: number; y: number; };\n const pEnd = vProps.scrollPaddingEnd as { x: number; y: number; };\n\n const snapModeProp = props.snap === true ? 'auto' : props.snap;\n const snapMode = (snapModeProp && snapModeProp !== 'auto')\n ? snapModeProp as 'start' | 'center' | 'end'\n : null;\n\n /**\n * Helper to find center index.\n */\n const getCenterIndex = (isX: boolean) => {\n const centerPos = (isX ? scrollOffset.x : scrollOffset.y) + (isX ? viewportSize.width : viewportSize.height) / 2;\n return isX ? getColIndexAt(centerPos) : getRowIndexAt(centerPos);\n };\n\n const { currentIndex, currentEndIndex, currentColIndex, currentEndColIndex } = scrollDetails.value;\n\n /**\n * Helper to calculate the target index for PageUp/PageDown.\n *\n * @param isVerticalAxis - True for vertical, false for horizontal.\n * @param isForward - True for forward (PageDown), false for backward (PageUp).\n */\n const getPageTarget = (isVerticalAxis: boolean, isForward: boolean) => {\n const isHorizontalAxis = !isVerticalAxis;\n const startIdx = isVerticalAxis ? currentIndex : currentColIndex;\n const endIdx = isVerticalAxis ? currentEndIndex : currentEndColIndex;\n const pageSize = Math.max(1, endIdx - startIdx);\n const maxIdx = isVerticalAxis\n ? props.items.length - 1\n : (props.columnCount ? props.columnCount - 1 : props.items.length - 1);\n\n if (isForward) {\n if (snapMode === 'center') {\n return Math.min(maxIdx, getCenterIndex(isHorizontalAxis) + pageSize);\n }\n if (snapMode === 'end') {\n return Math.min(maxIdx, endIdx + pageSize);\n }\n return endIdx; // default or snapMode === 'start'\n } else {\n // backward\n if (snapMode === 'center') {\n return Math.max(0, getCenterIndex(isHorizontalAxis) - pageSize);\n }\n if (snapMode === 'start') {\n return Math.max(0, startIdx - pageSize);\n }\n return startIdx; // default or snapMode === 'end'\n }\n };\n\n /**\n * Performs keyboard navigation for arrow keys.\n *\n * @param isVerticalAxis - True for vertical, false for horizontal.\n * @param isForward - True for forward direction (Down/Right), false for backward.\n */\n const navigate = (isVerticalAxis: boolean, isForward: boolean) => {\n const isHorizontalAxis = !isVerticalAxis;\n\n if (snapMode === 'center') {\n const centerIdx = getCenterIndex(isHorizontalAxis);\n const maxIdx = isHorizontalAxis\n ? (props.columnCount ? props.columnCount - 1 : props.items.length - 1)\n : props.items.length - 1;\n const targetIdx = isForward ? Math.min(maxIdx, centerIdx + 1) : Math.max(0, centerIdx - 1);\n scrollToIndex(isVerticalAxis ? targetIdx : null, isHorizontalAxis ? targetIdx : null, { align: 'center' });\n return;\n }\n\n if (isVerticalAxis) {\n if (isForward) {\n if (snapMode === 'start') {\n scrollToIndex(Math.min(props.items.length - 1, currentIndex + 1), null, { align: 'start' });\n } else {\n const align = snapMode || 'end';\n const viewportBottom = scrollOffset.y + viewportSize.height - (sEnd.y + pEnd.y);\n const itemBottom = getRowOffset(currentEndIndex) + getRowHeight(currentEndIndex);\n\n if (itemBottom > viewportBottom + 1) {\n scrollToIndex(currentEndIndex, null, { align });\n } else if (currentEndIndex < props.items.length - 1) {\n scrollToIndex(currentEndIndex + 1, null, { align });\n }\n }\n } else {\n // backward\n if (snapMode === 'end') {\n scrollToIndex(Math.max(0, currentEndIndex - 1), null, { align: 'end' });\n } else {\n const align = snapMode || 'start';\n const viewportTop = scrollOffset.y + sStart.y + pStart.y;\n const itemPos = getRowOffset(currentIndex);\n\n if (itemPos < viewportTop - 1) {\n scrollToIndex(currentIndex, null, { align });\n } else if (currentIndex > 0) {\n scrollToIndex(currentIndex - 1, null, { align });\n }\n }\n }\n } else {\n // Horizontal axis\n const maxColIdx = props.columnCount ? props.columnCount - 1 : props.items.length - 1;\n const isLogicalForward = isRtl.value ? !isForward : isForward;\n\n if (isLogicalForward) {\n if (snapMode === 'start') {\n scrollToIndex(null, Math.min(maxColIdx, currentColIndex + 1), { align: 'start' });\n } else {\n const align = snapMode || 'end';\n const viewportRight = scrollOffset.x + viewportSize.width - (sEnd.x + pEnd.x);\n const colEndPos = (props.columnCount ? getColumnOffset(currentEndColIndex) + getColumnWidth(currentEndColIndex) : getItemOffset(currentEndColIndex) + getItemSize(currentEndColIndex));\n\n if (colEndPos > viewportRight + 1) {\n scrollToIndex(null, currentEndColIndex, { align });\n } else if (currentEndColIndex < maxColIdx) {\n scrollToIndex(null, currentEndColIndex + 1, { align });\n }\n }\n } else {\n // backward\n if (snapMode === 'end') {\n scrollToIndex(null, Math.max(0, currentEndColIndex - 1), { align: 'end' });\n } else {\n const align = snapMode || 'start';\n const viewportLeft = scrollOffset.x + sStart.x + pStart.x;\n const colStartPos = (props.columnCount ? getColumnOffset(currentColIndex) : getItemOffset(currentColIndex));\n\n if (colStartPos < viewportLeft - 1) {\n scrollToIndex(null, currentColIndex, { align });\n } else if (currentColIndex > 0) {\n scrollToIndex(null, currentColIndex - 1, { align });\n }\n }\n }\n }\n };\n\n switch (event.key) {\n case 'Home': {\n event.preventDefault();\n stopProgrammaticScroll();\n const distance = Math.max(scrollOffset.x, scrollOffset.y);\n const viewport = props.direction === 'horizontal' ? viewportSize.width : viewportSize.height;\n const behavior = distance > 10 * viewport ? 'auto' : 'smooth';\n\n scrollToIndex(0, 0, { behavior, align: 'start' });\n break;\n }\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 const { totalSize } = scrollDetails.value;\n const distance = Math.max(\n totalSize.width - scrollOffset.x - viewportSize.width,\n totalSize.height - scrollOffset.y - viewportSize.height,\n );\n const viewport = props.direction === 'horizontal' ? viewportSize.width : viewportSize.height;\n const behavior = distance > 10 * viewport ? 'auto' : 'smooth';\n\n if (props.direction === 'both') {\n scrollToIndex(lastItemIndex, lastColIndex, { behavior, align: 'end' });\n } else {\n scrollToIndex(\n props.direction === 'vertical' ? lastItemIndex : 0,\n props.direction === 'horizontal' ? lastItemIndex : 0,\n { behavior, align: 'end' },\n );\n }\n break;\n }\n case 'ArrowUp':\n event.preventDefault();\n stopProgrammaticScroll();\n if (isVertical) {\n navigate(true, false);\n }\n break;\n case 'ArrowDown':\n event.preventDefault();\n stopProgrammaticScroll();\n if (isVertical) {\n navigate(true, true);\n }\n break;\n case 'ArrowLeft':\n event.preventDefault();\n stopProgrammaticScroll();\n if (isHorizontal) {\n navigate(false, false);\n }\n break;\n case 'ArrowRight':\n event.preventDefault();\n stopProgrammaticScroll();\n if (isHorizontal) {\n navigate(false, true);\n }\n break;\n case 'PageUp':\n event.preventDefault();\n stopProgrammaticScroll();\n if (props.direction === 'horizontal') {\n scrollToIndex(null, getPageTarget(false, false), { align: snapMode || 'end' });\n } else {\n scrollToIndex(getPageTarget(true, false), null, { align: snapMode || 'end' });\n }\n break;\n case 'PageDown':\n event.preventDefault();\n stopProgrammaticScroll();\n if (props.direction === 'horizontal') {\n scrollToIndex(null, getPageTarget(false, true), { align: snapMode || 'start' });\n } else {\n scrollToIndex(getPageTarget(true, true), null, { align: snapMode || 'start' });\n }\n break;\n }\n}\n\nonUnmounted(() => {\n hostResizeObserver?.disconnect();\n itemResizeObserver?.disconnect();\n extraResizeObserver?.disconnect();\n});\n\nconst containerStyle = computed(() => {\n const base: Record<string, string | number | undefined> = {\n ...(props.direction !== 'vertical' ? { whiteSpace: 'nowrap' as const } : {}),\n };\n\n if (showVirtualScrollbars.value || !isWindowContainer.value) {\n base.overflow = 'auto';\n }\n\n if (useVirtualScrolling.value) {\n base.touchAction = 'none';\n }\n\n if (isWindowContainer.value) {\n return base;\n }\n\n if (props.containerTag === 'table') {\n return {\n ...base,\n display: 'block',\n minInlineSize: props.direction === 'vertical' ? '100%' : 'auto',\n };\n }\n\n return base;\n});\n\n/**\n * Internal helper to generate consistent ScrollbarSlotProps.\n *\n * @param axis - The scroll axis.\n * @param totalSize - Total scrollable size (DU).\n * @param position - Current scroll position (DU).\n * @param viewportSize - Current viewport size (DU).\n * @param scrollToOffsetCallback - Callback to perform scroll.\n * @param scrollbar - Scrollbar state from useVirtualScrollbar.\n * @returns Props for the scrollbar slot or null if content fits.\n */\nfunction getScrollbarSlotProps(\n axis: 'vertical' | 'horizontal',\n totalSize: number,\n position: number,\n viewportSize: number,\n scrollToOffsetCallback: (offset: number) => void,\n scrollbar: ReturnType<typeof useVirtualScrollbar>,\n): ScrollbarSlotProps | null {\n if (totalSize <= viewportSize) {\n return null;\n }\n\n return {\n axis,\n positionPercent: scrollbar.positionPercent.value,\n viewportPercent: scrollbar.viewportPercent.value,\n thumbSizePercent: scrollbar.thumbSizePercent.value,\n thumbPositionPercent: scrollbar.thumbPositionPercent.value,\n trackProps: scrollbar.trackProps.value,\n thumbProps: scrollbar.thumbProps.value,\n scrollbarProps: {\n axis,\n totalSize,\n position,\n viewportSize,\n scrollToOffset: scrollToOffsetCallback,\n containerId: containerId.value,\n isRtl: isRtl.value,\n ariaLabel: `${ axis === 'vertical' ? 'Vertical' : 'Horizontal' } scroll`,\n },\n isDragging: scrollbar.isDragging.value,\n };\n}\n\nconst verticalScrollbarProps = computed(() => {\n if (props.direction === 'horizontal') {\n return null;\n }\n const { displayViewportSize, displayScrollOffset } = scrollDetails.value;\n return getScrollbarSlotProps(\n 'vertical',\n renderedHeight.value,\n displayScrollOffset.y,\n displayViewportSize.height,\n handleVerticalScrollbarScrollToOffset,\n verticalScrollbar,\n );\n});\n\nconst horizontalScrollbarProps = computed(() => {\n if (props.direction === 'vertical') {\n return null;\n }\n const { displayViewportSize, displayScrollOffset } = scrollDetails.value;\n return getScrollbarSlotProps(\n 'horizontal',\n renderedWidth.value,\n displayScrollOffset.x,\n displayViewportSize.width,\n handleHorizontalScrollbarScrollToOffset,\n horizontalScrollbar,\n );\n});\n\nconst wrapperStyle = computed(() => {\n const isHorizontal = props.direction === 'horizontal';\n const isVertical = props.direction === 'vertical';\n const isBoth = props.direction === 'both';\n\n const style: Record<string, string | number | undefined> = {\n inlineSize: isVertical ? '100%' : `${ renderedVirtualWidth.value }px`,\n blockSize: isHorizontal ? '100%' : `${ renderedVirtualHeight.value }px`,\n };\n\n if (!isHydrated.value) {\n style.display = 'flex';\n style.flexDirection = isHorizontal ? 'row' : 'column';\n if ((isHorizontal || isBoth) && props.columnGap) {\n style.columnGap = `${ props.columnGap }px`;\n }\n if ((isVertical || isBoth) && props.gap) {\n style.rowGap = `${ props.gap }px`;\n }\n }\n\n return style;\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' : `${ renderedVirtualWidth.value }px`,\n blockSize: props.direction === 'horizontal' ? '1px' : `${ renderedVirtualHeight.value }px`,\n}));\n\n/**\n * Calculates the final style object for an item, including position and dimensions.\n *\n * @param item - The rendered item state.\n * @returns CSS style object.\n */\nfunction getItemStyle(item: RenderedItem<T>) {\n const style = calculateItemStyle({\n containerTag: props.containerTag || 'div',\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 isRtl: isRtl.value,\n });\n\n if (!isHydrated.value && props.direction === 'both') {\n style.display = 'flex';\n if (props.columnGap) {\n style.columnGap = `${ props.columnGap }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\nconst effectiveRole = computed(() => {\n if (props.role) {\n return props.role;\n }\n return isTable.value ? null : (props.direction === 'both' ? 'grid' : 'list');\n});\n\nconst isGrid = computed(() => effectiveRole.value === 'grid' || isTable.value);\n\nconst containerRole = computed(() => isTable.value\n ? null\n : ((props.ariaLabel || props.ariaLabelledby) ? 'region' : 'none'));\nconst wrapperRole = computed(() => isTable.value ? null : effectiveRole.value);\nconst internalItemRole = computed(() => {\n if (isGrid.value) {\n return 'row';\n }\n\n const role = effectiveRole.value;\n if (role === 'tree') {\n return 'treeitem';\n }\n if (role === 'listbox') {\n return 'option';\n }\n if (role === 'menu') {\n return 'menuitem';\n }\n return 'listitem';\n});\nconst itemRole = computed(() => props.itemRole != null ? props.itemRole : internalItemRole.value);\nconst cellRole = computed(() => {\n if (props.role === 'grid' || (!props.role && props.direction === 'both')) {\n return 'gridcell';\n }\n return isTable.value ? 'cell' : null;\n});\n\nconst shouldBindItemAria = computed(() => {\n const role = itemRole.value;\n return role == null || (role !== 'none' && role !== 'presentation');\n});\n\nconst rootAriaProps = computed(() => ({\n 'aria-label': props.ariaLabel,\n 'aria-labelledby': props.ariaLabelledby,\n 'aria-busy': props.loading ? 'true' : undefined,\n}));\n\nconst wrapperAriaProps = computed(() => {\n const aria: Record<string, string | number | undefined> = {};\n\n const role = effectiveRole.value;\n const supportsOrientation = role && [ 'grid', 'tree', 'listbox', 'menu', 'tablist' ].includes(role);\n\n if (supportsOrientation) {\n aria[ 'aria-orientation' ] = props.direction === 'both' ? undefined : props.direction;\n }\n\n if (isGrid.value) {\n aria[ 'aria-rowcount' ] = props.items.length;\n if (props.columnCount > 0) {\n aria[ 'aria-colcount' ] = props.columnCount;\n }\n }\n\n return aria;\n});\n\nfunction getItemAriaProps(index: number) {\n const aria: Record<string, string | number | undefined> = {};\n\n if (isGrid.value) {\n aria[ 'aria-rowindex' ] = index + 1;\n } else {\n aria[ 'aria-setsize' ] = props.items.length;\n aria[ 'aria-posinset' ] = index + 1;\n }\n\n const role = itemRole.value;\n if (role !== null) {\n aria.role = (role === 'none' || role === 'presentation')\n ? internalItemRole.value\n : role;\n }\n\n return aria;\n}\n\nfunction getCellAriaProps(colIndex: number) {\n const role = cellRole.value;\n if (!role) {\n return {};\n }\n\n const aria: Record<string, string | number | undefined> = {\n role,\n };\n\n if (isGrid.value) {\n aria[ 'aria-colindex' ] = colIndex + 1;\n }\n\n return aria;\n}\n\ndefineExpose({\n ...toRefs(props),\n\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 * Helper to get the height of a specific row.\n * @param index - The row index.\n * @see useVirtualScroll\n */\n getRowHeight,\n\n /**\n * Helper to get ARIA attributes for a cell.\n * @param colIndex - The column index.\n */\n getCellAriaProps,\n\n /**\n * Helper to get ARIA attributes for an item.\n * @param index - The item index.\n */\n getItemAriaProps,\n\n /**\n * Helper to get the virtual offset of a specific row.\n * @param index - The row index.\n * @see useVirtualScroll\n */\n getRowOffset,\n\n /**\n * Helper to get the virtual offset of a specific column.\n * @param index - The column index.\n * @see useVirtualScroll\n */\n getColumnOffset,\n\n /**\n * Helper to get the virtual offset of a specific item.\n * @param index - The item index.\n * @see useVirtualScroll\n */\n getItemOffset,\n\n /**\n * Helper to get the size of a specific item along the scroll axis.\n * @param index - The item index.\n * @see useVirtualScroll\n */\n getItemSize,\n\n /**\n * Helper to get the row (or item) index at a specific vertical (or horizontal in horizontal mode) virtual offset (VU).\n * @param offset - The virtual pixel offset.\n * @see useVirtualScroll\n */\n getRowIndexAt,\n\n /**\n * Helper to get the column index at a specific horizontal virtual offset (VU).\n * @param offset - The virtual pixel offset.\n * @see useVirtualScroll\n */\n getColIndexAt,\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. Optional.\n * @param colIndex - The column index to scroll to. Pass null to only scroll vertically. Optional.\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 stopProgrammaticScroll();\n stopInertia();\n },\n\n /**\n * Detects the current direction (LTR/RTL) of the scroll container.\n */\n updateDirection,\n\n /**\n * Whether the scroll container is in Right-to-Left (RTL) mode.\n */\n isRtl,\n\n /**\n * Whether the component has finished its first client-side mount and hydration.\n */\n isHydrated,\n\n /**\n * Coordinate scaling factor for X axis.\n */\n scaleX,\n\n /**\n * Coordinate scaling factor for Y axis.\n */\n scaleY,\n\n /**\n * Physical width of the content in the DOM (clamped to browser limits).\n */\n renderedWidth,\n\n /**\n * Physical height of the content in the DOM (clamped to browser limits).\n */\n renderedHeight,\n\n /**\n * Absolute offset of the component within its container.\n */\n componentOffset,\n\n /**\n * Properties for the vertical scrollbar.\n * Useful when building custom scrollbar interfaces.\n */\n scrollbarPropsVertical: verticalScrollbarProps,\n\n /**\n * Properties for the horizontal scrollbar.\n * Useful when building custom scrollbar interfaces.\n */\n scrollbarPropsHorizontal: horizontalScrollbarProps,\n});\n</script>\n\n<template>\n <component\n :is=\"containerTag\"\n :id=\"containerId\"\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 'virtual-scroll--hide-scrollbar': showVirtualScrollbars,\n },\n ]\"\n :style=\"containerStyle\"\n tabindex=\"0\"\n :role=\"isTable ? undefined : containerRole\"\n v-bind=\"isTable ? { ...rootAriaProps, ...wrapperAriaProps } : rootAriaProps\"\n @keydown=\"handleKeyDown\"\n @pointerdown=\"handlePointerDown\"\n @pointermove=\"handlePointerMove\"\n @pointerup=\"handlePointerUp\"\n @pointercancel=\"handlePointerUp\"\n >\n <div\n v-if=\"showVirtualScrollbars\"\n class=\"virtual-scroll-scrollbar-container\"\n aria-hidden=\"true\"\n >\n <div\n class=\"virtual-scroll-scrollbar-viewport\"\n :style=\"{\n 'inlineSize': `${ scrollDetails.displayViewportSize.width }px`,\n 'blockSize': `${ scrollDetails.displayViewportSize.height }px`,\n '--vsi-scrollbar-has-cross-gap': direction === 'both' ? 1 : 0,\n }\"\n >\n <slot v-if=\"slots.scrollbar && verticalScrollbarProps\" name=\"scrollbar\" v-bind=\"verticalScrollbarProps\" />\n <VirtualScrollbar v-else-if=\"verticalScrollbarProps\" v-bind=\"verticalScrollbarProps.scrollbarProps\" />\n\n <slot v-if=\"slots.scrollbar && horizontalScrollbarProps\" name=\"scrollbar\" v-bind=\"horizontalScrollbarProps\" />\n <VirtualScrollbar v-else-if=\"horizontalScrollbarProps\" v-bind=\"horizontalScrollbarProps.scrollbarProps\" />\n </div>\n </div>\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 :role=\"isTable ? undefined : 'none'\"\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 :role=\"isTable ? undefined : wrapperRole\"\n v-bind=\"isTable ? {} : wrapperAriaProps\"\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 v-bind=\"shouldBindItemAria ? getItemAriaProps(renderedItem.index) : { role: 'none' }\"\n >\n <slot\n name=\"item\"\n :item=\"renderedItem.item\"\n :index=\"renderedItem.index\"\n :get-item-aria-props=\"getItemAriaProps\"\n :column-range=\"slotColumnRange\"\n :get-column-width=\"getColumnWidth\"\n :get-cell-aria-props=\"getCellAriaProps\"\n :gap=\"props.gap\"\n :column-gap=\"props.columnGap\"\n :is-sticky=\"renderedItem.isSticky\"\n :is-sticky-active=\"renderedItem.isStickyActive\"\n :is-sticky-active-x=\"renderedItem.isStickyActiveX\"\n :is-sticky-active-y=\"renderedItem.isStickyActiveY\"\n :offset=\"renderedItem.offset\"\n />\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 aria-live=\"polite\"\n aria-atomic=\"true\"\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 :role=\"isTable ? undefined : 'none'\"\n >\n <slot name=\"footer\" />\n </component>\n </component>\n</template>\n\n<style scoped>\n@layer components {\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 &.virtual-scroll--hide-scrollbar {\n scrollbar-width: none;\n -ms-overflow-style: none;\n\n &::-webkit-scrollbar {\n display: none;\n }\n }\n\n &.virtual-scroll--horizontal,\n &.virtual-scroll--both {\n white-space: nowrap;\n }\n }\n\n .virtual-scroll-scrollbar-container {\n position: sticky;\n inset-block-start: 0;\n inset-inline-start: 0;\n inline-size: 100%;\n block-size: 0;\n z-index: 30;\n pointer-events: none;\n overflow: visible;\n }\n\n .virtual-scroll-scrollbar-viewport {\n position: absolute;\n inset-block-start: 0;\n inset-inline-start: 0;\n pointer-events: none;\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}\n</style>\n"],"mappings":"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"}