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.
- package/LICENSE +21 -0
- package/README.md +558 -0
- package/dist/builder/a11y.d.ts +16 -0
- package/dist/builder/api.d.ts +21 -0
- package/dist/builder/context.d.ts +36 -0
- package/dist/builder/core.d.ts +16 -0
- package/dist/builder/data.d.ts +69 -0
- package/dist/builder/dom.d.ts +15 -0
- package/dist/builder/index.d.ts +25 -0
- package/dist/builder/materialize.d.ts +165 -0
- package/dist/builder/pool.d.ts +10 -0
- package/dist/builder/range.d.ts +10 -0
- package/dist/builder/scroll.d.ts +24 -0
- package/dist/builder/types.d.ts +464 -0
- package/dist/builder/velocity.d.ts +23 -0
- package/dist/constants.d.ts +58 -0
- package/dist/events/emitter.d.ts +18 -0
- package/dist/events/index.d.ts +6 -0
- package/dist/features/async/feature.d.ts +72 -0
- package/dist/features/async/index.d.ts +9 -0
- package/dist/features/async/manager.d.ts +103 -0
- package/dist/features/async/placeholder.d.ts +54 -0
- package/dist/features/async/sparse.d.ts +91 -0
- package/dist/features/autosize/feature.d.ts +34 -0
- package/dist/features/autosize/index.d.ts +2 -0
- package/dist/features/grid/feature.d.ts +48 -0
- package/dist/features/grid/index.d.ts +9 -0
- package/dist/features/grid/layout.d.ts +29 -0
- package/dist/features/grid/renderer.d.ts +71 -0
- package/dist/features/grid/types.d.ts +71 -0
- package/dist/features/groups/feature.d.ts +74 -0
- package/dist/features/groups/index.d.ts +10 -0
- package/dist/features/groups/layout.d.ts +47 -0
- package/dist/features/groups/sticky.d.ts +21 -0
- package/dist/features/groups/types.d.ts +86 -0
- package/dist/features/masonry/feature.d.ts +45 -0
- package/dist/features/masonry/index.d.ts +9 -0
- package/dist/features/masonry/layout.d.ts +29 -0
- package/dist/features/masonry/renderer.d.ts +55 -0
- package/dist/features/masonry/types.d.ts +68 -0
- package/dist/features/page/feature.d.ts +53 -0
- package/dist/features/page/index.d.ts +8 -0
- package/dist/features/scale/feature.d.ts +42 -0
- package/dist/features/scale/index.d.ts +10 -0
- package/dist/features/scrollbar/controller.d.ts +121 -0
- package/dist/features/scrollbar/feature.d.ts +60 -0
- package/dist/features/scrollbar/index.d.ts +8 -0
- package/dist/features/scrollbar/scrollbar.d.ts +73 -0
- package/dist/features/selection/feature.d.ts +75 -0
- package/dist/features/selection/index.d.ts +7 -0
- package/dist/features/selection/state.d.ts +115 -0
- package/dist/features/snapshots/feature.d.ts +79 -0
- package/dist/features/snapshots/index.d.ts +9 -0
- package/dist/features/table/feature.d.ts +67 -0
- package/dist/features/table/header.d.ts +49 -0
- package/dist/features/table/index.d.ts +10 -0
- package/dist/features/table/layout.d.ts +26 -0
- package/dist/features/table/renderer.d.ts +72 -0
- package/dist/features/table/types.d.ts +239 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +1 -0
- package/dist/internals.d.ts +21 -0
- package/dist/internals.js +1 -0
- package/dist/rendering/index.d.ts +12 -0
- package/dist/rendering/measured.d.ts +52 -0
- package/dist/rendering/renderer.d.ts +111 -0
- package/dist/rendering/scale.d.ts +121 -0
- package/dist/rendering/scroll.d.ts +23 -0
- package/dist/rendering/sizes.d.ts +63 -0
- package/dist/rendering/sort.d.ts +33 -0
- package/dist/rendering/viewport.d.ts +139 -0
- package/dist/size.json +1 -0
- package/dist/types.d.ts +487 -0
- package/dist/utils/padding.d.ts +38 -0
- package/dist/utils/stats.d.ts +49 -0
- package/dist/vlist-extras.css +1 -0
- package/dist/vlist-grid.css +1 -0
- package/dist/vlist-masonry.css +1 -0
- package/dist/vlist-table.css +1 -0
- package/dist/vlist.css +1 -0
- package/package.json +66 -14
- package/README.MD +0 -80
- package/index.d.ts +0 -3
- package/index.js +0 -188
- package/virtual-scroll.component.d.ts +0 -37
- package/vlist.d.ts +0 -4
- package/vlist.metadata.json +0 -1
- package/vlist.umd.js +0 -189
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist/builder - Types
|
|
3
|
+
* Feature interface, builder config, builder context, and return types
|
|
4
|
+
*/
|
|
5
|
+
import type { VListItem, VListEvents, ItemConfig, ItemTemplate, Range, ViewportState, EventHandler, Unsubscribe, ScrollToOptions, ScrollSnapshot, VListAdapter, GridConfig, MasonryConfig, GroupsConfig, SelectionConfig, ScrollbarOptions } from "../types";
|
|
6
|
+
import type { DOMStructure, Renderer, SizeCache, CompressionContext } from "../rendering";
|
|
7
|
+
import type { CompressionState } from "../rendering/viewport";
|
|
8
|
+
import type { SimpleDataManager } from "./data";
|
|
9
|
+
import type { ScrollController } from "../features/scrollbar/controller";
|
|
10
|
+
import type { Emitter } from "../events";
|
|
11
|
+
/** Options for the reload() method. */
|
|
12
|
+
export interface ReloadOptions {
|
|
13
|
+
/**
|
|
14
|
+
* Skip the initial page-1 load after resetting state.
|
|
15
|
+
* When true, reload() clears data and DOM but does NOT call loadInitial().
|
|
16
|
+
* The caller is responsible for loading data (e.g. via restoreScroll).
|
|
17
|
+
*
|
|
18
|
+
* Automatically set to true when `snapshot` is provided with meaningful data.
|
|
19
|
+
*/
|
|
20
|
+
skipInitialLoad?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Snapshot to restore after resetting state.
|
|
23
|
+
*
|
|
24
|
+
* When provided with meaningful data (total > 0 and index > 0),
|
|
25
|
+
* reload() automatically:
|
|
26
|
+
* 1. Skips loadInitial() (no wasted page-1 request)
|
|
27
|
+
* 2. Calls restoreScroll(snapshot) to load data at the target position
|
|
28
|
+
*
|
|
29
|
+
* This eliminates the need for the consumer to manually coordinate
|
|
30
|
+
* skipInitialLoad and restoreScroll after reload.
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* // Before: manual coordination
|
|
34
|
+
* const hasRestorable = snapshot && snapshot.total > 0 && snapshot.index > 0;
|
|
35
|
+
* await list.reload(hasRestorable ? { skipInitialLoad: true } : undefined);
|
|
36
|
+
* if (hasRestorable) list.restoreScroll(snapshot);
|
|
37
|
+
*
|
|
38
|
+
* // After: vlist handles it
|
|
39
|
+
* await list.reload({ snapshot });
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
snapshot?: ScrollSnapshot;
|
|
43
|
+
}
|
|
44
|
+
/** Configuration accepted by the builder's vlist() factory */
|
|
45
|
+
export interface BuilderConfig<T extends VListItem = VListItem> {
|
|
46
|
+
/** Container element or selector */
|
|
47
|
+
container: HTMLElement | string;
|
|
48
|
+
/** Item configuration (height/width and template) */
|
|
49
|
+
item: ItemConfig<T>;
|
|
50
|
+
/** Static items array (optional if using adapter via feature) */
|
|
51
|
+
items?: T[];
|
|
52
|
+
/** Number of extra items to render outside viewport (default: 3) */
|
|
53
|
+
overscan?: number;
|
|
54
|
+
/** Custom CSS class prefix (default: 'vlist') */
|
|
55
|
+
classPrefix?: string;
|
|
56
|
+
/** Accessible label for the listbox */
|
|
57
|
+
ariaLabel?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Layout orientation (default: 'vertical')
|
|
60
|
+
* - 'vertical' — Standard top-to-bottom scrolling
|
|
61
|
+
* - 'horizontal' — Left-to-right scrolling
|
|
62
|
+
*/
|
|
63
|
+
orientation?: "vertical" | "horizontal";
|
|
64
|
+
/**
|
|
65
|
+
* Padding around the list content (default: 0).
|
|
66
|
+
*
|
|
67
|
+
* Works exactly like CSS `padding` — adds inset space between the
|
|
68
|
+
* viewport edge and the items. Applied as CSS padding on the content
|
|
69
|
+
* element, so it works identically for list, grid, and masonry
|
|
70
|
+
* layouts with zero positioning overhead.
|
|
71
|
+
*
|
|
72
|
+
* Follows CSS shorthand convention:
|
|
73
|
+
* - `number` — Equal padding on all four sides
|
|
74
|
+
* - `[vertical, horizontal]` — Top/bottom and left/right
|
|
75
|
+
* - `[top, right, bottom, left]` — Per-side (CSS order)
|
|
76
|
+
*
|
|
77
|
+
* ```ts
|
|
78
|
+
* padding: 16 // 16px all sides
|
|
79
|
+
* padding: [16, 12] // 16px top/bottom, 12px left/right
|
|
80
|
+
* padding: [16, 12, 20, 8] // top, right, bottom, left
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
padding?: number | [number, number] | [number, number, number, number];
|
|
84
|
+
/**
|
|
85
|
+
* Enable built-in keyboard navigation and item-level focus (default: true).
|
|
86
|
+
*
|
|
87
|
+
* When `true`, the list handles Arrow Up/Down, Home/End key presses
|
|
88
|
+
* to move focus between items (WAI-ARIA listbox pattern).
|
|
89
|
+
*
|
|
90
|
+
* Set to `false` to disable all built-in keyboard handling, useful
|
|
91
|
+
* when the host application provides its own keyboard navigation or
|
|
92
|
+
* when the list is non-interactive.
|
|
93
|
+
*
|
|
94
|
+
* Replaces the former `accessible` option.
|
|
95
|
+
*/
|
|
96
|
+
interactive?: boolean;
|
|
97
|
+
/** @deprecated Use `interactive` instead */
|
|
98
|
+
accessible?: boolean;
|
|
99
|
+
/** Reverse mode for chat UIs */
|
|
100
|
+
reverse?: boolean;
|
|
101
|
+
/** Scroll behavior configuration (non-scrollbar options) */
|
|
102
|
+
scroll?: {
|
|
103
|
+
/** Enable mouse wheel scrolling (default: true) */
|
|
104
|
+
wheel?: boolean;
|
|
105
|
+
/** Wrap around when scrolling past boundaries (default: false) */
|
|
106
|
+
wrap?: boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Native scrollbar gutter behavior (default: 'auto').
|
|
109
|
+
* - `'auto'` — default browser behavior
|
|
110
|
+
* - `'stable'` — reserves space via `scrollbar-gutter: stable`
|
|
111
|
+
*/
|
|
112
|
+
gutter?: "auto" | "stable";
|
|
113
|
+
/** Scroll idle detection timeout in ms (default: 150) */
|
|
114
|
+
idleTimeout?: number;
|
|
115
|
+
/** External scroll element for window scrolling */
|
|
116
|
+
element?: Window;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Extended configuration accepted by framework adapters (React, Vue, Svelte, SolidJS).
|
|
121
|
+
*
|
|
122
|
+
* Adds convenience fields that adapters translate into `.use(withX())` calls
|
|
123
|
+
* automatically. For the core builder, use `BuilderConfig` directly.
|
|
124
|
+
*/
|
|
125
|
+
export interface VListConfig<T extends VListItem = VListItem> extends Omit<BuilderConfig<T>, "scroll"> {
|
|
126
|
+
/** Scroll behavior configuration — extends core scroll config with scrollbar shorthand. */
|
|
127
|
+
scroll?: BuilderConfig["scroll"] & {
|
|
128
|
+
/** Scrollbar mode (shorthand — same as top-level `scrollbar`). */
|
|
129
|
+
scrollbar?: "native" | "none" | ScrollbarOptions;
|
|
130
|
+
};
|
|
131
|
+
/** Layout mode (default: list). Set to `'grid'` or `'masonry'` with matching config. */
|
|
132
|
+
layout?: "list" | "grid" | "masonry";
|
|
133
|
+
/** Grid configuration — used when `layout` is `'grid'`. */
|
|
134
|
+
grid?: GridConfig;
|
|
135
|
+
/** Masonry configuration — used when `layout` is `'masonry'`. */
|
|
136
|
+
masonry?: MasonryConfig;
|
|
137
|
+
/** Async data adapter — enables `withAsync()`. Omit `items` when using an adapter. */
|
|
138
|
+
adapter?: VListAdapter<T>;
|
|
139
|
+
/** Loading behavior for async adapter. */
|
|
140
|
+
loading?: {
|
|
141
|
+
/** Velocity threshold above which data loading is skipped (px/ms). Default: 5 */
|
|
142
|
+
cancelThreshold?: number;
|
|
143
|
+
/** Velocity threshold for preloading (px/ms). Default: 2 */
|
|
144
|
+
preloadThreshold?: number;
|
|
145
|
+
/** Number of items to preload in scroll direction. Default: 50 */
|
|
146
|
+
preloadAhead?: number;
|
|
147
|
+
};
|
|
148
|
+
/** Section grouping configuration — enables `withGroups()`. */
|
|
149
|
+
groups?: GroupsConfig;
|
|
150
|
+
/** Selection configuration — enables `withSelection()`. */
|
|
151
|
+
selection?: SelectionConfig;
|
|
152
|
+
/** Top-level scrollbar shorthand (alternative to `scroll.scrollbar`). */
|
|
153
|
+
scrollbar?: "native" | "none" | ScrollbarOptions;
|
|
154
|
+
}
|
|
155
|
+
/** Resolved configuration stored inside BuilderContext */
|
|
156
|
+
export interface ResolvedBuilderConfig {
|
|
157
|
+
readonly overscan: number;
|
|
158
|
+
readonly classPrefix: string;
|
|
159
|
+
readonly reverse: boolean;
|
|
160
|
+
readonly wrap: boolean;
|
|
161
|
+
readonly horizontal: boolean;
|
|
162
|
+
readonly ariaIdPrefix: string;
|
|
163
|
+
readonly interactive: boolean;
|
|
164
|
+
}
|
|
165
|
+
/** Cached compression state */
|
|
166
|
+
export interface CachedCompression {
|
|
167
|
+
state: CompressionState;
|
|
168
|
+
totalItems: number;
|
|
169
|
+
}
|
|
170
|
+
/** Builder state */
|
|
171
|
+
export interface BuilderState {
|
|
172
|
+
viewportState: ViewportState;
|
|
173
|
+
lastRenderRange: Range;
|
|
174
|
+
isInitialized: boolean;
|
|
175
|
+
isDestroyed: boolean;
|
|
176
|
+
cachedCompression: CachedCompression | null;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* BuilderContext — the internal interface that features receive during setup.
|
|
180
|
+
*
|
|
181
|
+
* Provides access to all core components plus registration points for
|
|
182
|
+
* handlers, methods, and cleanup callbacks.
|
|
183
|
+
*/
|
|
184
|
+
export interface BuilderContext<T extends VListItem = VListItem> {
|
|
185
|
+
readonly dom: DOMStructure;
|
|
186
|
+
readonly sizeCache: SizeCache;
|
|
187
|
+
readonly emitter: Emitter<VListEvents<T>>;
|
|
188
|
+
readonly config: ResolvedBuilderConfig;
|
|
189
|
+
/** The raw user-provided builder config (for features that need original values) */
|
|
190
|
+
readonly rawConfig: BuilderConfig<T>;
|
|
191
|
+
/**
|
|
192
|
+
* Adjust a raw scroll position (from calculateScrollToIndex or similar)
|
|
193
|
+
* to account for CSS padding on the content element.
|
|
194
|
+
*
|
|
195
|
+
* CSS padding expands the scrollable area beyond `getTotalSize()`, so
|
|
196
|
+
* the max-scroll clamp must include the padding. The position itself
|
|
197
|
+
* is unchanged — CSS padding already offsets items visually.
|
|
198
|
+
*
|
|
199
|
+
* No-op when padding is 0 — returns the input unchanged.
|
|
200
|
+
*/
|
|
201
|
+
adjustScrollPosition(position: number): number;
|
|
202
|
+
renderer: Renderer<T>;
|
|
203
|
+
dataManager: SimpleDataManager<T>;
|
|
204
|
+
scrollController: ScrollController;
|
|
205
|
+
state: BuilderState;
|
|
206
|
+
/**
|
|
207
|
+
* Features register lightweight callbacks that run after each
|
|
208
|
+
* scroll-triggered render. These are NOT on the hot path —
|
|
209
|
+
* they run after DOM updates are complete.
|
|
210
|
+
*/
|
|
211
|
+
afterScroll: Array<(scrollPosition: number, direction: string) => void>;
|
|
212
|
+
/**
|
|
213
|
+
* Features register callbacks that run after a batch of new items
|
|
214
|
+
* is rendered. Receives the newly rendered items with their indices
|
|
215
|
+
* and DOM elements. Used by withAutoSize for ResizeObserver observation.
|
|
216
|
+
*/
|
|
217
|
+
afterRenderBatch: Array<(items: ReadonlyArray<{
|
|
218
|
+
index: number;
|
|
219
|
+
element: HTMLElement;
|
|
220
|
+
}>) => void>;
|
|
221
|
+
/**
|
|
222
|
+
* Features register callbacks that run when scrolling becomes idle.
|
|
223
|
+
* Use this for deferred work that should not run on the hot scroll path
|
|
224
|
+
* (e.g. reordering DOM children for accessibility).
|
|
225
|
+
*/
|
|
226
|
+
idleHandlers: Array<() => void>;
|
|
227
|
+
/**
|
|
228
|
+
* Features register handlers for user interaction events.
|
|
229
|
+
* These are attached as DOM event listeners during .build().
|
|
230
|
+
*/
|
|
231
|
+
clickHandlers: Array<(event: MouseEvent) => void>;
|
|
232
|
+
keydownHandlers: Array<(event: KeyboardEvent) => void>;
|
|
233
|
+
resizeHandlers: Array<(width: number, height: number) => void>;
|
|
234
|
+
contentSizeHandlers: Array<() => void>;
|
|
235
|
+
destroyHandlers: Array<() => void>;
|
|
236
|
+
/** Features register public methods by name. Exposed on the returned API. */
|
|
237
|
+
methods: Map<string, Function>;
|
|
238
|
+
replaceTemplate(template: ItemTemplate<T>): void;
|
|
239
|
+
replaceRenderer(renderer: Renderer<T>): void;
|
|
240
|
+
replaceDataManager(dataManager: SimpleDataManager<T>): void;
|
|
241
|
+
replaceScrollController(scrollController: ScrollController): void;
|
|
242
|
+
getItemsForRange(range: Range): T[];
|
|
243
|
+
getAllLoadedItems(): T[];
|
|
244
|
+
getVirtualTotal(): number;
|
|
245
|
+
getCachedCompression(): CompressionState;
|
|
246
|
+
getCompressionContext(): CompressionContext;
|
|
247
|
+
renderIfNeeded(): void;
|
|
248
|
+
forceRender(): void;
|
|
249
|
+
/**
|
|
250
|
+
* Remove all rendered DOM elements and return them to the pool.
|
|
251
|
+
* Used by reload to force a full re-render from scratch, bypassing
|
|
252
|
+
* the ID-based optimization that skips template updates for same-ID items.
|
|
253
|
+
*/
|
|
254
|
+
invalidateRendered(): void;
|
|
255
|
+
/**
|
|
256
|
+
* Get current render functions (for wrapping by selection/other features).
|
|
257
|
+
* Call this BEFORE setRenderFns to capture the current functions.
|
|
258
|
+
*/
|
|
259
|
+
getRenderFns(): {
|
|
260
|
+
renderIfNeeded: () => void;
|
|
261
|
+
forceRender: () => void;
|
|
262
|
+
};
|
|
263
|
+
/**
|
|
264
|
+
* Get current container width (for grid feature).
|
|
265
|
+
* This returns the width detected by ResizeObserver, which is more reliable
|
|
266
|
+
* than viewport.clientWidth in test environments.
|
|
267
|
+
*/
|
|
268
|
+
getContainerWidth(): number;
|
|
269
|
+
/**
|
|
270
|
+
* Replace the virtual-total function.
|
|
271
|
+
* Used by grid/groups features that change what "total" means
|
|
272
|
+
* (e.g. row count instead of item count).
|
|
273
|
+
*/
|
|
274
|
+
setVirtualTotalFn(fn: () => number): void;
|
|
275
|
+
/**
|
|
276
|
+
* Used by groups feature to inject grouped size function and by grid to add gap.
|
|
277
|
+
*/
|
|
278
|
+
rebuildSizeCache(total?: number): void;
|
|
279
|
+
/**
|
|
280
|
+
* Set a new effective size config function/value.
|
|
281
|
+
* Features that change sizes (groups, grid) call this before rebuildSizeCache.
|
|
282
|
+
*/
|
|
283
|
+
setSizeConfig(config: number | ((index: number) => number)): void;
|
|
284
|
+
/**
|
|
285
|
+
* Update content size on the main axis (height for vertical, width for horizontal).
|
|
286
|
+
*/
|
|
287
|
+
updateContentSize(totalSize: number): void;
|
|
288
|
+
/**
|
|
289
|
+
* Update compression mode when total items changes.
|
|
290
|
+
* Called by the core after data changes and by features that alter totals.
|
|
291
|
+
*/
|
|
292
|
+
updateCompressionMode(): void;
|
|
293
|
+
/**
|
|
294
|
+
* Replace the visible-range calculation function.
|
|
295
|
+
* Used by withCompression to inject compressed range calculation.
|
|
296
|
+
*/
|
|
297
|
+
setVisibleRangeFn(fn: (scrollTop: number, containerHeight: number, sc: SizeCache, totalItems: number, out: Range) => void): void;
|
|
298
|
+
/**
|
|
299
|
+
* Replace the scroll-to-index position calculator.
|
|
300
|
+
* Used by withCompression to inject compressed position calculation.
|
|
301
|
+
*/
|
|
302
|
+
setScrollToPosFn(fn: (index: number, sc: SizeCache, containerHeight: number, totalItems: number, align: "start" | "center" | "end") => number): void;
|
|
303
|
+
/**
|
|
304
|
+
* Get the current scroll-to-index position calculator.
|
|
305
|
+
* Returns the compression-aware version if withScale has replaced it.
|
|
306
|
+
*/
|
|
307
|
+
getScrollToPos(index: number, containerHeight: number, totalItems: number, align: "start" | "center" | "end"): number;
|
|
308
|
+
/**
|
|
309
|
+
* Replace the item positioning function.
|
|
310
|
+
* Used by withCompression to inject compressed item positioning.
|
|
311
|
+
*/
|
|
312
|
+
setPositionElementFn(fn: (element: HTMLElement, index: number) => void): void;
|
|
313
|
+
/**
|
|
314
|
+
* Replace the item-class updater used by the renderer proxy.
|
|
315
|
+
* Used by withTable to inject its own class-update logic (the core
|
|
316
|
+
* rendered Map is empty in table mode — the table renderer owns the DOM).
|
|
317
|
+
*/
|
|
318
|
+
setUpdateItemClassesFn(fn: (index: number, isSelected: boolean, isFocused: boolean) => void): void;
|
|
319
|
+
/**
|
|
320
|
+
* Replace the render functions.
|
|
321
|
+
* Used by grid/groups features that need to completely replace the render logic
|
|
322
|
+
* (e.g., to convert row ranges to item ranges for grid rendering).
|
|
323
|
+
*/
|
|
324
|
+
setRenderFns(renderIfNeeded: () => void, forceRender: () => void): void;
|
|
325
|
+
/**
|
|
326
|
+
* Replace the scroll get/set functions.
|
|
327
|
+
* Used by withCompression to manage a virtual scroll position that bypasses
|
|
328
|
+
* native DOM scrollTop (which can't represent compressed scroll space).
|
|
329
|
+
*/
|
|
330
|
+
setScrollFns(getTop: () => number, setTop: (pos: number) => void): void;
|
|
331
|
+
/**
|
|
332
|
+
* Set the scroll target element (default: viewport).
|
|
333
|
+
* Used by window mode feature to use window instead of viewport for scroll events.
|
|
334
|
+
*/
|
|
335
|
+
setScrollTarget(target: HTMLElement | Window): void;
|
|
336
|
+
/**
|
|
337
|
+
* Get the current scroll target element.
|
|
338
|
+
* Returns the element/window that scroll events are bound to.
|
|
339
|
+
*/
|
|
340
|
+
getScrollTarget(): HTMLElement | Window;
|
|
341
|
+
/**
|
|
342
|
+
* Override container dimension getters.
|
|
343
|
+
* Used by window mode feature to use window.innerWidth/innerHeight instead
|
|
344
|
+
* of viewport.clientWidth/clientHeight.
|
|
345
|
+
*/
|
|
346
|
+
setContainerDimensions(getter: {
|
|
347
|
+
width: () => number;
|
|
348
|
+
height: () => number;
|
|
349
|
+
}): void;
|
|
350
|
+
/**
|
|
351
|
+
* Disable the ResizeObserver on the viewport element.
|
|
352
|
+
* Used by window mode feature where the viewport doesn't need observation
|
|
353
|
+
* (window resize is used instead).
|
|
354
|
+
*/
|
|
355
|
+
disableViewportResize(): void;
|
|
356
|
+
/**
|
|
357
|
+
* Disable the wheel handler attached to the viewport.
|
|
358
|
+
* Used by window mode feature where wheel events should trigger native
|
|
359
|
+
* window scrolling instead of manual scroll position updates.
|
|
360
|
+
*/
|
|
361
|
+
disableWheelHandler(): void;
|
|
362
|
+
/**
|
|
363
|
+
* Get the current stripe-index function.
|
|
364
|
+
* Maps a layout index to the index used for even/odd striping.
|
|
365
|
+
* Default: identity (returns the index as-is).
|
|
366
|
+
* When groups override this, group headers return -1 (skip striping)
|
|
367
|
+
* and data items return a contiguous data-only index.
|
|
368
|
+
*/
|
|
369
|
+
getStripeIndexFn(): (index: number) => number;
|
|
370
|
+
/**
|
|
371
|
+
* Replace the stripe-index function.
|
|
372
|
+
* Used by the groups feature when `striped: "data"` to exclude
|
|
373
|
+
* group headers from the even/odd alternation.
|
|
374
|
+
*/
|
|
375
|
+
setStripeIndexFn(fn: (index: number) => number): void;
|
|
376
|
+
/**
|
|
377
|
+
* Returns the current item-to-scroll-index mapping function.
|
|
378
|
+
* Identity for flat lists; floor(index/columns) for grids where the
|
|
379
|
+
* size cache is indexed by row, not by flat item index.
|
|
380
|
+
*/
|
|
381
|
+
getItemToScrollIndexFn(): (index: number) => number;
|
|
382
|
+
/**
|
|
383
|
+
* Replace the item-to-scroll-index mapping function.
|
|
384
|
+
* Called by withGrid to convert flat item indices to row indices
|
|
385
|
+
* so that scrollToFocus uses the correct size-cache entry.
|
|
386
|
+
*/
|
|
387
|
+
setItemToScrollIndexFn(fn: (index: number) => number): void;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* VListFeature — the interface for builder features.
|
|
391
|
+
*
|
|
392
|
+
* Each feature:
|
|
393
|
+
* - Has a unique name (used for deduplication and error messages)
|
|
394
|
+
* - Optionally declares a priority (lower runs first, default: 50)
|
|
395
|
+
* - Implements setup() which receives BuilderContext and wires in handlers/methods
|
|
396
|
+
* - Optionally implements destroy() for cleanup
|
|
397
|
+
* - Optionally declares methods it adds and features it conflicts with
|
|
398
|
+
*/
|
|
399
|
+
export interface VListFeature<T extends VListItem = VListItem> {
|
|
400
|
+
/** Unique feature name (used for deduplication and error messages) */
|
|
401
|
+
readonly name: string;
|
|
402
|
+
/** Execution priority — lower runs first (default: 50) */
|
|
403
|
+
readonly priority?: number;
|
|
404
|
+
/** Setup function — receives BuilderContext, wires handlers and methods */
|
|
405
|
+
setup(ctx: BuilderContext<T>): void;
|
|
406
|
+
/** Cleanup function — called on destroy */
|
|
407
|
+
destroy?(): void;
|
|
408
|
+
/** Methods this feature adds to the public API */
|
|
409
|
+
readonly methods?: readonly string[];
|
|
410
|
+
/** Features this feature conflicts with (cannot be combined) */
|
|
411
|
+
readonly conflicts?: readonly string[];
|
|
412
|
+
}
|
|
413
|
+
/** Factory function that returns a feature */
|
|
414
|
+
export type FeatureFactory<T extends VListItem = VListItem> = VListFeature<T>;
|
|
415
|
+
/** Chainable builder interface */
|
|
416
|
+
export interface VListBuilder<T extends VListItem = VListItem> {
|
|
417
|
+
/** Register a feature. Chainable. */
|
|
418
|
+
use(feature: VListFeature<T>): VListBuilder<T>;
|
|
419
|
+
/** Materialize the virtual list. Creates DOM, initializes features, returns API. */
|
|
420
|
+
build(): VList<T>;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* VList instance API — returned by `vlist(config).build()`.
|
|
424
|
+
*
|
|
425
|
+
* Always-available methods (data, scroll, events, lifecycle) are required.
|
|
426
|
+
* Feature methods (selection, snapshots, grid, etc.) are optional —
|
|
427
|
+
* they exist on the instance only when the corresponding feature is
|
|
428
|
+
* registered via `.use()`.
|
|
429
|
+
*/
|
|
430
|
+
export interface VList<T extends VListItem = VListItem> {
|
|
431
|
+
/** The root DOM element */
|
|
432
|
+
readonly element: HTMLElement;
|
|
433
|
+
/** Current items */
|
|
434
|
+
readonly items: readonly T[];
|
|
435
|
+
/** Total item count */
|
|
436
|
+
readonly total: number;
|
|
437
|
+
setItems: (items: T[]) => void;
|
|
438
|
+
appendItems: (items: T[]) => void;
|
|
439
|
+
prependItems: (items: T[]) => void;
|
|
440
|
+
updateItem: (id: string | number, updates: Partial<T>) => void;
|
|
441
|
+
removeItem: (id: string | number) => void;
|
|
442
|
+
getItemAt: (index: number) => T | undefined;
|
|
443
|
+
getIndexById: (id: string | number) => number;
|
|
444
|
+
reload: (options?: ReloadOptions) => Promise<void>;
|
|
445
|
+
scrollToIndex: (index: number, alignOrOptions?: "start" | "center" | "end" | ScrollToOptions) => void;
|
|
446
|
+
cancelScroll: () => void;
|
|
447
|
+
getScrollPosition: () => number;
|
|
448
|
+
on: <K extends keyof VListEvents<T>>(event: K, handler: EventHandler<VListEvents<T>[K]>) => Unsubscribe;
|
|
449
|
+
off: <K extends keyof VListEvents<T>>(event: K, handler: EventHandler<VListEvents<T>[K]>) => void;
|
|
450
|
+
destroy: () => void;
|
|
451
|
+
select?: (...ids: Array<string | number>) => void;
|
|
452
|
+
deselect?: (...ids: Array<string | number>) => void;
|
|
453
|
+
toggleSelect?: (id: string | number) => void;
|
|
454
|
+
selectAll?: () => void;
|
|
455
|
+
clearSelection?: () => void;
|
|
456
|
+
getSelected?: () => Array<string | number>;
|
|
457
|
+
getSelectedItems?: () => T[];
|
|
458
|
+
selectNext?: () => void;
|
|
459
|
+
selectPrevious?: () => void;
|
|
460
|
+
getScrollSnapshot?: () => ScrollSnapshot;
|
|
461
|
+
restoreScroll?: (snapshot: ScrollSnapshot) => void;
|
|
462
|
+
[key: string]: unknown;
|
|
463
|
+
}
|
|
464
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist/builder — Velocity Tracking
|
|
3
|
+
* Lightweight 2-sample velocity tracker for scroll momentum detection.
|
|
4
|
+
*/
|
|
5
|
+
export declare const MIN_RELIABLE_SAMPLES = 2;
|
|
6
|
+
/** Create a velocity tracker. */
|
|
7
|
+
export declare const createVelocityTracker: (_initialPosition?: number) => {
|
|
8
|
+
velocity: number;
|
|
9
|
+
sampleCount: number;
|
|
10
|
+
};
|
|
11
|
+
/** Update velocity from new scroll position. Mutates tracker in place. */
|
|
12
|
+
export declare const updateVelocityTracker: (tracker: {
|
|
13
|
+
velocity: number;
|
|
14
|
+
sampleCount: number;
|
|
15
|
+
_lp?: number;
|
|
16
|
+
_lt?: number;
|
|
17
|
+
}, newPosition: number) => {
|
|
18
|
+
velocity: number;
|
|
19
|
+
sampleCount: number;
|
|
20
|
+
_lp?: number;
|
|
21
|
+
_lt?: number;
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=velocity.d.ts.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist - Constants
|
|
3
|
+
* All default values and magic numbers in one place
|
|
4
|
+
*/
|
|
5
|
+
/** Default number of extra items to render outside viewport */
|
|
6
|
+
export declare const OVERSCAN = 3;
|
|
7
|
+
/** Default CSS class prefix */
|
|
8
|
+
export declare const CLASS_PREFIX = "vlist";
|
|
9
|
+
/** Distance from bottom (in pixels) to trigger infinite scroll */
|
|
10
|
+
export declare const LOAD_THRESHOLD = 200;
|
|
11
|
+
/** Default number of items to load per request */
|
|
12
|
+
export declare const INITIAL_LOAD_SIZE = 50;
|
|
13
|
+
/** Default load size for data manager */
|
|
14
|
+
export declare const LOAD_SIZE = 50;
|
|
15
|
+
/** Number of extra items to preload ahead of scroll direction */
|
|
16
|
+
export declare const PRELOAD_AHEAD = 50;
|
|
17
|
+
/**
|
|
18
|
+
* Velocity threshold above which data loading is cancelled (px/ms)
|
|
19
|
+
* When scrolling faster than this, we skip loading data since the user
|
|
20
|
+
* is likely scrolling quickly past content they don't want to see.
|
|
21
|
+
*/
|
|
22
|
+
export declare const LOAD_VELOCITY_THRESHOLD = 12;
|
|
23
|
+
/**
|
|
24
|
+
* Velocity threshold for preloading (px/ms)
|
|
25
|
+
* When scrolling faster than this but slower than LOAD_VELOCITY_THRESHOLD,
|
|
26
|
+
* we preload extra items in the scroll direction to reduce placeholder flashing.
|
|
27
|
+
*/
|
|
28
|
+
export declare const PRELOAD_VELOCITY_THRESHOLD = 2;
|
|
29
|
+
/**
|
|
30
|
+
* Maximum virtual size in pixels along the main axis
|
|
31
|
+
* Most browsers support ~16.7M pixels, we use 16M for safety margin
|
|
32
|
+
*/
|
|
33
|
+
export declare const MAX_VIRTUAL_SIZE = 16000000;
|
|
34
|
+
/** Idle timeout for scroll detection (ms) */
|
|
35
|
+
export declare const SCROLL_IDLE_TIMEOUT = 150;
|
|
36
|
+
/** Default wheel sensitivity multiplier */
|
|
37
|
+
export declare const WHEEL_SENSITIVITY = 1;
|
|
38
|
+
/** Default auto-hide behavior */
|
|
39
|
+
export declare const SCROLLBAR_AUTO_HIDE = true;
|
|
40
|
+
/** Default auto-hide delay in milliseconds */
|
|
41
|
+
export declare const SCROLLBAR_AUTO_HIDE_DELAY = 1000;
|
|
42
|
+
/** Default minimum thumb size in pixels */
|
|
43
|
+
export declare const SCROLLBAR_MIN_THUMB_SIZE = 30;
|
|
44
|
+
/** Default chunk size for sparse storage */
|
|
45
|
+
export declare const CHUNK_SIZE = 100;
|
|
46
|
+
/** Default maximum cached items before eviction */
|
|
47
|
+
export declare const MAX_CACHED_ITEMS = 10000;
|
|
48
|
+
/** Buffer for eviction (keep extra items around visible range) */
|
|
49
|
+
export declare const EVICTION_BUFFER = 500;
|
|
50
|
+
/** Default character used for masking text in placeholders */
|
|
51
|
+
export declare const MASK_CHARACTER = "x";
|
|
52
|
+
/** Maximum items to sample for placeholder structure analysis */
|
|
53
|
+
export declare const MAX_SAMPLE_SIZE = 20;
|
|
54
|
+
/** Internal flag to identify placeholder items */
|
|
55
|
+
export declare const PLACEHOLDER_FLAG = "_isPlaceholder";
|
|
56
|
+
/** Prefix for placeholder item IDs */
|
|
57
|
+
export declare const PLACEHOLDER_ID_PREFIX = "__placeholder_";
|
|
58
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist - Event Emitter
|
|
3
|
+
* Lightweight, type-safe event system
|
|
4
|
+
*/
|
|
5
|
+
import type { EventHandler, Unsubscribe, EventMap } from "../types";
|
|
6
|
+
/**
|
|
7
|
+
* Create a type-safe event emitter
|
|
8
|
+
* Functional approach - returns an object with methods
|
|
9
|
+
*/
|
|
10
|
+
export declare const createEmitter: <T extends EventMap>() => {
|
|
11
|
+
on: <K extends keyof T>(event: K, handler: EventHandler<T[K]>) => Unsubscribe;
|
|
12
|
+
off: <K extends keyof T>(event: K, handler: EventHandler<T[K]>) => void;
|
|
13
|
+
emit: <K extends keyof T>(event: K, payload: T[K]) => void;
|
|
14
|
+
clear: <K extends keyof T>(event?: K) => void;
|
|
15
|
+
};
|
|
16
|
+
/** Event emitter type */
|
|
17
|
+
export type Emitter<T extends EventMap> = ReturnType<typeof createEmitter<T>>;
|
|
18
|
+
//# sourceMappingURL=emitter.d.ts.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist/data - Builder Feature
|
|
3
|
+
* Wraps async data loading with sparse storage into a VListFeature for the composable builder.
|
|
4
|
+
*
|
|
5
|
+
* Priority: 20 (runs before scrollbar and selection, after grid/groups)
|
|
6
|
+
*
|
|
7
|
+
* What it wires:
|
|
8
|
+
* - Replaces data manager — swaps the simple in-memory store with sparse storage
|
|
9
|
+
* - Scroll boundary detection — triggers loadMore() near the bottom (or top in reverse)
|
|
10
|
+
* - Velocity-aware loading — skips data fetching during fast scrolling, loads on idle
|
|
11
|
+
* - Placeholder generation — creates skeleton items for unloaded ranges
|
|
12
|
+
* - Request deduplication — prevents duplicate fetches for the same range
|
|
13
|
+
* - Idle handler — loads any pending ranges when scrolling stops
|
|
14
|
+
* - Snapshot-aware reload — reload({ snapshot }) skips page-1 and auto-restores position
|
|
15
|
+
*
|
|
16
|
+
* Added methods: reload, loadVisibleRange
|
|
17
|
+
* Added events: load:start, load:end, error
|
|
18
|
+
*/
|
|
19
|
+
import type { VListItem, VListAdapter } from "../../types";
|
|
20
|
+
import type { VListFeature } from "../../builder/types";
|
|
21
|
+
/** Data feature configuration */
|
|
22
|
+
export interface DataFeatureConfig<T extends VListItem = VListItem> {
|
|
23
|
+
/** Async data source (required) */
|
|
24
|
+
adapter: VListAdapter<T>;
|
|
25
|
+
/** Total number of items (optional - if not provided, adapter must return it) */
|
|
26
|
+
total?: number;
|
|
27
|
+
/** Whether to automatically load initial data. Default: true */
|
|
28
|
+
autoLoad?: boolean;
|
|
29
|
+
/** Storage configuration */
|
|
30
|
+
storage?: {
|
|
31
|
+
/** Number of items per chunk. Default: 100 */
|
|
32
|
+
chunkSize?: number;
|
|
33
|
+
/** Maximum cached items before eviction. Default: 10000 */
|
|
34
|
+
maxCachedItems?: number;
|
|
35
|
+
};
|
|
36
|
+
/** Loading behavior configuration */
|
|
37
|
+
loading?: {
|
|
38
|
+
/** Velocity threshold above which data loading is skipped (px/ms). Default: 5 */
|
|
39
|
+
cancelThreshold?: number;
|
|
40
|
+
/** Velocity threshold for preloading (px/ms). Default: 2 */
|
|
41
|
+
preloadThreshold?: number;
|
|
42
|
+
/** Number of items to preload in scroll direction. Default: 50 */
|
|
43
|
+
preloadAhead?: number;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Create a data feature for the builder.
|
|
48
|
+
*
|
|
49
|
+
* Adds async data loading with sparse storage, placeholders, and infinite scroll.
|
|
50
|
+
*
|
|
51
|
+
* ```ts
|
|
52
|
+
* import { vlist } from 'vlist/builder'
|
|
53
|
+
* import { withData } from 'vlist/data'
|
|
54
|
+
*
|
|
55
|
+
* const list = vlist({
|
|
56
|
+
* container: '#app',
|
|
57
|
+
* item: { height: 48, template: renderItem },
|
|
58
|
+
* })
|
|
59
|
+
* .use(withData({
|
|
60
|
+
* adapter: {
|
|
61
|
+
* read: async ({ offset, limit }) => {
|
|
62
|
+
* const res = await fetch(`/api/items?offset=${offset}&limit=${limit}`)
|
|
63
|
+
* const data = await res.json()
|
|
64
|
+
* return { items: data.items, total: data.total, hasMore: data.hasMore }
|
|
65
|
+
* }
|
|
66
|
+
* }
|
|
67
|
+
* }))
|
|
68
|
+
* .build()
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export declare const withAsync: <T extends VListItem = VListItem>(config: DataFeatureConfig<T>) => VListFeature<T>;
|
|
72
|
+
//# sourceMappingURL=feature.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist - Data Domain
|
|
3
|
+
* Data management, sparse storage, and placeholder generation
|
|
4
|
+
*/
|
|
5
|
+
export { withAsync, type DataFeatureConfig } from "./feature";
|
|
6
|
+
export { createDataManager, mergeRanges, calculateMissingRanges, isPlaceholderItem, filterPlaceholders, countRealItems, type DataManager, type DataManagerConfig, type DataState, } from "./manager";
|
|
7
|
+
export { createSparseStorage, type SparseStorage, type SparseStorageConfig, type SparseStorageStats, } from "./sparse";
|
|
8
|
+
export { createPlaceholderManager, type PlaceholderManager, type PlaceholderConfig, } from "./placeholder";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|