@pdanpdan/virtual-scroll 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +278 -140
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +866 -112
- package/dist/index.js +1 -844
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1125 -0
- package/dist/index.mjs.map +1 -0
- package/dist/virtual-scroll.css +2 -0
- package/package.json +8 -5
- package/src/components/VirtualScroll.test.ts +527 -688
- package/src/components/VirtualScroll.vue +402 -209
- package/src/composables/useVirtualScroll.test.ts +241 -1447
- package/src/composables/useVirtualScroll.ts +544 -531
- package/src/index.ts +2 -0
- package/src/types.ts +535 -0
- package/src/utils/fenwick-tree.ts +38 -18
- package/src/utils/scroll.test.ts +148 -0
- package/src/utils/scroll.ts +40 -10
- package/src/utils/virtual-scroll-logic.test.ts +2517 -0
- package/src/utils/virtual-scroll-logic.ts +605 -0
- package/dist/index.css +0 -2
package/README.md
CHANGED
|
@@ -57,6 +57,45 @@ import VirtualScroll from '@pdanpdan/virtual-scroll/VirtualScroll.vue';
|
|
|
57
57
|
- Enables deep integration with your project's CSS-in-JS or specialized styling solutions.
|
|
58
58
|
- Easier debugging of the component source in some IDEs.
|
|
59
59
|
|
|
60
|
+
### 3. CDN Usage
|
|
61
|
+
|
|
62
|
+
You can use the library directly from a CDN like unpkg or jsdelivr.
|
|
63
|
+
|
|
64
|
+
```html
|
|
65
|
+
<!-- Import Vue 3 first -->
|
|
66
|
+
<script src="https://unpkg.com/vue@3"></script>
|
|
67
|
+
|
|
68
|
+
<!-- Import VirtualScroll CSS -->
|
|
69
|
+
<link rel="stylesheet" href="https://unpkg.com/@pdanpdan/virtual-scroll/dist/virtual-scroll.css">
|
|
70
|
+
|
|
71
|
+
<!-- Import VirtualScroll JavaScript -->
|
|
72
|
+
<script src="https://unpkg.com/@pdanpdan/virtual-scroll"></script>
|
|
73
|
+
|
|
74
|
+
<div id="app">
|
|
75
|
+
<div style="height: 400px; overflow: auto;">
|
|
76
|
+
<virtual-scroll :items="items" :item-size="50">
|
|
77
|
+
<template #item="{ item, index }">
|
|
78
|
+
<div style="height: 50px;">{{ index }}: {{ item.label }}</div>
|
|
79
|
+
</template>
|
|
80
|
+
</virtual-scroll>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<script>
|
|
85
|
+
const { createApp, ref } = Vue;
|
|
86
|
+
const { VirtualScroll } = window.VirtualScroll;
|
|
87
|
+
|
|
88
|
+
createApp({
|
|
89
|
+
setup() {
|
|
90
|
+
const items = ref(Array.from({ length: 1000 }, (_, i) => ({ label: `Item ${i}` })));
|
|
91
|
+
return { items };
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
.component('VirtualScroll', VirtualScroll)
|
|
95
|
+
.mount('#app');
|
|
96
|
+
</script>
|
|
97
|
+
```
|
|
98
|
+
|
|
60
99
|
## Basic Usage
|
|
61
100
|
|
|
62
101
|
```vue
|
|
@@ -91,146 +130,120 @@ const items = Array.from({ length: 10000 }, (_, i) => ({ id: i, label: `Item ${
|
|
|
91
130
|
</style>
|
|
92
131
|
```
|
|
93
132
|
|
|
94
|
-
##
|
|
133
|
+
## Sizing Guide
|
|
134
|
+
|
|
135
|
+
The component offers flexible ways to define item and column sizes. Understanding how these options interact is key to achieving smooth scrolling and correct layout.
|
|
136
|
+
|
|
137
|
+
### Sizing Options
|
|
138
|
+
|
|
139
|
+
| Option Type | `itemSize` / `columnWidth` | Performance | Description |
|
|
140
|
+
|-------------|----------------------------|-------------|-------------|
|
|
141
|
+
| **Fixed** | `number` (e.g., `50`) | **Best** | Every item has the exact same size. Calculations are *O(1)*. |
|
|
142
|
+
| **Array** | `number[]` (cols only) | **Great** | Each column has a fixed size from the array (cycles if shorter). |
|
|
143
|
+
| **Function** | `(item, index) => number` | **Good** | Size is known but varies per item. No `ResizeObserver` overhead unless it differs from measured size. |
|
|
144
|
+
| **Dynamic** | `0`, `null`, or `undefined` | **Fair** | Sizes are measured automatically via `ResizeObserver` after rendering. |
|
|
145
|
+
|
|
146
|
+
### How Sizing Works
|
|
147
|
+
|
|
148
|
+
1. **Initial Estimate**:
|
|
149
|
+
- If a **fixed size** or **function** is provided, it's used as the initial size.
|
|
150
|
+
- If **dynamic** is used, the component uses `defaultItemSize` (default: `40`) or `defaultColumnWidth` (default: `100`) as the initial estimate.
|
|
151
|
+
2. **Measurement**:
|
|
152
|
+
- When an item is rendered, its actual size is measured using `ResizeObserver`.
|
|
153
|
+
- If the measured size differs from the estimate (by more than 0.5px), the internal Fenwick Tree is updated.
|
|
154
|
+
3. **Refinement**:
|
|
155
|
+
- All subsequent item positions are automatically adjusted based on the new measurement.
|
|
156
|
+
- The total scrollable area (`totalWidth`/`totalHeight`) is updated to reflect the real content size.
|
|
157
|
+
|
|
158
|
+
### Fallback Logic
|
|
159
|
+
|
|
160
|
+
- **Unset Props**: If `itemSize` or `columnWidth` are not provided, they default to `40` and `100` respectively (fixed).
|
|
161
|
+
- **Dynamic Fallback**: When using dynamic sizing, `defaultItemSize` and `defaultColumnWidth` act as the source of truth for items that haven't been rendered yet.
|
|
162
|
+
- **Function/Array Fallback**: If a function or array returns an invalid value, it falls back to the respective `default...` prop.
|
|
163
|
+
|
|
164
|
+
### Recommendations for Smooth Scrolling
|
|
95
165
|
|
|
96
|
-
|
|
166
|
+
1. **Accurate Estimates**: When using dynamic sizing, set `defaultItemSize` as close as possible to the *average* height of your items. This minimizes scrollbar "jumping".
|
|
167
|
+
2. **Avoid 0 sizes**: Ensure your items have a minimum height/width (e.g., via CSS `min-height`). Items with 0 size might not be detected correctly by the virtualizer.
|
|
168
|
+
3. **Box Sizing**: Use `box-sizing: border-box` on your items to ensure padding and borders are included in the measured size.
|
|
169
|
+
4. **Manual Refresh**: If you change external state that affects a sizing function's output without changing the function reference itself, call `virtualScrollRef.refresh()` to force a full re-calculation.
|
|
170
|
+
|
|
171
|
+
## Component Reference
|
|
172
|
+
|
|
173
|
+
The `VirtualScroll` component provides a declarative interface for virtualizing lists and grids. It automatically manages the rendering lifecycle of items, measures dynamic sizes, and handles complex scroll behaviors like stickiness and restoration.
|
|
174
|
+
|
|
175
|
+
### Props
|
|
176
|
+
|
|
177
|
+
#### Core Configuration
|
|
97
178
|
| Prop | Type | Default | Description |
|
|
98
179
|
|------|------|---------|-------------|
|
|
99
180
|
| `items` | `T[]` | Required | Array of items to be virtualized. |
|
|
100
|
-
| `itemSize` | `number \| ((item: T, index: number) => number) \| null` | `
|
|
101
|
-
| `direction` | `'vertical' \| 'horizontal' \| 'both'` | `'vertical'` |
|
|
102
|
-
| `gap` | `number` | `0` |
|
|
181
|
+
| `itemSize` | `number \| ((item: T, index: number) => number) \| null` | `40` | Fixed size or function. Pass `0`/`null` for dynamic. |
|
|
182
|
+
| `direction` | `'vertical' \| 'horizontal' \| 'both'` | `'vertical'` | Scroll direction. |
|
|
183
|
+
| `gap` | `number` | `0` | Spacing between items. |
|
|
103
184
|
|
|
104
|
-
|
|
185
|
+
#### Grid Configuration (only for direction="both")
|
|
105
186
|
| Prop | Type | Default | Description |
|
|
106
187
|
|------|------|---------|-------------|
|
|
107
|
-
| `columnCount` | `number` | `0` | Number of columns for
|
|
108
|
-
| `columnWidth` | `
|
|
109
|
-
| `columnGap` | `number` | `0` |
|
|
188
|
+
| `columnCount` | `number` | `0` | Number of columns for grid mode. |
|
|
189
|
+
| `columnWidth` | `num \| num[] \| fn \| null` | `100` | Width for columns in grid mode. |
|
|
190
|
+
| `columnGap` | `number` | `0` | Spacing between columns. |
|
|
110
191
|
|
|
111
|
-
|
|
112
|
-
| Prop | Type | Default | Description |
|
|
113
|
-
|------|------|---------|-------------|
|
|
114
|
-
| `stickyIndices` | `number[]` | `[]` | Indices of items that should stick to the top/start. Supports iOS-style pushing effect. |
|
|
115
|
-
| `stickyHeader` | `boolean` | `false` | Whether the header slot content is sticky. If true, header size is measured and added to `scrollPaddingStart`. |
|
|
116
|
-
| `stickyFooter` | `boolean` | `false` | Whether the footer slot content is sticky. If true, footer size is measured and added to `scrollPaddingEnd`. |
|
|
117
|
-
| `loading` | `boolean` | `false` | Whether items are currently being loaded. Prevents multiple `load` events and displays the `#loading` slot. |
|
|
118
|
-
| `loadDistance` | `number` | `200` | Distance from the end of the scrollable area to trigger `load` event. |
|
|
119
|
-
| `restoreScrollOnPrepend` | `boolean` | `false` | Whether to automatically restore scroll position when items are prepended to the list. |
|
|
120
|
-
|
|
121
|
-
### Advanced Configuration
|
|
192
|
+
#### Features & Behavior
|
|
122
193
|
| Prop | Type | Default | Description |
|
|
123
194
|
|------|------|---------|-------------|
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
131
|
-
| `
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
| `initialScrollAlign` | `ScrollAlignment \| ScrollAlignmentOptions` | `'start'` | Alignment for the initial scroll index. |
|
|
135
|
-
|
|
136
|
-
### Development
|
|
195
|
+
| `stickyIndices` | `number[]` | `[]` | Indices of items that should remain sticky. |
|
|
196
|
+
| `stickyHeader` / `stickyFooter` | `boolean` | `false` | If true, measures and adds slot size to padding. |
|
|
197
|
+
| `ssrRange` | `object` | `undefined` | Items to pre-render on server. See [SSR Support](#ssr-support). |
|
|
198
|
+
| `loading` | `boolean` | `false` | Shows loading state and prevents duplicate events. |
|
|
199
|
+
| `loadDistance` | `number` | `200` | Distance from end to trigger `load` event. |
|
|
200
|
+
| `restoreScrollOnPrepend` | `boolean` | `false` | Maintain position when items added to top. |
|
|
201
|
+
| `initialScrollIndex` | `number` | `undefined` | Index to jump to on mount. |
|
|
202
|
+
| `initialScrollAlign` | `ScrollAlignment \| object` | `'start'` | Alignment for initial jump. |
|
|
203
|
+
|
|
204
|
+
#### Advanced & Performance
|
|
137
205
|
| Prop | Type | Default | Description |
|
|
138
206
|
|------|------|---------|-------------|
|
|
139
|
-
| `
|
|
140
|
-
| `
|
|
141
|
-
| `
|
|
207
|
+
| `container` | `HTMLElement \| Window` | `host element` | The scrollable container element. |
|
|
208
|
+
| `scrollPaddingStart` / `End` | `num \| {x, y}` | `0` | Padding for scroll calculations. |
|
|
209
|
+
| `containerTag` / `wrapperTag` / `itemTag` | `string` | `'div'` | HTML tags for component parts. |
|
|
210
|
+
| `bufferBefore` / `bufferAfter` | `number` | `5` | Items to render outside the viewport. |
|
|
211
|
+
| `defaultItemSize` | `number` | `40` | Initial estimate for dynamic items. |
|
|
212
|
+
| `defaultColumnWidth` | `number` | `100` | Initial estimate for dynamic columns. |
|
|
213
|
+
| `debug` | `boolean` | `false` | Enables debug visualization. |
|
|
142
214
|
|
|
143
215
|
## Slots
|
|
144
216
|
|
|
145
|
-
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
-
|
|
153
|
-
-
|
|
154
|
-
-
|
|
217
|
+
- `item`: Scoped slot for individual items.
|
|
218
|
+
- `item: T`: The source data item.
|
|
219
|
+
- `index: number`: The original 0-based index of the item.
|
|
220
|
+
- `columnRange: ColumnRange`: Visible column range and paddings.
|
|
221
|
+
- `getColumnWidth: (index: number) => number`: Helper to get width of any column.
|
|
222
|
+
- `isSticky: boolean`: True if the item is configured to be sticky.
|
|
223
|
+
- `isStickyActive: boolean`: True if the item is currently stuck at the threshold.
|
|
224
|
+
- `header`: Content rendered at the top of the scrollable area.
|
|
225
|
+
- `footer`: Content rendered at the bottom of the scrollable area.
|
|
226
|
+
- `loading`: Content shown at the end when `loading` prop is true.
|
|
155
227
|
|
|
156
228
|
## Events
|
|
157
229
|
|
|
158
|
-
-
|
|
159
|
-
|
|
160
|
-
-
|
|
161
|
-
- `direction`: `'vertical'` or `'horizontal'`.
|
|
162
|
-
- `visibleRangeChange`: Emitted when the rendered items range or column range changes.
|
|
163
|
-
- `range`: `{ start: number; end: number; colStart: number; colEnd: number; }`
|
|
230
|
+
- `scroll`: Emitted on every scroll change. Payload: `ScrollDetails<T>`.
|
|
231
|
+
- `load`: Triggered near the end of content. Payload: `'vertical' | 'horizontal'`.
|
|
232
|
+
- `visibleRangeChange`: Emitted when rendered indices change. Payload: `{ start, end, colStart, colEnd }`.
|
|
164
233
|
|
|
165
234
|
## Keyboard Navigation
|
|
166
235
|
|
|
167
|
-
When the container is focused
|
|
168
|
-
-
|
|
169
|
-
-
|
|
170
|
-
-
|
|
171
|
-
-
|
|
172
|
-
|
|
173
|
-
## Methods (Exposed)
|
|
174
|
-
|
|
175
|
-
- `scrollToIndex(rowIndex: number | null, colIndex: number | null, options?: ScrollAlignment | ScrollAlignmentOptions | ScrollToIndexOptions)`
|
|
176
|
-
- `rowIndex`: Row index to scroll to.
|
|
177
|
-
- `colIndex`: Column index to scroll to (for horizontal or grid).
|
|
178
|
-
- `options`:
|
|
179
|
-
- `align`: `'start' | 'center' | 'end' | 'auto'` or `{ x, y }` alignments.
|
|
180
|
-
- `behavior`: `'auto' | 'smooth'`.
|
|
181
|
-
- `scrollToOffset(x: number | null, y: number | null, options?: { behavior?: 'auto' | 'smooth' })`
|
|
182
|
-
- `x`: Pixel offset on X axis.
|
|
183
|
-
- `y`: Pixel offset on Y axis.
|
|
184
|
-
- `refresh()`: Resets all dynamic measurements and re-initializes sizes from current items and props.
|
|
185
|
-
|
|
186
|
-
## Types
|
|
187
|
-
|
|
188
|
-
### ScrollDetails<T>
|
|
189
|
-
```typescript
|
|
190
|
-
/* eslint-disable unused-imports/no-unused-vars */
|
|
191
|
-
interface ScrollDetails<T = unknown> {
|
|
192
|
-
items: Array<{
|
|
193
|
-
item: T;
|
|
194
|
-
index: number;
|
|
195
|
-
offset: { x: number; y: number; };
|
|
196
|
-
size: { width: number; height: number; };
|
|
197
|
-
originalX: number;
|
|
198
|
-
originalY: number;
|
|
199
|
-
isSticky?: boolean;
|
|
200
|
-
isStickyActive?: boolean;
|
|
201
|
-
stickyOffset: { x: number; y: number; };
|
|
202
|
-
}>;
|
|
203
|
-
currentIndex: number;
|
|
204
|
-
currentColIndex: number;
|
|
205
|
-
scrollOffset: { x: number; y: number; };
|
|
206
|
-
viewportSize: { width: number; height: number; };
|
|
207
|
-
totalSize: { width: number; height: number; };
|
|
208
|
-
isScrolling: boolean;
|
|
209
|
-
isProgrammaticScroll: boolean;
|
|
210
|
-
range: { start: number; end: number; };
|
|
211
|
-
columnRange: { start: number; end: number; padStart: number; padEnd: number; };
|
|
212
|
-
}
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
### RenderedItem<T>
|
|
216
|
-
```typescript
|
|
217
|
-
/* eslint-disable unused-imports/no-unused-vars */
|
|
218
|
-
interface RenderedItem<T = unknown> {
|
|
219
|
-
item: T;
|
|
220
|
-
index: number;
|
|
221
|
-
offset: { x: number; y: number; };
|
|
222
|
-
size: { width: number; height: number; };
|
|
223
|
-
originalX: number;
|
|
224
|
-
originalY: number;
|
|
225
|
-
isSticky?: boolean;
|
|
226
|
-
isStickyActive?: boolean;
|
|
227
|
-
stickyOffset: { x: number; y: number; };
|
|
228
|
-
}
|
|
229
|
-
```
|
|
236
|
+
When the container is focused (it has `tabindex="0"`), it supports:
|
|
237
|
+
- `Home`: Scroll to the very beginning (Index 0,0).
|
|
238
|
+
- `End`: Scroll to the very last row and column.
|
|
239
|
+
- `ArrowUp` / `ArrowDown`: Scroll up/down by 40px (or `DEFAULT_ITEM_SIZE`).
|
|
240
|
+
- `ArrowLeft` / `ArrowRight`: Scroll left/right by 40px (or `DEFAULT_ITEM_SIZE`).
|
|
241
|
+
- `PageUp` / `PageDown`: Scroll by one full viewport height/width.
|
|
230
242
|
|
|
231
|
-
|
|
243
|
+
### CSS Classes
|
|
232
244
|
|
|
233
245
|
- `.virtual-scroll-container`: Root container.
|
|
246
|
+
- `.virtual-scroll--vertical`, `.virtual-scroll--horizontal`, `.virtual-scroll--both`: Direction modifier.
|
|
234
247
|
- `.virtual-scroll-wrapper`: Items wrapper.
|
|
235
248
|
- `.virtual-scroll-item`: Individual item.
|
|
236
249
|
- `.virtual-scroll-header` / `.virtual-scroll-footer`: Header/Footer slots.
|
|
@@ -239,53 +252,178 @@ interface RenderedItem<T = unknown> {
|
|
|
239
252
|
- `.virtual-scroll--hydrated`: Applied when client-side mount is complete.
|
|
240
253
|
- `.virtual-scroll--window`: Applied when using window/body scroll.
|
|
241
254
|
- `.virtual-scroll--table`: Applied when `containerTag="table"` is used.
|
|
255
|
+
- `.virtual-scroll--debug`: Applied when debug mode is enabled.
|
|
242
256
|
|
|
243
|
-
|
|
257
|
+
### Exposed Members
|
|
244
258
|
|
|
245
|
-
|
|
259
|
+
Access these via a template `ref` on the `VirtualScroll` component:
|
|
246
260
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
When `ssrRange` is provided:
|
|
257
|
-
1. **Server-Side**: Only the specified range of items is rendered. Items are rendered in-flow (relative positioning) with their offsets adjusted so the specified range appears at the top-left of the container.
|
|
258
|
-
2. **Client-Side Hydration**:
|
|
259
|
-
- The component initially renders the SSR content to match the server-generated HTML.
|
|
260
|
-
- On mount, it expands the container size and automatically scrolls to exactly match the pre-rendered range using `align: 'start'`.
|
|
261
|
-
- It then seamlessly transitions to virtual mode (absolute positioning) while maintaining the scroll position.
|
|
261
|
+
- `scrollDetails`: Full reactive state of the virtual scroll system.
|
|
262
|
+
- `columnRange`: Information about the current visible range of columns.
|
|
263
|
+
- `getColumnWidth(index: number)`: Helper to get the calculated width of a specific column.
|
|
264
|
+
- `scrollToIndex(rowIndex, colIndex, options)`: Programmatic scroll to a specific row and/or column (default `align: 'auto'`, `behavior: 'auto'`).
|
|
265
|
+
- `scrollToOffset(x, y, options)`: Programmatic scroll to a specific pixel position (default `behavior: 'auto'`).
|
|
266
|
+
- `refresh()`: Resets all dynamic measurements and re-initializes state.
|
|
267
|
+
- `stopProgrammaticScroll()`: Immediately halts any active smooth scroll animation.
|
|
262
268
|
|
|
263
269
|
## Composable API
|
|
264
270
|
|
|
265
271
|
For advanced use cases, you can use the underlying logic via the `useVirtualScroll` composable.
|
|
266
272
|
|
|
273
|
+
### Example
|
|
274
|
+
|
|
267
275
|
```typescript
|
|
276
|
+
/* eslint-disable unused-imports/no-unused-vars */
|
|
268
277
|
import { useVirtualScroll } from '@pdanpdan/virtual-scroll';
|
|
269
278
|
import { computed, ref } from 'vue';
|
|
270
279
|
|
|
271
280
|
const items = ref([ { id: 1 }, { id: 2 } ]);
|
|
281
|
+
const containerRef = ref<HTMLElement | null>(null);
|
|
282
|
+
|
|
272
283
|
const props = computed(() => ({
|
|
273
284
|
items: items.value,
|
|
274
285
|
itemSize: 50,
|
|
286
|
+
container: containerRef.value,
|
|
275
287
|
direction: 'vertical' as const,
|
|
276
288
|
}));
|
|
277
289
|
|
|
278
|
-
const { renderedItems, scrollDetails } = useVirtualScroll(props);
|
|
279
|
-
console.log(renderedItems.value, scrollDetails.value);
|
|
290
|
+
const { renderedItems, scrollDetails, totalHeight } = useVirtualScroll(props);
|
|
280
291
|
```
|
|
281
292
|
|
|
282
|
-
|
|
293
|
+
### Return Value
|
|
294
|
+
|
|
295
|
+
Returns a set of reactive references and methods for managing the virtual scroll state:
|
|
296
|
+
|
|
297
|
+
| Member | Type | Description |
|
|
298
|
+
|--------|------|-------------|
|
|
299
|
+
| `renderedItems` | `Ref<RenderedItem<T>[]>` | List of items to render in the buffer. |
|
|
300
|
+
| `scrollDetails` | `Ref<ScrollDetails<T>>` | Full reactive state of the virtualizer. |
|
|
301
|
+
| `totalWidth` / `totalHeight` | `Ref<number>` | Calculated total content dimensions. |
|
|
302
|
+
| `columnRange` | `Ref<ColumnRange>` | Visible column indices and paddings. |
|
|
303
|
+
| `isHydrated` | `Ref<boolean>` | True after mount and hydration. |
|
|
304
|
+
| `scrollToIndex` | `Function` | Programmatic scroll to row/column index. |
|
|
305
|
+
| `scrollToOffset` | `Function` | Programmatic scroll to pixel offset. |
|
|
306
|
+
| `stopProgrammaticScroll` | `Function` | Cancel any active smooth scroll animation. |
|
|
307
|
+
| `updateItemSize` | `Function` | Register a new manual item measurement. |
|
|
308
|
+
| `updateItemSizes` | `Function` | Register multiple manual item measurements. |
|
|
309
|
+
| `updateHostOffset` | `Function` | Recalculate host element position. |
|
|
310
|
+
| `getColumnWidth` | `Function` | Helper to get a column's width. |
|
|
311
|
+
| `refresh` | `Function` | Resets all measurements and state. |
|
|
312
|
+
|
|
313
|
+
## API Reference
|
|
314
|
+
|
|
315
|
+
### Types
|
|
316
|
+
|
|
317
|
+
#### ScrollDetails<T>
|
|
318
|
+
| Property | Type | Description |
|
|
319
|
+
|----------|------|-------------|
|
|
320
|
+
| `items` | `RenderedItem<T>[]` | Rendered items in the buffer. |
|
|
321
|
+
| `currentIndex` | `number` | First visible row index. |
|
|
322
|
+
| `currentColIndex` | `number` | First visible column index. |
|
|
323
|
+
| `scrollOffset` | `{ x: number, y: number }` | Current pixel scroll position. |
|
|
324
|
+
| `viewportSize` | `{ width: number, height: number }` | Dimensions of the visible area. |
|
|
325
|
+
| `totalSize` | `{ width: number, height: number }` | Estimated total dimensions. |
|
|
326
|
+
| `isScrolling` | `boolean` | Active scrolling state. |
|
|
327
|
+
| `isProgrammaticScroll` | `boolean` | True if triggered by method. |
|
|
328
|
+
| `range` | `{ start: number, end: number }` | Visible row range. |
|
|
329
|
+
| `columnRange` | `ColumnRange` | Visible column range (grid). |
|
|
330
|
+
|
|
331
|
+
#### RenderedItem<T>
|
|
332
|
+
| Property | Type | Description |
|
|
333
|
+
|----------|------|-------------|
|
|
334
|
+
| `item` | `T` | The source data item. |
|
|
335
|
+
| `index` | `number` | Position in original array. |
|
|
336
|
+
| `offset` | `{ x: number, y: number }` | Position relative to wrapper. |
|
|
337
|
+
| `size` | `{ width, height }` | Current dimensions. |
|
|
338
|
+
| `originalX` / `originalY` | `number` | Offsets before sticky adjustments. |
|
|
339
|
+
| `isSticky` | `boolean` |configured as sticky. |
|
|
340
|
+
| `isStickyActive` | `boolean` | Currently stuck. |
|
|
341
|
+
| `stickyOffset` | `{ x, y }` | Translation for pushing effect. |
|
|
342
|
+
|
|
343
|
+
#### ColumnRange
|
|
344
|
+
| Property | Type | Description |
|
|
345
|
+
|----------|------|-------------|
|
|
346
|
+
| `start` | `number` | First rendered column index. |
|
|
347
|
+
| `end` | `number` | Last rendered column index (exclusive). |
|
|
348
|
+
| `padStart` | `number` | Pixel space before first column. |
|
|
349
|
+
| `padEnd` | `number` | Pixel space after last column. |
|
|
350
|
+
|
|
351
|
+
#### ScrollToIndexOptions
|
|
352
|
+
| Property | Type | Description |
|
|
353
|
+
|----------|------|-------------|
|
|
354
|
+
| `align` | `ScrollAlignment \| ScrollAlignmentOptions` | How to position the item (default: `'auto'`). |
|
|
355
|
+
| `behavior` | `'auto' \| 'smooth'` | Scroll animation behavior (default: `'auto'`). |
|
|
356
|
+
|
|
357
|
+
#### ScrollAlignmentOptions
|
|
358
|
+
| Property | Type | Description |
|
|
359
|
+
|----------|------|-------------|
|
|
360
|
+
| `x` | `ScrollAlignment` | Alignment on the horizontal axis. |
|
|
361
|
+
| `y` | `ScrollAlignment` | Alignment on the vertical axis. |
|
|
362
|
+
|
|
363
|
+
#### ScrollAlignment
|
|
364
|
+
Values: `'start' | 'center' | 'end' | 'auto'`
|
|
365
|
+
|
|
366
|
+
- `start`: Align to top/left edge.
|
|
367
|
+
- `center`: Align to viewport center.
|
|
368
|
+
- `end`: Align to bottom/right edge.
|
|
369
|
+
- `auto`: **Smart Alignment**. Only scrolls if item is not fully visible.
|
|
370
|
+
|
|
371
|
+
### Methods
|
|
372
|
+
|
|
373
|
+
#### `scrollToIndex(rowIndex, colIndex, options)`
|
|
374
|
+
Ensures a specific item is visible within the viewport. Corrects position if item sizes are dynamic.
|
|
375
|
+
|
|
376
|
+
#### `scrollToOffset(x, y, options)`
|
|
377
|
+
Scrolls the container to an absolute pixel position.
|
|
378
|
+
|
|
379
|
+
#### `stopProgrammaticScroll()`
|
|
380
|
+
Immediately halts any active smooth scroll animation.
|
|
381
|
+
|
|
382
|
+
#### `updateItemSize(index, width, height, element?)`
|
|
383
|
+
Manually registers a new measurement for a single item.
|
|
384
|
+
|
|
385
|
+
#### `updateItemSizes(updates)`
|
|
386
|
+
Batched version of `updateItemSize`.
|
|
387
|
+
|
|
388
|
+
#### `updateHostOffset()`
|
|
389
|
+
Forces a recalculation of the host element's position relative to the scroll container.
|
|
390
|
+
|
|
391
|
+
#### `getColumnWidth(index)`
|
|
392
|
+
Returns the currently calculated width for a specific column index.
|
|
393
|
+
|
|
394
|
+
#### `refresh()`
|
|
395
|
+
Invalidates all cached measurements and triggers a full re-initialization.
|
|
396
|
+
|
|
397
|
+
## Utility Functions
|
|
398
|
+
|
|
399
|
+
The library exports several utility functions and classes:
|
|
400
|
+
|
|
401
|
+
- `isElement(val: any): val is HTMLElement`: Checks if value is an `HTMLElement` (excludes `window`).
|
|
402
|
+
- `isScrollableElement(val: any): val is HTMLElement | Window`: Checks if value has scroll properties.
|
|
403
|
+
- `isScrollToIndexOptions(val: any): val is ScrollToIndexOptions`: Type guard for scroll options.
|
|
404
|
+
- `getPaddingX / getPaddingY`: Internal helpers for extraction of padding from props.
|
|
405
|
+
- `FenwickTree`: The underlying data structure for efficient size and offset management.
|
|
406
|
+
- `DEFAULT_ITEM_SIZE / DEFAULT_COLUMN_WIDTH / DEFAULT_BUFFER`: The default values used by the library.
|
|
283
407
|
|
|
284
|
-
|
|
408
|
+
## SSR Support
|
|
285
409
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
410
|
+
The component is designed to be SSR-friendly. You can pre-render a specific range of items on the server using the `ssrRange` prop.
|
|
411
|
+
|
|
412
|
+
```vue
|
|
413
|
+
<VirtualScroll
|
|
414
|
+
:items="items"
|
|
415
|
+
:ssr-range="{ start: 100, end: 120, colStart: 50, colEnd: 70 }"
|
|
416
|
+
>
|
|
417
|
+
<!-- ... -->
|
|
418
|
+
</VirtualScroll>
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
When `ssrRange` is provided:
|
|
422
|
+
1. **Server-Side**: Only the specified range of items is rendered. Items are rendered in-flow (relative positioning) with their offsets adjusted so the specified range appears at the top-left of the container.
|
|
423
|
+
2. **Client-Side Hydration**:
|
|
424
|
+
- The component initially renders the SSR content to match the server-generated HTML.
|
|
425
|
+
- On mount, it expands the container size and automatically scrolls to exactly match the pre-rendered range using `align: 'start'`.
|
|
426
|
+
- It then seamlessly transitions to virtual mode (absolute positioning) while maintaining the scroll position.
|
|
289
427
|
|
|
290
428
|
## License
|
|
291
429
|
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`vue`);var t=class{tree;values;constructor(e){this.tree=new Float64Array(e+1),this.values=new Float64Array(e)}update(e,t){if(!(e<0||e>=this.values.length))for(this.values[e]=this.values[e]+t,e++;e<this.tree.length;)this.tree[e]=this.tree[e]+t,e+=e&-e}query(e){let t=0;for(;e>0;)t+=this.tree[e]||0,e-=e&-e;return t}set(e,t){e<0||e>=this.values.length||(this.values[e]=t)}get length(){return this.values.length}get(e){return this.values[e]||0}getValues(){return this.values}findLowerBound(e){let t=0,n=this.tree.length,r=1<<Math.floor(Math.log2(n-1));for(;r>0;){let i=t+r;if(i<n){let n=this.tree[i]||0;n<=e&&(t=i,e-=n)}r>>=1}return t}rebuild(){this.tree.fill(0);for(let e=0;e<this.values.length;e++)this.tree[e+1]=this.values[e]||0;for(let e=1;e<this.tree.length;e++){let t=e+(e&-e);t<this.tree.length&&(this.tree[t]=this.tree[t]+this.tree[e])}}resize(e){if(e===this.values.length)return;let t=new Float64Array(e);t.set(this.values.subarray(0,Math.min(e,this.values.length))),this.values=t,this.tree=new Float64Array(e+1),this.rebuild()}shift(e){if(e===0)return;let t=this.values.length,n=new Float64Array(t);e>0?n.set(this.values.subarray(0,Math.min(t-e,this.values.length)),e):n.set(this.values.subarray(-e)),this.values=n,this.rebuild()}};function n(e){return e===null||typeof window<`u`&&e===window}function r(e){return!!e&&typeof e==`object`&&`tagName`in e&&e.tagName===`BODY`}function i(e){return n(e)||r(e)}function a(e){return!!e&&`getBoundingClientRect`in e}function o(e){return!!e&&`scrollLeft`in e}function s(e){return typeof e==`object`&&!!e&&(`align`in e||`behavior`in e||`isCorrection`in e)}function c(e,t){return typeof e==`object`&&e?e.x||0:(t===`horizontal`||t===`both`)&&e||0}function l(e,t){return typeof e==`object`&&e?e.y||0:(t===`vertical`||t===`both`)&&e||0}function u(e){let{rowIndex:t,colIndex:n,options:r,itemsLength:i,columnCount:a,direction:o,usableWidth:c,usableHeight:l,totalWidth:u,totalHeight:d,gap:f,columnGap:p,fixedSize:m,fixedWidth:h,relativeScrollX:g,relativeScrollY:_,getItemSizeY:v,getItemSizeX:y,getItemQueryY:b,getItemQueryX:x,getColumnSize:S,getColumnQuery:C,stickyIndices:w}=e,T;T=s(r)?r.align:r;let E=(typeof T==`object`?T.x:T)||`auto`,D=(typeof T==`object`?T.y:T)||`auto`,O=o===`vertical`||o===`both`,k=o===`horizontal`||o===`both`,A=g,j=_,M=0,N=0,P=E===`auto`?`auto`:E,F=D===`auto`?`auto`:D;if(t!=null){let e=0;if(O&&w&&w.length>0){let n,r=0,i=w.length-1;for(;r<=i;){let e=r+i>>>1;w[e]<t?(n=w[e],r=e+1):i=e-1}n!==void 0&&(e=m===null?v(n)-f:m)}let n=0;if(t>=i?(n=d,N=0):(n=m===null?b(t):t*(m+f),N=m===null?v(t)-f:m),D===`start`)j=n-e;else if(D===`center`)j=n-(l-N)/2;else if(D===`end`)j=n-(l-N);else if(!(N<=l-e?n>=_+e-.5&&n+N<=_+l+.5:n<=_+e+.5&&n+N>=_+l-.5)){let t=n-e,r=n-(l-N);N<=l-e?n<_+e?(j=t,F=`start`):(j=r,F=`end`):Math.abs(t-_)<Math.abs(r-_)?(j=t,F=`start`):(j=r,F=`end`)}}if(n!=null){let e=0;if(k&&w&&w.length>0&&(o===`horizontal`||o===`both`)){let t,r=0,i=w.length-1;for(;r<=i;){let e=r+i>>>1;w[e]<n?(t=w[e],r=e+1):i=e-1}t!==void 0&&(e=o===`horizontal`?m===null?y(t)-p:m:h===null?S(t)-p:h)}let t=0;if(n>=a&&a>0?(t=u,M=0):o===`horizontal`?(t=m===null?x(n):n*(m+p),M=m===null?y(n)-p:m):(t=C(n),M=S(n)-p),E===`start`)A=t-e;else if(E===`center`)A=t-(c-M)/2;else if(E===`end`)A=t-(c-M);else if(!(M<=c-e?t>=g+e-.5&&t+M<=g+c+.5:t<=g+e+.5&&t+M>=g+c-.5)){let n=t-e,r=t-(c-M);M<=c-e?t<g+e?(A=n,P=`start`):(A=r,P=`end`):Math.abs(n-g)<Math.abs(r-g)?(A=n,P=`start`):(A=r,P=`end`)}}return A=Math.max(0,Math.min(A,Math.max(0,u-c))),j=Math.max(0,Math.min(j,Math.max(0,d-l))),{targetX:A,targetY:j,itemWidth:M,itemHeight:N,effectiveAlignX:P,effectiveAlignY:F}}function d(e){let{direction:t,relativeScrollX:n,relativeScrollY:r,usableWidth:i,usableHeight:a,itemsLength:o,bufferBefore:s,bufferAfter:c,gap:l,columnGap:u,fixedSize:d,findLowerBoundY:f,findLowerBoundX:p,queryY:m,queryX:h}=e,g=t===`vertical`||t===`both`,_=0,v=o;if(g)if(d!==null)_=Math.floor(r/(d+l)),v=Math.ceil((r+a)/(d+l));else{_=f(r);let e=r+a;v=f(e),v<o&&m(v)<e&&v++}else if(d!==null)_=Math.floor(n/(d+u)),v=Math.ceil((n+i)/(d+u));else{_=p(n);let e=n+i;v=p(e),v<o&&h(v)<e&&v++}return{start:Math.max(0,_-s),end:Math.min(o,v+c)}}function f(e){let{columnCount:t,relativeScrollX:n,usableWidth:r,colBuffer:i,fixedWidth:a,columnGap:o,findLowerBound:s,query:c,totalColsQuery:l}=e;if(!t)return{start:0,end:0,padStart:0,padEnd:0};let u=0,d=t;if(a!==null)u=Math.floor(n/(a+o)),d=Math.ceil((n+r)/(a+o));else{u=s(n);let e=c(u),i=u;for(;i<t&&e<n+r;)e=c(++i);d=i}let f=Math.max(0,u-i),p=Math.min(t,d+i),m=a===null?c(f):f*(a+o),h=a===null?Math.max(0,l()-o):t*(a+o)-o,g=a===null?c(p)-(p>=t?o:0):p*(a+o)-(p>=t?o:0);return{start:f,end:p,padStart:m,padEnd:Math.max(0,h-g)}}function p(e){let{index:t,isSticky:n,direction:r,relativeScrollX:i,relativeScrollY:a,originalX:o,originalY:s,width:c,height:l,stickyIndices:u,fixedSize:d,fixedWidth:f,gap:p,columnGap:m,getItemQueryY:h,getItemQueryX:g}=e,_=!1,v={x:0,y:0};if(!n)return{isStickyActive:_,stickyOffset:v};if((r===`vertical`||r===`both`)&&a>s){let e,n=0,r=u.length-1;for(;n<=r;){let i=n+r>>>1;u[i]>t?(e=u[i],r=i-1):n=i+1}if(e!==void 0){let t=d===null?h(e):e*(d+p);a>=t?_=!1:(_=!0,v.y=Math.max(0,Math.min(l,t-a))-l)}else _=!0}if((r===`horizontal`||r===`both`&&!_)&&i>o){let e,n=0,a=u.length-1;for(;n<=a;){let r=n+a>>>1;u[r]>t?(e=u[r],a=r-1):n=r+1}if(e!==void 0){let t=r===`horizontal`?d===null?g(e):e*(d+m):f===null?g(e):e*(f+m);i>=t?_=!1:(_=!0,v.x=Math.max(0,Math.min(c,t-i))-c)}else _=!0}return{isStickyActive:_,stickyOffset:v}}function m(e){let{index:t,direction:n,fixedSize:r,gap:i,columnGap:a,usableWidth:o,usableHeight:s,totalWidth:c,queryY:l,queryX:u,getSizeY:d,getSizeX:f}=e,p=0,m=0,h=0,g=0;return n===`horizontal`?(p=r===null?u(t):t*(r+a),h=r===null?f(t)-a:r,g=s):(m=(n===`vertical`||n===`both`)&&r!==null?t*(r+i):l(t),g=r===null?d(t)-i:r,h=n===`both`?c:o),{height:g,width:h,x:p,y:m}}function h(e){let{item:t,direction:n,itemSize:r,containerTag:i,paddingStartX:a,paddingStartY:o,isHydrated:s}=e,c=n===`vertical`,l=n===`horizontal`,u=n===`both`,d=r==null||r===0,f={blockSize:l?`100%`:d?`auto`:`${t.size.height}px`};return c&&i===`table`?f.minInlineSize=`100%`:f.inlineSize=c?`100%`:d?`auto`:`${t.size.width}px`,d&&(c||(f.minInlineSize=`1px`),l||(f.minBlockSize=`1px`)),s&&(t.isStickyActive?((c||u)&&(f.insetBlockStart=`${o}px`),(l||u)&&(f.insetInlineStart=`${a}px`),f.transform=`translate(${t.stickyOffset.x}px, ${t.stickyOffset.y}px)`):f.transform=`translate(${t.offset.x}px, ${t.offset.y}px)`),f}function g(e){let{direction:t,itemsLength:n,columnCount:r,fixedSize:i,fixedWidth:a,gap:o,columnGap:s,usableWidth:c,usableHeight:l,queryY:u,queryX:d,queryColumn:f}=e,p=0,m=0;return t===`both`?(r>0&&(p=a===null?Math.max(0,f(r)-s):r*(a+s)-s),m=i===null?Math.max(0,u(n)-(n>0?o:0)):Math.max(0,n*(i+o)-(n>0?o:0)),p=Math.max(p,c),m=Math.max(m,l)):t===`horizontal`?(p=i===null?Math.max(0,d(n)-(n>0?s:0)):Math.max(0,n*(i+s)-(n>0?s:0)),m=l):(p=c,m=i===null?Math.max(0,u(n)-(n>0?o:0)):Math.max(0,n*(i+o)-(n>0?o:0))),{width:p,height:m}}const _=40,v=100,y=5;function b(n){let r=(0,e.ref)(0),i=(0,e.ref)(0),h=(0,e.ref)(!1),_=(0,e.ref)(!1),v=(0,e.ref)(!1),y=(0,e.ref)(!1),b=(0,e.ref)(0),x=(0,e.ref)(0),S=(0,e.reactive)({x:0,y:0}),C,w=(0,e.ref)(!1),T=new t(n.value.items?.length||0),E=new t(n.value.items?.length||0),D=new t(n.value.columnCount||0),O=(0,e.ref)(0),k=new Uint8Array,A=new Uint8Array,j=new Uint8Array,M=(0,e.ref)(null),N=(0,e.ref)(!1),P=[],F=(0,e.computed)(()=>n.value.itemSize===void 0||n.value.itemSize===null||n.value.itemSize===0),I=(0,e.computed)(()=>n.value.columnWidth===void 0||n.value.columnWidth===null||n.value.columnWidth===0),L=(0,e.computed)(()=>typeof n.value.itemSize==`number`&&n.value.itemSize>0?n.value.itemSize:null),R=(0,e.computed)(()=>typeof n.value.columnWidth==`number`&&n.value.columnWidth>0?n.value.columnWidth:null),z=(0,e.computed)(()=>n.value.defaultItemSize||L.value||40),B=(0,e.computed)(()=>[...n.value.stickyIndices||[]].sort((e,t)=>e-t)),ee=(0,e.computed)(()=>new Set(B.value)),V=(0,e.computed)(()=>c(n.value.scrollPaddingStart,n.value.direction)),te=(0,e.computed)(()=>c(n.value.scrollPaddingEnd,n.value.direction)),H=(0,e.computed)(()=>l(n.value.scrollPaddingStart,n.value.direction)),U=(0,e.computed)(()=>l(n.value.scrollPaddingEnd,n.value.direction)),W=(0,e.computed)(()=>{let e=n.value.direction===`horizontal`||n.value.direction===`both`;return b.value-(e?V.value+te.value:0)}),G=(0,e.computed)(()=>{let e=n.value.direction===`vertical`||n.value.direction===`both`;return x.value-(e?H.value+U.value:0)}),K=(0,e.computed)(()=>{if(O.value,!_.value&&n.value.ssrRange&&!y.value){let{start:e=0,end:t=0,colStart:r=0,colEnd:i=0}=n.value.ssrRange,a=n.value.columnCount||0;if(n.value.direction===`both`){if(a<=0)return 0;let e=i||a,t=D.query(e)-D.query(r);return Math.max(0,t-(e>r&&n.value.columnGap||0))}if(n.value.direction===`horizontal`){if(L.value!==null){let r=t-e;return Math.max(0,r*(L.value+(n.value.columnGap||0))-(r>0&&n.value.columnGap||0))}let r=T.query(t)-T.query(e);return Math.max(0,r-(t>e&&n.value.columnGap||0))}}return g({direction:n.value.direction||`vertical`,itemsLength:n.value.items.length,columnCount:n.value.columnCount||0,fixedSize:L.value,fixedWidth:R.value,gap:n.value.gap||0,columnGap:n.value.columnGap||0,usableWidth:W.value,usableHeight:G.value,queryY:e=>E.query(e),queryX:e=>T.query(e),queryColumn:e=>D.query(e)}).width}),q=(0,e.computed)(()=>{if(O.value,!_.value&&n.value.ssrRange&&!y.value){let{start:e,end:t}=n.value.ssrRange;if(n.value.direction===`vertical`||n.value.direction===`both`){if(L.value!==null){let r=t-e;return Math.max(0,r*(L.value+(n.value.gap||0))-(r>0&&n.value.gap||0))}let r=E.query(t)-E.query(e);return Math.max(0,r-(t>e&&n.value.gap||0))}}return g({direction:n.value.direction||`vertical`,itemsLength:n.value.items.length,columnCount:n.value.columnCount||0,fixedSize:L.value,fixedWidth:R.value,gap:n.value.gap||0,columnGap:n.value.columnGap||0,usableWidth:W.value,usableHeight:G.value,queryY:e=>E.query(e),queryX:e=>T.query(e),queryColumn:e=>D.query(e)}).height}),J=(0,e.computed)(()=>{let e=n.value.direction===`horizontal`||n.value.direction===`both`?c(n.value.scrollPaddingStart,n.value.direction):0;return Math.max(0,r.value+e-S.x)}),Y=(0,e.computed)(()=>{let e=n.value.direction===`vertical`||n.value.direction===`both`?l(n.value.scrollPaddingStart,n.value.direction):0;return Math.max(0,i.value+e-S.y)}),ne=e=>{O.value;let t=n.value.columnWidth;if(typeof t==`number`&&t>0)return t;if(Array.isArray(t)&&t.length>0){let r=t[e%t.length];return r!=null&&r>0?r:n.value.defaultColumnWidth||100}return typeof t==`function`?t(e):D.get(e)||n.value.defaultColumnWidth||100},X=(e,t,a)=>{let c=typeof a==`object`&&a&&`isCorrection`in a?a.isCorrection:!1,l=n.value.container||window,d=n.value.direction===`vertical`||n.value.direction===`both`,f=n.value.direction===`horizontal`||n.value.direction===`both`,{targetX:p,targetY:m,effectiveAlignX:h,effectiveAlignY:g}=u({rowIndex:e,colIndex:t,options:a,itemsLength:n.value.items.length,columnCount:n.value.columnCount||0,direction:n.value.direction||`vertical`,usableWidth:W.value,usableHeight:G.value,totalWidth:K.value,totalHeight:q.value,gap:n.value.gap||0,columnGap:n.value.columnGap||0,fixedSize:L.value,fixedWidth:R.value,relativeScrollX:J.value,relativeScrollY:Y.value,getItemSizeY:e=>E.get(e),getItemSizeX:e=>T.get(e),getItemQueryY:e=>E.query(e),getItemQueryX:e=>T.query(e),getColumnSize:e=>D.get(e),getColumnQuery:e=>D.query(e),stickyIndices:B.value});if(!c){let n=s(a)?a.behavior:void 0;M.value={rowIndex:e,colIndex:t,options:{align:{x:h,y:g},...n==null?{}:{behavior:n}}}}let _=p+S.x-(f?V.value:0),v=m+S.y-(d?H.value:0),y;s(a)&&(y=a.behavior);let b=c?`auto`:y||`smooth`;if(w.value=!0,typeof window<`u`&&l===window)window.scrollTo({left:t==null?void 0:Math.max(0,_),top:e==null?void 0:Math.max(0,v),behavior:b});else if(o(l)){let n={behavior:b};t!=null&&(n.left=Math.max(0,_)),e!=null&&(n.top=Math.max(0,v)),typeof l.scrollTo==`function`?l.scrollTo(n):(n.left!==void 0&&(l.scrollLeft=n.left),n.top!==void 0&&(l.scrollTop=n.top))}(b===`auto`||b===void 0)&&(t!=null&&(r.value=Math.max(0,_)),e!=null&&(i.value=Math.max(0,v)))},Z=(e,t,a)=>{let s=n.value.container||window;w.value=!0;let c=n.value.direction===`vertical`||n.value.direction===`both`,l=n.value.direction===`horizontal`||n.value.direction===`both`,u=e==null?null:l?Math.max(0,Math.min(e,Math.max(0,K.value-W.value))):Math.max(0,e),d=t==null?null:c?Math.max(0,Math.min(t,Math.max(0,q.value-G.value))):Math.max(0,t),f=typeof window<`u`&&s===window?window.scrollX:s.scrollLeft,p=typeof window<`u`&&s===window?window.scrollY:s.scrollTop,m=u===null?f:u+S.x-(l?V.value:0),h=d===null?p:d+S.y-(c?H.value:0);if(typeof window<`u`&&s===window)window.scrollTo({left:e==null?void 0:m,top:t==null?void 0:h,behavior:a?.behavior||`auto`});else if(o(s)){let n={behavior:a?.behavior||`auto`};e!=null&&(n.left=m),t!=null&&(n.top=h),typeof s.scrollTo==`function`?s.scrollTo(n):(n.left!==void 0&&(s.scrollLeft=n.left),n.top!==void 0&&(s.scrollTop=n.top))}(a?.behavior===`auto`||a?.behavior===void 0)&&(e!=null&&(r.value=m),t!=null&&(i.value=h))},re=()=>{let t=n.value.items,r=t.length,i=n.value.columnCount||0;if(T.resize(r),E.resize(r),D.resize(i),A.length!==r){let e=new Uint8Array(r);e.set(A.subarray(0,Math.min(r,A.length))),A=e}if(j.length!==r){let e=new Uint8Array(r);e.set(j.subarray(0,Math.min(r,j.length))),j=e}if(k.length!==i){let e=new Uint8Array(i);e.set(k.subarray(0,Math.min(i,k.length))),k=e}let a=0;if(n.value.restoreScrollOnPrepend&&P.length>0&&r>P.length){let e=P[0];if(e!==void 0){for(let n=1;n<=r-P.length;n++)if(t[n]===e){a=n;break}}}if(a>0){T.shift(a),E.shift(a),M.value&&M.value.rowIndex!==null&&M.value.rowIndex!==void 0&&(M.value.rowIndex+=a);let i=new Uint8Array(r),o=new Uint8Array(r);i.set(A.subarray(0,Math.min(r-a,A.length)),a),o.set(j.subarray(0,Math.min(r-a,j.length)),a),A=i,j=o;let s=n.value.gap||0,c=n.value.columnGap||0,l=0,u=0;for(let e=0;e<a;e++){let r=typeof n.value.itemSize==`function`?n.value.itemSize(t[e],e):z.value;n.value.direction===`horizontal`?l+=r+c:u+=r+s}(l>0||u>0)&&(0,e.nextTick)(()=>{Z(l>0?J.value+l:null,u>0?Y.value+u:null,{behavior:`auto`})})}if(i>0){let e=n.value.columnGap||0,t=!1,r=n.value.columnWidth;for(let a=0;a<i;a++){let i=D.get(a),o=k[a]===1;if(!I.value||!o&&i===0){let o=0;o=typeof r==`number`&&r>0?r:Array.isArray(r)&&r.length>0?r[a%r.length]||n.value.defaultColumnWidth||100:typeof r==`function`?r(a):n.value.defaultColumnWidth||100;let s=o+e;Math.abs(i-s)>.5?(D.set(a,s),k[a]=I.value?0:1,t=!0):I.value||(k[a]=1)}}t&&D.rebuild()}let o=n.value.gap||0,s=n.value.columnGap||0,c=!1;for(let e=0;e<r;e++){let t=n.value.items[e],r=T.get(e),i=E.get(e),a=n.value.direction===`vertical`,l=n.value.direction===`horizontal`,u=n.value.direction===`both`,d=A[e]===1,f=j[e]===1;if(l){if(!F.value||!d&&r===0){let i=(typeof n.value.itemSize==`function`?n.value.itemSize(t,e):z.value)+s;Math.abs(r-i)>.5?(T.set(e,i),A[e]=F.value?0:1,c=!0):F.value||(A[e]=1)}}else r!==0&&(T.set(e,0),A[e]=0,c=!0);if(a||u){if(!F.value||!f&&i===0){let r=(typeof n.value.itemSize==`function`?n.value.itemSize(t,e):z.value)+o;Math.abs(i-r)>.5?(E.set(e,r),j[e]=F.value?0:1,c=!0):F.value||(j[e]=1)}}else i!==0&&(E.set(e,0),j[e]=0,c=!0)}c&&(T.rebuild(),E.rebuild()),P=[...t],N.value=!0,O.value++},Q=()=>{if(n.value.hostElement&&typeof window<`u`){let e=n.value.hostElement.getBoundingClientRect(),t=n.value.container||window,r=0,i=0;if(t===window)r=e.left+window.scrollX,i=e.top+window.scrollY;else if(t===n.value.hostElement)r=0,i=0;else if(a(t)){let n=t.getBoundingClientRect();r=e.left-n.left+t.scrollLeft,i=e.top-n.top+t.scrollTop}(Math.abs(S.x-r)>.1||Math.abs(S.y-i)>.1)&&(S.x=r,S.y=i)}};(0,e.watch)([()=>n.value.items,()=>n.value.items.length,()=>n.value.direction,()=>n.value.columnCount,()=>n.value.columnWidth,()=>n.value.itemSize,()=>n.value.gap,()=>n.value.columnGap,()=>n.value.defaultItemSize,()=>n.value.defaultColumnWidth],re,{immediate:!0}),(0,e.watch)(()=>[n.value.container,n.value.hostElement],()=>{Q()});let ie=(0,e.computed)(()=>{if(O.value,(!_.value||v.value)&&n.value.ssrRange)return{start:n.value.ssrRange.start,end:n.value.ssrRange.end};let e=n.value.ssrRange&&!h.value?0:n.value.bufferBefore??5,t=n.value.bufferAfter??5;return d({direction:n.value.direction||`vertical`,relativeScrollX:J.value,relativeScrollY:Y.value,usableWidth:W.value,usableHeight:G.value,itemsLength:n.value.items.length,bufferBefore:e,bufferAfter:t,gap:n.value.gap||0,columnGap:n.value.columnGap||0,fixedSize:L.value,findLowerBoundY:e=>E.findLowerBound(e),findLowerBoundX:e=>T.findLowerBound(e),queryY:e=>E.query(e),queryX:e=>T.query(e)})}),ae=(0,e.computed)(()=>{O.value;let e=L.value,t=n.value.gap||0,r=n.value.columnGap||0;return n.value.direction===`horizontal`?e===null?T.findLowerBound(J.value):Math.floor(J.value/(e+r)):e===null?E.findLowerBound(Y.value):Math.floor(Y.value/(e+t))}),oe=[],se=(0,e.computed)(()=>{O.value;let{start:e,end:t}=ie.value,r=[],i=L.value,a=n.value.gap||0,o=n.value.columnGap||0,s=B.value,c=ee.value,l=new Set;for(let n=e;n<t;n++)l.add(n);if(_.value||!n.value.ssrRange){let n=ae.value,r,i=0,a=s.length-1;for(;i<=a;){let e=i+a>>>1;s[e]<n?(r=s[e],i=e+1):a=e-1}r!==void 0&&l.add(r);let o=0,c=s.length-1,u=-1;for(;o<=c;){let t=o+c>>>1;s[t]>=e?(u=t,c=t-1):o=t+1}if(u!==-1)for(let e=u;e<s.length;e++){let n=s[e];if(n>=t)break;l.add(n)}}let u=Array.from(l).sort((e,t)=>e-t),d=n.value.ssrRange?.start||0,f=n.value.ssrRange?.colStart||0,h=0,g=0;!_.value&&n.value.ssrRange&&(g=n.value.direction===`vertical`||n.value.direction===`both`?i===null?E.query(d):d*(i+a):0,n.value.direction===`horizontal`?h=i===null?T.query(f):f*(i+o):n.value.direction===`both`&&(h=D.query(f)));let v=new Map(oe.map(e=>[e.index,e]));for(let e of u){let t=n.value.items[e];if(t===void 0)continue;let{x:i,y:a,width:o,height:l}=m({index:e,direction:n.value.direction||`vertical`,fixedSize:L.value,gap:n.value.gap||0,columnGap:n.value.columnGap||0,usableWidth:W.value,usableHeight:G.value,totalWidth:K.value,queryY:e=>E.query(e),queryX:e=>T.query(e),getSizeY:e=>E.get(e),getSizeX:e=>T.get(e)}),u=c.has(e),d=i,f=a,{isStickyActive:_,stickyOffset:y}=p({index:e,isSticky:u,direction:n.value.direction||`vertical`,relativeScrollX:J.value,relativeScrollY:Y.value,originalX:d,originalY:f,width:o,height:l,stickyIndices:s,fixedSize:L.value,fixedWidth:R.value,gap:n.value.gap||0,columnGap:n.value.columnGap||0,getItemQueryY:e=>E.query(e),getItemQueryX:e=>T.query(e)}),b=d-h,x=f-g,S=v.get(e);S&&S.item===t&&S.offset.x===b&&S.offset.y===x&&S.size.width===o&&S.size.height===l&&S.isSticky===u&&S.isStickyActive===_&&S.stickyOffset.x===y.x&&S.stickyOffset.y===y.y?r.push(S):r.push({item:t,index:e,offset:{x:b,y:x},size:{width:o,height:l},originalX:d,originalY:f,isSticky:u,isStickyActive:_,stickyOffset:y})}return oe=r,r}),ce=(0,e.computed)(()=>{O.value;let e=n.value.columnCount||0;if(!e)return{start:0,end:0,padStart:0,padEnd:0};if((!_.value||v.value)&&n.value.ssrRange){let{colStart:t=0,colEnd:r=0}=n.value.ssrRange;return{start:Math.max(0,t),end:Math.min(e,r||e),padStart:0,padEnd:0}}let t=n.value.ssrRange&&!h.value?0:2;return f({columnCount:e,relativeScrollX:J.value,usableWidth:W.value,colBuffer:t,fixedWidth:R.value,columnGap:n.value.columnGap||0,findLowerBound:e=>D.findLowerBound(e),query:e=>D.query(e),totalColsQuery:()=>D.query(e)})}),le=(0,e.computed)(()=>{O.value;let e=L.value,t=n.value.columnGap||0,r=0;return n.value.direction===`horizontal`?r=e===null?T.findLowerBound(J.value):Math.floor(J.value/(e+t)):n.value.direction===`both`&&(r=D.findLowerBound(J.value)),{items:se.value,currentIndex:ae.value,currentColIndex:r,scrollOffset:{x:J.value,y:Y.value},viewportSize:{width:b.value,height:x.value},totalSize:{width:K.value,height:q.value},isScrolling:h.value,isProgrammaticScroll:w.value,range:ie.value,columnRange:ce.value}}),ue=()=>{w.value=!1,M.value=null},$=e=>{let t=e.target;typeof window>`u`||(t===window||t===document?(r.value=window.scrollX,i.value=window.scrollY,b.value=document.documentElement.clientWidth,x.value=document.documentElement.clientHeight):o(t)&&(r.value=t.scrollLeft,i.value=t.scrollTop,b.value=t.clientWidth,x.value=t.clientHeight),h.value||=(w.value||(M.value=null),!0),clearTimeout(C),C=setTimeout(()=>{h.value=!1,w.value=!1},250))},de=e=>{let t=!1,r=0,i=0,a=n.value.gap||0,o=n.value.columnGap||0,s=J.value,c=Y.value,l=n.value.direction===`horizontal`?L.value===null?T.findLowerBound(s):Math.floor(s/(L.value+o)):L.value===null?E.findLowerBound(c):Math.floor(c/(L.value+a)),u=n.value.direction===`both`?D.findLowerBound(s):n.value.direction===`horizontal`?l:0,d=n.value.direction===`horizontal`,f=n.value.direction===`vertical`,p=n.value.direction===`both`,m=new Set,h=new Set;for(let{index:s,inlineSize:c,blockSize:g,element:_}of e){if(c<=0&&g<=0)continue;let e=F.value||typeof n.value.itemSize==`function`;if(s>=0&&!m.has(s)&&e&&g>0){if(m.add(s),d&&c>0){let e=T.get(s),n=c+o;if(!A[s]||Math.abs(n-e)>.1){let i=n-e;T.update(s,i),A[s]=1,t=!0,s<l&&(r+=i)}}if(f||p){let e=E.get(s),n=g+a;if(!j[s]||Math.abs(n-e)>.1){let r=n-e;E.update(s,r),j[s]=1,t=!0,s<l&&(i+=r)}}}let v=I.value||typeof n.value.columnWidth==`function`;if(p&&_&&n.value.columnCount&&v&&(c>0||_.dataset.colIndex===void 0)){let e=_.dataset.colIndex;if(e!=null){let i=Number.parseInt(e,10);if(i>=0&&i<(n.value.columnCount||0)&&!h.has(i)){h.add(i);let e=D.get(i),n=c+o;if(!k[i]||Math.abs(e-n)>.1){let a=n-e;Math.abs(a)>.1&&(D.update(i,a),t=!0,i<u&&(r+=a)),k[i]=1}}}else{let e=_.dataset.colIndex===void 0?Array.from(_.querySelectorAll(`[data-col-index]`)):[_];for(let i of e){let e=Number.parseInt(i.dataset.colIndex,10);if(e>=0&&e<(n.value.columnCount||0)&&!h.has(e)){h.add(e);let n=i.getBoundingClientRect().width,a=D.get(e),s=n+o;if(!k[e]||Math.abs(a-s)>.1){let n=s-a;Math.abs(n)>.1&&(D.update(e,n),t=!0,e<u&&(r+=n)),k[e]=1}}}}}}t&&(O.value++,!(M.value!==null||w.value)&&(r!==0||i!==0)&&Z(r===0?null:s+r,i===0?null:c+i,{behavior:`auto`}))},fe=(e,t,n,r)=>{de([{index:e,inlineSize:t,blockSize:n,element:r}])},pe=()=>{if(M.value&&!v.value){let{rowIndex:e,colIndex:t,options:r}=M.value;if(s(r)&&r.behavior===`smooth`&&h.value)return;let{targetX:i,targetY:a}=u({rowIndex:e,colIndex:t,options:r,itemsLength:n.value.items.length,columnCount:n.value.columnCount||0,direction:n.value.direction||`vertical`,usableWidth:W.value,usableHeight:G.value,totalWidth:K.value,totalHeight:q.value,gap:n.value.gap||0,columnGap:n.value.columnGap||0,fixedSize:L.value,fixedWidth:R.value,relativeScrollX:J.value,relativeScrollY:Y.value,getItemSizeY:e=>E.get(e),getItemSizeX:e=>T.get(e),getItemQueryY:e=>E.query(e),getItemQueryX:e=>T.query(e),getColumnSize:e=>D.get(e),getColumnQuery:e=>D.query(e),stickyIndices:B.value}),o=t==null||Math.abs(J.value-i)<1,c=e==null||Math.abs(Y.value-a)<1,l=t==null||t===void 0||k[t]===1,d=e==null||e===void 0||j[e]===1;o&&c?l&&d&&(M.value=null):X(e,t,s(r)?{...r,isCorrection:!0}:{align:r,isCorrection:!0})}};(0,e.watch)([O,b,x],pe),(0,e.watch)(h,e=>{e||pe()});let me=null,he=e=>{if(!e||typeof window>`u`)return;let t=e===window?document:e;if(t.addEventListener(`scroll`,$,{passive:!0}),e===window){b.value=document.documentElement.clientWidth,x.value=document.documentElement.clientHeight,r.value=window.scrollX,i.value=window.scrollY;let e=()=>{b.value=document.documentElement.clientWidth,x.value=document.documentElement.clientHeight,Q()};return window.addEventListener(`resize`,e),()=>{t.removeEventListener(`scroll`,$),window.removeEventListener(`resize`,e)}}else return b.value=e.clientWidth,x.value=e.clientHeight,r.value=e.scrollLeft,i.value=e.scrollTop,me=new ResizeObserver(t=>{for(let n of t)n.target===e&&(b.value=e.clientWidth,x.value=e.clientHeight,Q())}),me.observe(e),()=>{t.removeEventListener(`scroll`,$),me?.disconnect()}},ge;return(0,e.getCurrentInstance)()&&((0,e.onMounted)(()=>{y.value=!0,(0,e.watch)(()=>n.value.container,e=>{ge?.(),ge=he(e||null)},{immediate:!0}),Q(),n.value.ssrRange||n.value.initialScrollIndex!==void 0?(0,e.nextTick)(()=>{Q();let t=n.value.initialScrollIndex===void 0?n.value.ssrRange?.start:n.value.initialScrollIndex,r=n.value.initialScrollAlign||`start`;t!=null&&X(t,n.value.ssrRange?.colStart,{align:r,behavior:`auto`}),_.value=!0,v.value=!0,(0,e.nextTick)(()=>{v.value=!1})}):_.value=!0}),(0,e.onUnmounted)(()=>{ge?.()})),{renderedItems:se,totalWidth:K,totalHeight:q,scrollDetails:le,scrollToIndex:X,scrollToOffset:Z,stopProgrammaticScroll:ue,updateItemSize:fe,updateItemSizes:de,updateHostOffset:Q,columnRange:ce,getColumnWidth:ne,refresh:()=>{T.resize(0),E.resize(0),D.resize(0),k.fill(0),A.fill(0),j.fill(0),re()},isHydrated:_}}var x={key:0,class:`virtual-scroll-debug-info`},S=(0,e.defineComponent)({__name:`VirtualScroll`,props:{items:{},itemSize:{},direction:{default:`vertical`},bufferBefore:{default:5},bufferAfter:{default:5},container:{},ssrRange:{},columnCount:{default:0},columnWidth:{},containerTag:{default:`div`},wrapperTag:{default:`div`},itemTag:{default:`div`},scrollPaddingStart:{default:0},scrollPaddingEnd:{default:0},stickyHeader:{type:Boolean,default:!1},stickyFooter:{type:Boolean,default:!1},gap:{default:0},columnGap:{default:0},stickyIndices:{default:()=>[]},loadDistance:{default:200},loading:{type:Boolean,default:!1},restoreScrollOnPrepend:{type:Boolean,default:!1},initialScrollIndex:{},initialScrollAlign:{},defaultItemSize:{},defaultColumnWidth:{},debug:{type:Boolean,default:!1}},emits:[`scroll`,`load`,`visibleRangeChange`],setup(t,{expose:n,emit:r}){let a=t,o=r,s=(0,e.useSlots)(),c=(0,e.ref)(null),l=(0,e.ref)(null),u=(0,e.ref)(null),d=(0,e.ref)(null),f=new Map,p=(0,e.ref)(0),m=(0,e.ref)(0),g=(0,e.computed)(()=>{let e=a.container===void 0?c.value:a.container;return e===c.value||typeof window<`u`&&(e===window||e===null)}),_=(0,e.computed)(()=>{let e=a.scrollPaddingStart,t=a.scrollPaddingEnd;a.items.length;let n=typeof e==`object`?e.x||0:(a.direction===`horizontal`||a.direction===`both`)&&e||0,r=typeof e==`object`?e.y||0:(a.direction===`vertical`||a.direction===`both`)&&e||0,i=typeof t==`object`?t.x||0:(a.direction===`horizontal`||a.direction===`both`)&&t||0,o=typeof t==`object`?t.y||0:(a.direction===`vertical`||a.direction===`both`)&&t||0;return{items:a.items,itemSize:a.itemSize,direction:a.direction,bufferBefore:a.bufferBefore,bufferAfter:a.bufferAfter,container:a.container===void 0?c.value:a.container,hostElement:l.value,ssrRange:a.ssrRange,columnCount:a.columnCount,columnWidth:a.columnWidth,scrollPaddingStart:{x:n,y:r+(a.stickyHeader&&g.value?p.value:0)},scrollPaddingEnd:{x:i,y:o+(a.stickyFooter&&g.value?m.value:0)},gap:a.gap,columnGap:a.columnGap,stickyIndices:a.stickyIndices,loadDistance:a.loadDistance,loading:a.loading,restoreScrollOnPrepend:a.restoreScrollOnPrepend,initialScrollIndex:a.initialScrollIndex,initialScrollAlign:a.initialScrollAlign,defaultItemSize:a.defaultItemSize,defaultColumnWidth:a.defaultColumnWidth,debug:a.debug}}),{isHydrated:v,columnRange:y,renderedItems:S,scrollDetails:C,totalHeight:w,totalWidth:T,getColumnWidth:E,scrollToIndex:D,scrollToOffset:O,updateHostOffset:k,updateItemSizes:A,refresh:j,stopProgrammaticScroll:M}=b(_);function N(){j(),(0,e.nextTick)(()=>{let e=[];for(let[t,n]of f.entries())n&&e.push({index:t,inlineSize:n.offsetWidth,blockSize:n.offsetHeight,element:n});e.length>0&&A(e)})}(0,e.watch)(C,(e,t)=>{v.value&&(o(`scroll`,e),(!t||e.range.start!==t.range.start||e.range.end!==t.range.end||e.columnRange.start!==t.columnRange.start||e.columnRange.end!==t.columnRange.end)&&o(`visibleRangeChange`,{start:e.range.start,end:e.range.end,colStart:e.columnRange.start,colEnd:e.columnRange.end}),!a.loading&&(a.direction!==`horizontal`&&e.totalSize.height-(e.scrollOffset.y+e.viewportSize.height)<=a.loadDistance&&o(`load`,`vertical`),a.direction!==`vertical`&&e.totalSize.width-(e.scrollOffset.x+e.viewportSize.width)<=a.loadDistance&&o(`load`,`horizontal`)))}),(0,e.watch)(v,e=>{e&&o(`visibleRangeChange`,{start:C.value.range.start,end:C.value.range.end,colStart:C.value.columnRange.start,colEnd:C.value.columnRange.end})},{once:!0});let P=typeof window>`u`?null:new ResizeObserver(k),F=typeof window>`u`?null:new ResizeObserver(e=>{let t=[];for(let n of e){let e=n.target,r=Number(e.dataset.index),i=e.dataset.colIndex,a=n.contentRect.width,o=n.contentRect.height;n.borderBoxSize&&n.borderBoxSize.length>0?(a=n.borderBoxSize[0].inlineSize,o=n.borderBoxSize[0].blockSize):(a=e.offsetWidth,o=e.offsetHeight),i===void 0?Number.isNaN(r)||t.push({index:r,inlineSize:a,blockSize:o,element:e}):t.push({index:-1,inlineSize:a,blockSize:o,element:e})}t.length>0&&A(t)}),I=typeof window>`u`?null:new ResizeObserver(()=>{p.value=u.value?.offsetHeight||0,m.value=d.value?.offsetHeight||0,k()});(0,e.watch)(u,(e,t)=>{t&&I?.unobserve(t),e&&I?.observe(e)},{immediate:!0}),(0,e.watch)(d,(e,t)=>{t&&I?.unobserve(t),e&&I?.observe(e)},{immediate:!0}),(0,e.onMounted)(()=>{c.value&&P?.observe(c.value);for(let e of f.values())F?.observe(e),a.direction===`both`&&e.querySelectorAll(`[data-col-index]`).forEach(e=>F?.observe(e))}),(0,e.watch)([c,l],([e],[t])=>{t&&P?.unobserve(t),e&&P?.observe(e)});function L(e,t){if(e)f.set(t,e),F?.observe(e),a.direction===`both`&&e.querySelectorAll(`[data-col-index]`).forEach(e=>F?.observe(e));else{let e=f.get(t);e&&(F?.unobserve(e),a.direction===`both`&&e.querySelectorAll(`[data-col-index]`).forEach(e=>F?.unobserve(e)),f.delete(t))}}function R(e){let{viewportSize:t,scrollOffset:n}=C.value,r=a.direction!==`vertical`,i=a.direction!==`horizontal`;switch(e.key){case`Home`:e.preventDefault(),M(),D(0,0,`start`);break;case`End`:{e.preventDefault(),M();let t=a.items.length-1,n=(a.columnCount||0)>0?a.columnCount-1:0;r?i?D(t,n,`end`):D(0,t,`end`):D(t,0,`end`);break}case`ArrowUp`:e.preventDefault(),M(),O(null,n.y-40);break;case`ArrowDown`:e.preventDefault(),M(),O(null,n.y+40);break;case`ArrowLeft`:e.preventDefault(),M(),O(n.x-40,null);break;case`ArrowRight`:e.preventDefault(),M(),O(n.x+40,null);break;case`PageUp`:e.preventDefault(),M(),O(!i&&r?n.x-t.width:null,i?n.y-t.height:null);break;case`PageDown`:e.preventDefault(),M(),O(!i&&r?n.x+t.width:null,i?n.y+t.height:null);break}}(0,e.onUnmounted)(()=>{P?.disconnect(),F?.disconnect(),I?.disconnect()});let z=(0,e.computed)(()=>i(a.container)),B=(0,e.computed)(()=>z.value?{...a.direction===`vertical`?{}:{whiteSpace:`nowrap`}}:a.containerTag===`table`?{minInlineSize:a.direction===`vertical`?`100%`:`auto`}:{...a.direction===`vertical`?{}:{whiteSpace:`nowrap`}}),ee=(0,e.computed)(()=>({inlineSize:a.direction===`vertical`?`100%`:`${T.value}px`,blockSize:a.direction===`horizontal`?`100%`:`${w.value}px`})),V=(0,e.computed)(()=>{let e=a.direction===`horizontal`;return{display:e?`inline-block`:`block`,...e?{blockSize:`100%`,verticalAlign:`top`}:{inlineSize:`100%`}}}),te=(0,e.computed)(()=>({inlineSize:a.direction===`vertical`?`1px`:`${T.value}px`,blockSize:a.direction===`horizontal`?`1px`:`${w.value}px`}));function H(e){return h({containerTag:a.containerTag,direction:a.direction,isHydrated:v.value,item:e,itemSize:a.itemSize,paddingStartX:_.value.scrollPaddingStart.x,paddingStartY:_.value.scrollPaddingStart.y})}let U=(0,e.computed)(()=>a.debug),W=(0,e.computed)(()=>a.containerTag===`table`),G=(0,e.computed)(()=>W.value?`thead`:`div`),K=(0,e.computed)(()=>W.value?`tfoot`:`div`);return n({scrollDetails:C,columnRange:y,getColumnWidth:E,scrollToIndex:D,scrollToOffset:O,refresh:N,stopProgrammaticScroll:M}),(n,r)=>((0,e.openBlock)(),(0,e.createBlock)((0,e.resolveDynamicComponent)(t.containerTag),{ref_key:`hostRef`,ref:c,class:(0,e.normalizeClass)([`virtual-scroll-container`,[`virtual-scroll--${t.direction}`,{"virtual-scroll--hydrated":(0,e.unref)(v),"virtual-scroll--window":z.value,"virtual-scroll--table":W.value}]]),style:(0,e.normalizeStyle)(B.value),tabindex:`0`,onKeydown:R,onWheelPassive:(0,e.unref)(M),onPointerdownPassive:(0,e.unref)(M),onTouchstartPassive:(0,e.unref)(M)},{default:(0,e.withCtx)(()=>[s.header?((0,e.openBlock)(),(0,e.createBlock)((0,e.resolveDynamicComponent)(G.value),{key:0,ref_key:`headerRef`,ref:u,class:(0,e.normalizeClass)([`virtual-scroll-header`,{"virtual-scroll--sticky":t.stickyHeader}])},{default:(0,e.withCtx)(()=>[(0,e.renderSlot)(n.$slots,`header`,{},void 0,!0)]),_:3},8,[`class`])):(0,e.createCommentVNode)(``,!0),((0,e.openBlock)(),(0,e.createBlock)((0,e.resolveDynamicComponent)(t.wrapperTag),{ref_key:`wrapperRef`,ref:l,class:`virtual-scroll-wrapper`,style:(0,e.normalizeStyle)(ee.value)},{default:(0,e.withCtx)(()=>[W.value?((0,e.openBlock)(),(0,e.createBlock)((0,e.resolveDynamicComponent)(t.itemTag),{key:0,class:`virtual-scroll-spacer`,style:(0,e.normalizeStyle)(te.value)},{default:(0,e.withCtx)(()=>[...r[0]||=[(0,e.createElementVNode)(`td`,{style:{padding:`0`,border:`none`,"block-size":`inherit`}},null,-1)]]),_:1},8,[`style`])):(0,e.createCommentVNode)(``,!0),((0,e.openBlock)(!0),(0,e.createElementBlock)(e.Fragment,null,(0,e.renderList)((0,e.unref)(S),r=>((0,e.openBlock)(),(0,e.createBlock)((0,e.resolveDynamicComponent)(t.itemTag),{key:r.index,ref_for:!0,ref:e=>L(e,r.index),"data-index":r.index,class:(0,e.normalizeClass)([`virtual-scroll-item`,{"virtual-scroll--sticky":r.isStickyActive,"virtual-scroll--debug":U.value}]),style:(0,e.normalizeStyle)(H(r))},{default:(0,e.withCtx)(()=>[(0,e.renderSlot)(n.$slots,`item`,{item:r.item,index:r.index,columnRange:(0,e.unref)(y),getColumnWidth:(0,e.unref)(E),isSticky:r.isSticky,isStickyActive:r.isStickyActive},void 0,!0),U.value?((0,e.openBlock)(),(0,e.createElementBlock)(`div`,x,` #`+(0,e.toDisplayString)(r.index)+` (`+(0,e.toDisplayString)(Math.round(r.offset.x))+`, `+(0,e.toDisplayString)(Math.round(r.offset.y))+`) `,1)):(0,e.createCommentVNode)(``,!0)]),_:2},1032,[`data-index`,`class`,`style`]))),128))]),_:3},8,[`style`])),t.loading&&s.loading?((0,e.openBlock)(),(0,e.createElementBlock)(`div`,{key:1,class:`virtual-scroll-loading`,style:(0,e.normalizeStyle)(V.value)},[(0,e.renderSlot)(n.$slots,`loading`,{},void 0,!0)],4)):(0,e.createCommentVNode)(``,!0),s.footer?((0,e.openBlock)(),(0,e.createBlock)((0,e.resolveDynamicComponent)(K.value),{key:2,ref_key:`footerRef`,ref:d,class:(0,e.normalizeClass)([`virtual-scroll-footer`,{"virtual-scroll--sticky":t.stickyFooter}])},{default:(0,e.withCtx)(()=>[(0,e.renderSlot)(n.$slots,`footer`,{},void 0,!0)]),_:3},8,[`class`])):(0,e.createCommentVNode)(``,!0)]),_:3},40,[`class`,`style`,`onWheelPassive`,`onPointerdownPassive`,`onTouchstartPassive`]))}}),C=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},w=C(S,[[`__scopeId`,`data-v-922485f2`]]);exports.DEFAULT_BUFFER=5,exports.DEFAULT_COLUMN_WIDTH=100,exports.DEFAULT_ITEM_SIZE=40,exports.FenwickTree=t,exports.VirtualScroll=w,exports.calculateColumnRange=f,exports.calculateItemPosition=m,exports.calculateItemStyle=h,exports.calculateRange=d,exports.calculateScrollTarget=u,exports.calculateStickyItem=p,exports.calculateTotalSize=g,exports.getPaddingX=c,exports.getPaddingY=l,exports.isBody=r,exports.isElement=a,exports.isScrollToIndexOptions=s,exports.isScrollableElement=o,exports.isWindow=n,exports.isWindowLike=i,exports.useVirtualScroll=b;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|