vlist 0.1.2 → 1.5.4

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.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +558 -0
  3. package/dist/builder/a11y.d.ts +16 -0
  4. package/dist/builder/api.d.ts +21 -0
  5. package/dist/builder/context.d.ts +36 -0
  6. package/dist/builder/core.d.ts +16 -0
  7. package/dist/builder/data.d.ts +69 -0
  8. package/dist/builder/dom.d.ts +15 -0
  9. package/dist/builder/index.d.ts +25 -0
  10. package/dist/builder/materialize.d.ts +165 -0
  11. package/dist/builder/pool.d.ts +10 -0
  12. package/dist/builder/range.d.ts +10 -0
  13. package/dist/builder/scroll.d.ts +24 -0
  14. package/dist/builder/types.d.ts +464 -0
  15. package/dist/builder/velocity.d.ts +23 -0
  16. package/dist/constants.d.ts +58 -0
  17. package/dist/events/emitter.d.ts +18 -0
  18. package/dist/events/index.d.ts +6 -0
  19. package/dist/features/async/feature.d.ts +72 -0
  20. package/dist/features/async/index.d.ts +9 -0
  21. package/dist/features/async/manager.d.ts +103 -0
  22. package/dist/features/async/placeholder.d.ts +54 -0
  23. package/dist/features/async/sparse.d.ts +91 -0
  24. package/dist/features/autosize/feature.d.ts +34 -0
  25. package/dist/features/autosize/index.d.ts +2 -0
  26. package/dist/features/grid/feature.d.ts +48 -0
  27. package/dist/features/grid/index.d.ts +9 -0
  28. package/dist/features/grid/layout.d.ts +29 -0
  29. package/dist/features/grid/renderer.d.ts +71 -0
  30. package/dist/features/grid/types.d.ts +71 -0
  31. package/dist/features/groups/feature.d.ts +74 -0
  32. package/dist/features/groups/index.d.ts +10 -0
  33. package/dist/features/groups/layout.d.ts +47 -0
  34. package/dist/features/groups/sticky.d.ts +21 -0
  35. package/dist/features/groups/types.d.ts +86 -0
  36. package/dist/features/masonry/feature.d.ts +45 -0
  37. package/dist/features/masonry/index.d.ts +9 -0
  38. package/dist/features/masonry/layout.d.ts +29 -0
  39. package/dist/features/masonry/renderer.d.ts +55 -0
  40. package/dist/features/masonry/types.d.ts +68 -0
  41. package/dist/features/page/feature.d.ts +53 -0
  42. package/dist/features/page/index.d.ts +8 -0
  43. package/dist/features/scale/feature.d.ts +42 -0
  44. package/dist/features/scale/index.d.ts +10 -0
  45. package/dist/features/scrollbar/controller.d.ts +121 -0
  46. package/dist/features/scrollbar/feature.d.ts +60 -0
  47. package/dist/features/scrollbar/index.d.ts +8 -0
  48. package/dist/features/scrollbar/scrollbar.d.ts +73 -0
  49. package/dist/features/selection/feature.d.ts +75 -0
  50. package/dist/features/selection/index.d.ts +7 -0
  51. package/dist/features/selection/state.d.ts +115 -0
  52. package/dist/features/snapshots/feature.d.ts +79 -0
  53. package/dist/features/snapshots/index.d.ts +9 -0
  54. package/dist/features/table/feature.d.ts +67 -0
  55. package/dist/features/table/header.d.ts +49 -0
  56. package/dist/features/table/index.d.ts +10 -0
  57. package/dist/features/table/layout.d.ts +26 -0
  58. package/dist/features/table/renderer.d.ts +72 -0
  59. package/dist/features/table/types.d.ts +239 -0
  60. package/dist/index.d.ts +28 -0
  61. package/dist/index.js +1 -0
  62. package/dist/internals.d.ts +21 -0
  63. package/dist/internals.js +1 -0
  64. package/dist/rendering/index.d.ts +12 -0
  65. package/dist/rendering/measured.d.ts +52 -0
  66. package/dist/rendering/renderer.d.ts +111 -0
  67. package/dist/rendering/scale.d.ts +121 -0
  68. package/dist/rendering/scroll.d.ts +23 -0
  69. package/dist/rendering/sizes.d.ts +63 -0
  70. package/dist/rendering/sort.d.ts +33 -0
  71. package/dist/rendering/viewport.d.ts +139 -0
  72. package/dist/size.json +1 -0
  73. package/dist/types.d.ts +487 -0
  74. package/dist/utils/padding.d.ts +38 -0
  75. package/dist/utils/stats.d.ts +49 -0
  76. package/dist/vlist-extras.css +1 -0
  77. package/dist/vlist-grid.css +1 -0
  78. package/dist/vlist-masonry.css +1 -0
  79. package/dist/vlist-table.css +1 -0
  80. package/dist/vlist.css +1 -0
  81. package/package.json +66 -14
  82. package/README.MD +0 -80
  83. package/index.d.ts +0 -3
  84. package/index.js +0 -188
  85. package/virtual-scroll.component.d.ts +0 -37
  86. package/vlist.d.ts +0 -4
  87. package/vlist.metadata.json +0 -1
  88. package/vlist.umd.js +0 -189
@@ -0,0 +1,139 @@
1
+ /**
2
+ * vlist - Virtual Scrolling Core
3
+ * Pure functions for virtual scroll calculations
4
+ *
5
+ * Compression support is NOT imported here — it's injected via
6
+ * CompressionState parameters. When compression is inactive
7
+ * (the common case), all calculations use simple size-cache math
8
+ * with zero dependency on the compression module.
9
+ *
10
+ * This keeps the builder core lightweight. The withCompression feature
11
+ * and the monolithic createVList entry point import compression
12
+ * separately and pass the state in.
13
+ */
14
+ import type { Range, ViewportState } from "../types";
15
+ import type { SizeCache } from "./sizes";
16
+ /** Compression calculation result */
17
+ export interface CompressionState {
18
+ /** Whether compression is active */
19
+ isCompressed: boolean;
20
+ /** The actual total size (uncompressed) */
21
+ actualSize: number;
22
+ /** The virtual size (capped at MAX_VIRTUAL_SIZE) */
23
+ virtualSize: number;
24
+ /** Compression ratio (1 = no compression, <1 = compressed) */
25
+ ratio: number;
26
+ }
27
+ /**
28
+ * A "no compression" state for lists that don't need it.
29
+ * Used by the builder core when withCompression is not installed.
30
+ */
31
+ export declare const NO_COMPRESSION: CompressionState;
32
+ /**
33
+ * Create a trivial compression state from a size cache.
34
+ * No compression logic — just reads the total height.
35
+ * For use when the full compression module is not loaded.
36
+ */
37
+ export declare const getSimpleCompressionState: (_totalItems: number, sizeCache: SizeCache) => CompressionState;
38
+ /**
39
+ * Signature for the function that calculates the visible item range.
40
+ * The compression module provides a version that handles compressed scroll;
41
+ * virtual.ts provides a simple fallback for non-compressed lists.
42
+ */
43
+ export type VisibleRangeFn = (scrollPosition: number, containerHeight: number, sizeCache: SizeCache, totalItems: number, compression: CompressionState, out: Range) => Range;
44
+ /**
45
+ * Signature for the scroll-to-index calculator.
46
+ */
47
+ export type ScrollToIndexFn = (index: number, sizeCache: SizeCache, containerHeight: number, totalItems: number, compression: CompressionState, align: "start" | "center" | "end") => number;
48
+ /**
49
+ * Calculate visible range using size cache lookups.
50
+ * Fast path for lists that don't need compression (< ~350 000 items at 48px).
51
+ * Mutates `out` to avoid allocation on the scroll hot path.
52
+ */
53
+ export declare const simpleVisibleRange: VisibleRangeFn;
54
+ /**
55
+ * Calculate render range (adds overscan around visible range).
56
+ * This function is compression-agnostic — works for both paths.
57
+ * Mutates `out` to avoid allocation on the scroll hot path.
58
+ */
59
+ export declare const calculateRenderRange: (visibleRange: Range, overscan: number, totalItems: number, out: Range) => Range;
60
+ /**
61
+ * Simple scroll-to-index calculation (non-compressed).
62
+ * Uses size cache offsets directly.
63
+ */
64
+ export declare const simpleScrollToIndex: ScrollToIndexFn;
65
+ /**
66
+ * Calculate total content height.
67
+ * Uses compression's virtualSize when compressed, raw height otherwise.
68
+ */
69
+ export declare const calculateTotalSize: (_totalItems: number, sizeCache: SizeCache, compression?: CompressionState | null) => number;
70
+ /**
71
+ * Calculate actual total size (without compression cap)
72
+ */
73
+ export declare const calculateActualSize: (_totalItems: number, sizeCache: SizeCache) => number;
74
+ /**
75
+ * Calculate the offset (translateY) for an item
76
+ * For non-compressed lists only
77
+ */
78
+ export declare const calculateItemOffset: (index: number, sizeCache: SizeCache) => number;
79
+ /**
80
+ * Clamp scroll position to valid range
81
+ */
82
+ export declare const clampScrollPosition: (scrollPosition: number, totalHeight: number, containerHeight: number) => number;
83
+ /**
84
+ * Determine scroll direction
85
+ */
86
+ export declare const getScrollDirection: (currentScrollTop: number, previousScrollTop: number) => "up" | "down";
87
+ /**
88
+ * Create initial viewport state.
89
+ *
90
+ * Accepts an optional `visibleRangeFn` so that compression-aware callers
91
+ * can inject the compressed version. Defaults to `simpleVisibleRange`.
92
+ */
93
+ export declare const createViewportState: (containerHeight: number, sizeCache: SizeCache, totalItems: number, overscan: number, compression: CompressionState, visibleRangeFn?: VisibleRangeFn) => ViewportState;
94
+ /**
95
+ * Update viewport state after scroll.
96
+ * Mutates state in place for performance on the scroll hot path.
97
+ */
98
+ export declare const updateViewportState: (state: ViewportState, scrollPosition: number, sizeCache: SizeCache, totalItems: number, overscan: number, compression: CompressionState, visibleRangeFn?: VisibleRangeFn) => ViewportState;
99
+ /**
100
+ * Update viewport state when container resizes.
101
+ * Mutates state in place for performance.
102
+ */
103
+ export declare const updateViewportSize: (state: ViewportState, containerHeight: number, sizeCache: SizeCache, totalItems: number, overscan: number, compression: CompressionState, visibleRangeFn?: VisibleRangeFn) => ViewportState;
104
+ /**
105
+ * Update viewport state when total items changes.
106
+ * Mutates state in place for performance.
107
+ */
108
+ export declare const updateViewportItems: (state: ViewportState, sizeCache: SizeCache, totalItems: number, overscan: number, compression: CompressionState, visibleRangeFn?: VisibleRangeFn) => ViewportState;
109
+ /**
110
+ * Calculate scroll position to bring an index into view.
111
+ *
112
+ * Accepts an optional `scrollToIndexFn` so that compression-aware callers
113
+ * can inject the compressed version. Defaults to `simpleScrollToIndex`.
114
+ */
115
+ export declare const calculateScrollToIndex: (index: number, sizeCache: SizeCache, containerHeight: number, totalItems: number, align: "start" | "center" | "end" | undefined, compression: CompressionState, scrollToIndexFn?: ScrollToIndexFn) => number;
116
+ /**
117
+ * Check if two ranges are equal
118
+ */
119
+ export declare const rangesEqual: (a: Range, b: Range) => boolean;
120
+ /**
121
+ * Check if an index is within a range
122
+ */
123
+ export declare const isInRange: (index: number, range: Range) => boolean;
124
+ /**
125
+ * Get the count of items in a range
126
+ */
127
+ export declare const getRangeCount: (range: Range) => number;
128
+ /**
129
+ * Create an array of indices from a range
130
+ */
131
+ export declare const rangeToIndices: (range: Range) => number[];
132
+ /**
133
+ * Calculate which indices need to be added/removed when range changes
134
+ */
135
+ export declare const diffRanges: (oldRange: Range, newRange: Range) => {
136
+ add: number[];
137
+ remove: number[];
138
+ };
139
+ //# sourceMappingURL=viewport.d.ts.map
package/dist/size.json ADDED
@@ -0,0 +1 @@
1
+ {"base":{"minified":"27.0","gzipped":"10.4"},"withGrid":{"minified":"39.0","gzipped":"14.3"},"withMasonry":{"minified":"36.6","gzipped":"13.8"},"withGroups":{"minified":"35.4","gzipped":"13.1"},"withAsync":{"minified":"39.6","gzipped":"14.8"},"withSelection":{"minified":"37.0","gzipped":"13.1"},"withScale":{"minified":"37.9","gzipped":"13.5"},"withScrollbar":{"minified":"30.9","gzipped":"11.5"},"withPage":{"minified":"28.4","gzipped":"10.8"},"withSnapshots":{"minified":"29.4","gzipped":"11.2"},"withTable":{"minified":"44.6","gzipped":"15.9"},"withAutoSize":{"minified":"29.8","gzipped":"11.3"}}
@@ -0,0 +1,487 @@
1
+ /**
2
+ * vlist - Core Types
3
+ * Minimal, clean interfaces for the virtual list
4
+ */
5
+ /** Base event map with index signature */
6
+ export type EventMap = Record<string, unknown>;
7
+ /** Base item interface - must have an id */
8
+ export interface VListItem {
9
+ id: string | number;
10
+ [key: string]: unknown;
11
+ }
12
+ /** Groups configuration for createVList */
13
+ export interface GroupHeaderConfig {
14
+ /**
15
+ * Header size in pixels along the main axis (vertical scrolling).
16
+ * - `number` — Fixed size for all headers
17
+ * - `(group: string, groupIndex: number) => number` — Variable size per group
18
+ *
19
+ * Required when `orientation` is `'vertical'` (default).
20
+ * Ignored when `orientation` is `'horizontal'`.
21
+ */
22
+ height?: number | ((group: string, groupIndex: number) => number);
23
+ /**
24
+ * Header size in pixels along the main axis (horizontal scrolling).
25
+ * - `number` — Fixed size for all headers
26
+ * - `(group: string, groupIndex: number) => number` — Variable size per group
27
+ *
28
+ * Required when `orientation` is `'horizontal'`.
29
+ * Ignored when `orientation` is `'vertical'` (default).
30
+ */
31
+ width?: number | ((group: string, groupIndex: number) => number);
32
+ /**
33
+ * Template function to render a group header.
34
+ * Receives the group key and the group's sequential index (0-based).
35
+ */
36
+ template: (group: string, groupIndex: number) => string | HTMLElement;
37
+ }
38
+ export interface GroupsConfig {
39
+ /**
40
+ * Determine which group an item belongs to.
41
+ * Called with the DATA index (index into the original items array).
42
+ * Items with the same group key are grouped together.
43
+ *
44
+ * Items MUST be pre-sorted by group — the function is called in order
45
+ * and a new header is inserted whenever the return value changes.
46
+ */
47
+ getGroupForIndex: (index: number) => string;
48
+ /**
49
+ * Group header configuration — mirrors the `item` config shape.
50
+ */
51
+ header?: GroupHeaderConfig;
52
+ /**
53
+ * @deprecated Use `header.height` instead.
54
+ */
55
+ headerHeight?: number | ((group: string, groupIndex: number) => number);
56
+ /**
57
+ * @deprecated Use `header.template` instead.
58
+ */
59
+ headerTemplate?: (group: string, groupIndex: number) => string | HTMLElement;
60
+ /**
61
+ * Enable sticky headers (default: true).
62
+ * When true, the current group's header "sticks" to the top of the
63
+ * viewport and is pushed out by the next group's header approaching.
64
+ */
65
+ sticky?: boolean;
66
+ }
67
+ /** Grid configuration for createVList */
68
+ export interface GridConfig {
69
+ /**
70
+ * Number of columns in the grid.
71
+ * Item width = containerWidth / columns (minus gaps).
72
+ *
73
+ * Must be a positive integer ≥ 1.
74
+ */
75
+ columns: number;
76
+ /**
77
+ * Gap between grid items in pixels (default: 0).
78
+ * Applied both horizontally (between columns) and vertically (between rows).
79
+ */
80
+ gap?: number;
81
+ }
82
+ /** Masonry configuration for createVList */
83
+ export interface MasonryConfig {
84
+ /**
85
+ * Number of cross-axis divisions (columns in vertical, rows in horizontal).
86
+ * Items flow into the shortest column/row.
87
+ *
88
+ * Must be a positive integer ≥ 1.
89
+ */
90
+ columns: number;
91
+ /**
92
+ * Gap between masonry items in pixels (default: 0).
93
+ * Applied both horizontally and vertically.
94
+ */
95
+ gap?: number;
96
+ }
97
+ /** Item-specific configuration */
98
+ /** Context provided to size function in grid mode */
99
+ export interface GridSizeContext {
100
+ /** Current container width */
101
+ containerWidth: number;
102
+ /** Number of columns */
103
+ columns: number;
104
+ /** Gap between items in pixels */
105
+ gap: number;
106
+ /** Calculated column width */
107
+ columnWidth: number;
108
+ }
109
+ /** @deprecated Use GridSizeContext instead */
110
+ export type GridHeightContext = GridSizeContext;
111
+ export interface ItemConfig<T extends VListItem = VListItem> {
112
+ /**
113
+ * Item size in pixels along the main axis (required for vertical scrolling, cross-axis size for horizontal)
114
+ *
115
+ * - `number` — Fixed size for all items (fast path, zero overhead)
116
+ * - `(index: number) => number` — Variable size per item (prefix-sum based lookups)
117
+ * - `(index: number, context?: GridSizeContext) => number` — Dynamic size based on grid state
118
+ *
119
+ * In grid mode, the size function receives grid context as a second parameter,
120
+ * allowing you to calculate size based on column width to maintain aspect ratios:
121
+ *
122
+ * ```ts
123
+ * height: (index, context) => {
124
+ * if (context) {
125
+ * return context.columnWidth * 0.75; // 4:3 aspect ratio
126
+ * }
127
+ * return 200; // fallback for non-grid
128
+ * }
129
+ * ```
130
+ *
131
+ * Required when `orientation` is `'vertical'` (default).
132
+ * Optional when `orientation` is `'horizontal'` (used as cross-axis size).
133
+ */
134
+ height?: number | ((index: number, context?: GridSizeContext) => number);
135
+ /**
136
+ * Item width in pixels (required for horizontal scrolling)
137
+ *
138
+ * - `number` — Fixed width for all items (fast path, zero overhead)
139
+ * - `(index: number) => number` — Variable width per item (prefix-sum based lookups)
140
+ *
141
+ * Required when `orientation` is `'horizontal'`.
142
+ * Ignored when `orientation` is `'vertical'` (default).
143
+ */
144
+ width?: number | ((index: number) => number);
145
+ /**
146
+ * Estimated item height for auto-measurement (Mode B — vertical scrolling)
147
+ *
148
+ * When set, vlist renders items using this estimated size, measures their
149
+ * actual DOM height after render via ResizeObserver, caches the result,
150
+ * and adjusts scroll position to prevent visual jumps.
151
+ *
152
+ * Use this for content with unknown heights: variable-length text,
153
+ * images with unknown aspect ratios, mixed-media feeds, etc.
154
+ *
155
+ * Takes precedence only when `height` is not set — if both are provided,
156
+ * `height` wins (Mode A) and `estimatedHeight` is ignored.
157
+ *
158
+ * Ignored when `orientation` is `'horizontal'`.
159
+ */
160
+ estimatedHeight?: number;
161
+ /**
162
+ * Estimated item width for auto-measurement (Mode B — horizontal scrolling)
163
+ *
164
+ * Horizontal equivalent of `estimatedHeight`. When set, vlist renders items
165
+ * using this estimated width, measures actual DOM width after render,
166
+ * caches the result, and adjusts scroll position to prevent visual jumps.
167
+ *
168
+ * Takes precedence only when `width` is not set — if both are provided,
169
+ * `width` wins (Mode A) and `estimatedWidth` is ignored.
170
+ *
171
+ * Only used when `orientation` is `'horizontal'`.
172
+ */
173
+ estimatedWidth?: number;
174
+ /**
175
+ * Add a `.vlist-item--odd` class on odd-indexed items for zebra-stripe styling.
176
+ *
177
+ * Virtual lists recycle DOM elements out of document order, so CSS
178
+ * `:nth-child(even/odd)` does not match the logical item index.
179
+ * When enabled, vlist toggles the class based on the real item index,
180
+ * giving you a reliable CSS hook:
181
+ *
182
+ * ```css
183
+ * .vlist-item--odd { background: #fafafb; }
184
+ * ```
185
+ *
186
+ * - `true` — all items (including group headers) count for even/odd.
187
+ * - `"data"` — only data items count; group headers are excluded from
188
+ * the stripe index so they don't shift the alternating pattern.
189
+ * - `"even"` — counter resets after each group header; first data row
190
+ * in every group is even (non-striped). macOS Finder behavior.
191
+ * - `"odd"` — counter resets after each group header; first data row
192
+ * in every group is odd (striped).
193
+ *
194
+ * Default: `false` (no extra work on the render hot path).
195
+ */
196
+ striped?: boolean | "data" | "even" | "odd";
197
+ /**
198
+ * Gap between items in pixels along the main axis (default: 0).
199
+ *
200
+ * Adds consistent spacing between items without requiring CSS margin
201
+ * or padding hacks on `.vlist-item`. The gap is baked into the size
202
+ * cache (each slot = itemSize + gap) and subtracted from the DOM
203
+ * element height, so items are positioned with precise spacing.
204
+ *
205
+ * Works identically to the `gap` option in grid and masonry modes.
206
+ *
207
+ * ```ts
208
+ * item: {
209
+ * height: 60,
210
+ * gap: 10, // 10px between each item
211
+ * template: …,
212
+ * }
213
+ * ```
214
+ */
215
+ gap?: number;
216
+ /** Template function to render each item */
217
+ template: ItemTemplate<T>;
218
+ }
219
+ /** Scroll position snapshot for save/restore */
220
+ export interface ScrollSnapshot {
221
+ /** First visible item index */
222
+ index: number;
223
+ /** Pixel offset within the first visible item (how far it's scrolled off) */
224
+ offsetInItem: number;
225
+ /** Total item count at snapshot time (used by restore to set sizeCache) */
226
+ total?: number;
227
+ /** Selected item IDs (optional, included for convenience) */
228
+ selectedIds?: Array<string | number>;
229
+ }
230
+ /** Options for scrollToIndex / scrollToItem */
231
+ export interface ScrollToOptions {
232
+ /** Alignment within the viewport (default: 'start') */
233
+ align?: "start" | "center" | "end";
234
+ /** Scroll behavior (default: 'auto' = instant) */
235
+ behavior?: "auto" | "smooth";
236
+ /** Animation duration in ms (default: 300, only used with behavior: 'smooth') */
237
+ duration?: number;
238
+ }
239
+ /** Scroll behavior configuration */
240
+ export interface ScrollConfig {
241
+ /** Enable mouse wheel scrolling (default: true) */
242
+ wheel?: boolean;
243
+ /**
244
+ * Scrollbar gutter behavior for the native scrollbar (default: 'auto').
245
+ *
246
+ * - `'auto'` — Default browser behavior. On macOS with overlay scrollbars,
247
+ * no space is reserved. On Windows/Linux, the classic scrollbar takes
248
+ * ~15-17px from the content area when it appears.
249
+ * - `'stable'` — Always reserves space for the scrollbar via
250
+ * `scrollbar-gutter: stable`. Prevents layout shift when content
251
+ * grows past the container. Recommended for Windows/Linux targets.
252
+ *
253
+ * Has no effect when `withScrollbar()` is active (the native scrollbar
254
+ * is hidden and replaced by an absolute-positioned custom scrollbar).
255
+ */
256
+ gutter?: "auto" | "stable";
257
+ /**
258
+ * Wrap around when scrolling past boundaries (default: false).
259
+ *
260
+ * When `true`, `scrollToIndex` wraps around:
261
+ * - Index past the last item → wraps to the beginning
262
+ * - Negative index → wraps from the end
263
+ *
264
+ * Useful for carousels, wizards, and circular navigation.
265
+ */
266
+ wrap?: boolean;
267
+ /**
268
+ * Scrollbar mode (default: custom scrollbar).
269
+ *
270
+ * - *omitted* — Custom scrollbar (default), native scrollbar hidden via CSS
271
+ * - `'native'` — Browser native scrollbar (falls back to custom in compressed mode)
272
+ * - `'none'` — No scrollbar at all (native hidden, custom not created)
273
+ * - `ScrollbarOptions` — Custom scrollbar with fine-tuning options
274
+ */
275
+ scrollbar?: "native" | "none" | ScrollbarOptions;
276
+ /** External scroll element for window scrolling */
277
+ element?: Window;
278
+ /** Scroll idle detection timeout in ms (default: 150) */
279
+ idleTimeout?: number;
280
+ }
281
+ /** Custom scrollbar fine-tuning options */
282
+ export interface ScrollbarOptions {
283
+ /** Auto-hide scrollbar after idle (default: true) */
284
+ autoHide?: boolean;
285
+ /** Auto-hide delay in milliseconds (default: 1000) */
286
+ autoHideDelay?: number;
287
+ /** Minimum thumb size in pixels (default: 30) */
288
+ minThumbSize?: number;
289
+ /**
290
+ * Show scrollbar when hovering near the scrollbar edge (default: true).
291
+ * When true, an invisible hover zone is placed along the scrollbar edge.
292
+ * Moving the mouse into this zone reveals the scrollbar; it stays visible
293
+ * as long as the cursor remains over the zone or the track.
294
+ */
295
+ showOnHover?: boolean;
296
+ /**
297
+ * Width of the invisible hover zone in pixels (default: 16).
298
+ * Only used when `showOnHover` is true.
299
+ * A wider zone makes the scrollbar easier to discover;
300
+ * a narrower zone avoids interference with content near the edge.
301
+ */
302
+ hoverZoneWidth?: number;
303
+ /**
304
+ * Show scrollbar when the mouse enters the list viewport (default: true).
305
+ * When false, the scrollbar only appears on scroll or when hovering
306
+ * near the scrollbar edge (if `showOnHover` is true).
307
+ */
308
+ showOnViewportEnter?: boolean;
309
+ }
310
+ /**
311
+ * Scrollbar configuration.
312
+ * @deprecated Use `scroll.scrollbar` in `ScrollConfig` instead.
313
+ */
314
+ export interface ScrollbarConfig {
315
+ /** Enable scrollbar (default: true) */
316
+ enabled?: boolean;
317
+ /** Auto-hide scrollbar after idle (default: true) */
318
+ autoHide?: boolean;
319
+ /** Auto-hide delay in milliseconds (default: 1000) */
320
+ autoHideDelay?: number;
321
+ /** Minimum thumb size in pixels (default: 30) */
322
+ minThumbSize?: number;
323
+ }
324
+ /** Item template function */
325
+ export type ItemTemplate<T = VListItem> = (item: T, index: number, state: ItemState) => string | HTMLElement;
326
+ /** State passed to template */
327
+ export interface ItemState {
328
+ selected: boolean;
329
+ focused: boolean;
330
+ }
331
+ /** Selection mode */
332
+ export type SelectionMode = "none" | "single" | "multiple";
333
+ /** Selection configuration */
334
+ export interface SelectionConfig {
335
+ /** Selection mode (default: 'none') */
336
+ mode?: SelectionMode;
337
+ /** Initially selected item IDs */
338
+ initial?: Array<string | number>;
339
+ }
340
+ /** Selection state */
341
+ export interface SelectionState {
342
+ /** Currently selected item IDs */
343
+ selected: Set<string | number>;
344
+ /** Currently focused item index (-1 if none) */
345
+ focusedIndex: number;
346
+ /** Whether the focus ring should be visible (true for keyboard, false for mouse) */
347
+ focusVisible: boolean;
348
+ }
349
+ /** Adapter for async data loading */
350
+ export interface VListAdapter<T extends VListItem = VListItem> {
351
+ /** Fetch items for a range */
352
+ read: (params: AdapterParams) => Promise<AdapterResponse<T>>;
353
+ }
354
+ /** Parameters passed to adapter.read */
355
+ export interface AdapterParams {
356
+ /** Starting offset */
357
+ offset: number;
358
+ /** Number of items to fetch */
359
+ limit: number;
360
+ /** Optional cursor for cursor-based pagination */
361
+ cursor: string | undefined;
362
+ }
363
+ /** Response from adapter.read */
364
+ export interface AdapterResponse<T extends VListItem = VListItem> {
365
+ /** Fetched items */
366
+ items: T[];
367
+ /** Total count (if known) */
368
+ total?: number;
369
+ /** Next cursor (for cursor-based pagination) */
370
+ cursor?: string;
371
+ /** Whether more items exist */
372
+ hasMore?: boolean;
373
+ }
374
+ /** Visible range of items */
375
+ export interface Range {
376
+ start: number;
377
+ end: number;
378
+ }
379
+ /** Viewport state */
380
+ export interface ViewportState {
381
+ /** Current scroll position along main axis (scrollTop for vertical, scrollLeft for horizontal) */
382
+ scrollPosition: number;
383
+ /** Container size along main axis (height for vertical, width for horizontal) */
384
+ containerSize: number;
385
+ /** Total content size (may be capped for compression) */
386
+ totalSize: number;
387
+ /** Actual total size without compression */
388
+ actualSize: number;
389
+ /** Whether compression is active */
390
+ isCompressed: boolean;
391
+ /** Compression ratio (1 = no compression, <1 = compressed) */
392
+ compressionRatio: number;
393
+ /** Visible item range */
394
+ visibleRange: Range;
395
+ /** Render range (includes overscan) */
396
+ renderRange: Range;
397
+ }
398
+ /** Viewport state snapshot attached to error events for debugging */
399
+ export interface ErrorViewportSnapshot {
400
+ scrollPosition: number;
401
+ containerSize: number;
402
+ visibleRange: {
403
+ start: number;
404
+ end: number;
405
+ };
406
+ renderRange: {
407
+ start: number;
408
+ end: number;
409
+ };
410
+ totalItems: number;
411
+ isCompressed: boolean;
412
+ }
413
+ /** Event types and their payloads */
414
+ export interface VListEvents<T extends VListItem = VListItem> extends EventMap {
415
+ /** Item clicked */
416
+ "item:click": {
417
+ item: T;
418
+ index: number;
419
+ event: MouseEvent;
420
+ };
421
+ /** Item double-clicked */
422
+ "item:dblclick": {
423
+ item: T;
424
+ index: number;
425
+ event: MouseEvent;
426
+ };
427
+ /** Selection changed */
428
+ "selection:change": {
429
+ selected: Array<string | number>;
430
+ items: T[];
431
+ };
432
+ /** Scroll position changed */
433
+ scroll: {
434
+ scrollPosition: number;
435
+ direction: "up" | "down";
436
+ };
437
+ /** Scroll velocity changed */
438
+ "velocity:change": {
439
+ velocity: number;
440
+ reliable: boolean;
441
+ };
442
+ /** Visible range changed */
443
+ "range:change": {
444
+ range: Range;
445
+ };
446
+ /** Data loading started */
447
+ "load:start": {
448
+ offset: number;
449
+ limit: number;
450
+ };
451
+ /** Data loading completed */
452
+ "load:end": {
453
+ items: T[];
454
+ total?: number;
455
+ offset?: number;
456
+ };
457
+ /** Error occurred (includes viewport state when available) */
458
+ error: {
459
+ error: Error;
460
+ context: string;
461
+ viewport?: ErrorViewportSnapshot;
462
+ };
463
+ /** Container resized */
464
+ resize: {
465
+ height: number;
466
+ width: number;
467
+ };
468
+ /** Scroll idle — fired after scrolling stops and idle timeout elapses */
469
+ "scroll:idle": {
470
+ scrollPosition: number;
471
+ };
472
+ /** Data changed — fired after item removal or other data mutations */
473
+ "data:change": {
474
+ type: "remove";
475
+ id: string | number;
476
+ } | {
477
+ type: "update";
478
+ id: string | number;
479
+ };
480
+ /** Destroy — fired just before the instance is torn down */
481
+ destroy: undefined;
482
+ }
483
+ /** Event handler type */
484
+ export type EventHandler<T> = (payload: T) => void;
485
+ /** Unsubscribe function */
486
+ export type Unsubscribe = () => void;
487
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,38 @@
1
+ /**
2
+ * vlist/utils — Padding Resolution
3
+ * Shared helpers for resolving the CSS-shorthand padding config into
4
+ * concrete pixel values. Used by core (to apply CSS) and by features
5
+ * (to subtract cross-axis padding from container dimensions).
6
+ */
7
+ import type { BuilderConfig } from "../builder/types";
8
+ /** Padding config — re-exported from BuilderConfig for convenience */
9
+ export type PaddingConfig = NonNullable<BuilderConfig["padding"]>;
10
+ /** Resolved padding — all four sides in pixels */
11
+ export interface ResolvedPadding {
12
+ readonly top: number;
13
+ readonly right: number;
14
+ readonly bottom: number;
15
+ readonly left: number;
16
+ }
17
+ /**
18
+ * Resolve a padding config into all four sides.
19
+ *
20
+ * - `undefined` → { 0, 0, 0, 0 }
21
+ * - `number` → equal on all sides
22
+ * - `[v, h]` → top/bottom = v, left/right = h
23
+ * - `[t, r, b, l]` → per-side (CSS order)
24
+ *
25
+ * Returns a frozen singleton for the zero case (no allocation).
26
+ */
27
+ export declare const resolvePadding: (padding: PaddingConfig | undefined) => ResolvedPadding;
28
+ /**
29
+ * Total padding along the main axis (scroll direction).
30
+ * Vertical → top + bottom. Horizontal → left + right.
31
+ */
32
+ export declare const mainAxisPaddingFrom: (p: ResolvedPadding, isHorizontal: boolean) => number;
33
+ /**
34
+ * Total padding along the cross axis (perpendicular to scroll).
35
+ * Vertical → left + right. Horizontal → top + bottom.
36
+ */
37
+ export declare const crossAxisPaddingFrom: (p: ResolvedPadding, isHorizontal: boolean) => number;
38
+ //# sourceMappingURL=padding.d.ts.map