vlist 2.3.0 → 2.4.1
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.github.md +13 -14
- package/README.md +12 -11
- package/dist/constants.d.ts +28 -0
- package/dist/core/adapter.d.ts +92 -0
- package/dist/core/pipeline.d.ts +2 -0
- package/dist/core/runway.d.ts +82 -0
- package/dist/core/state.d.ts +6 -2
- package/dist/core/types.d.ts +31 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +1 -1
- package/dist/internals.d.ts +2 -2
- package/dist/internals.js +1 -1
- package/dist/plugins/carousel/engine.d.ts +56 -0
- package/dist/plugins/carousel/index.d.ts +7 -0
- package/dist/plugins/carousel/plugin.d.ts +44 -0
- package/dist/plugins/carousel/presets.d.ts +18 -0
- package/dist/plugins/grid/renderer.d.ts +1 -4
- package/dist/plugins/groups/sticky.d.ts +1 -1
- package/dist/plugins/masonry/renderer.d.ts +1 -1
- package/dist/plugins/scale/index.d.ts +0 -3
- package/dist/plugins/scale/plugin.d.ts +1 -18
- package/dist/plugins/scrollbar/index.d.ts +0 -1
- package/dist/plugins/table/renderer.d.ts +2 -5
- package/dist/rendering/index.d.ts +1 -6
- package/dist/size.json +1 -1
- package/dist/types.d.ts +2 -9
- package/package.json +1 -1
- package/dist/plugins/scrollbar/controller.d.ts +0 -121
- package/dist/rendering/measured.d.ts +0 -52
- package/dist/rendering/renderer.d.ts +0 -113
- package/dist/rendering/scale.d.ts +0 -121
- package/dist/rendering/scroll.d.ts +0 -23
- package/dist/rendering/viewport.d.ts +0 -139
package/README.github.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# vlist
|
|
2
2
|
|
|
3
|
-
The virtual list library for every framework. Ultra efficient, batteries-included, and accessible with composable plugins — in
|
|
3
|
+
The virtual list library for every framework. Ultra efficient, batteries-included, and accessible with composable plugins — in 9.7 KB.
|
|
4
4
|
|
|
5
|
-
**v2.
|
|
5
|
+
**v2.4.1** — [Changelog](./CHANGELOG.md) · **New:** Bounded logical scroll model for 1M+ items (`scroll: { mode: "bounded" }`), `carousel()` plugin with infinite loop, snap, and focal scaling. `scale()` deprecated.
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/vlist)
|
|
8
8
|
[](https://bundlephobia.com/package/vlist)
|
|
@@ -11,9 +11,9 @@ The virtual list library for every framework. Ultra efficient, batteries-include
|
|
|
11
11
|
|
|
12
12
|
- **Accessible** — WAI-ARIA, 2D keyboard navigation, focus recovery, screen-reader DOM ordering
|
|
13
13
|
- **Zero dependencies** — framework-agnostic core with tiny adapters for Vue, Svelte, Solid, React
|
|
14
|
-
- **
|
|
14
|
+
- **9.7 KB gzipped** — composable plugins with perfect tree-shaking
|
|
15
15
|
- **Constant memory** — ~0.1 MB overhead at any scale, from 10K to 1M+ items
|
|
16
|
-
- **Tree, grid, masonry, table, groups, data, selection, search, sortable, transition
|
|
16
|
+
- **Tree, grid, masonry, carousel, table, groups, data, selection, search, sortable, transition** — all opt-in
|
|
17
17
|
- **Axis-neutral** — vertical and horizontal scrolling through a single code path, all plugins work in both orientations
|
|
18
18
|
|
|
19
19
|
**18 interactive examples, docs & benchmarks → [vlist.io](https://vlist.io)**
|
|
@@ -94,21 +94,21 @@ const list = createVList({
|
|
|
94
94
|
|
|
95
95
|
| Plugin | Size | Description |
|
|
96
96
|
|--------|------|-------------|
|
|
97
|
-
| **Base** |
|
|
97
|
+
| **Base** | 9.7 KB | Virtualization, ARIA, keyboard nav, gap, padding, bounded scroll (1M+ items) |
|
|
98
98
|
| `data()` | +4.8 KB | Lazy loading with velocity-aware fetching |
|
|
99
|
-
| `selection()` | +2.
|
|
99
|
+
| `selection()` | +2.7 KB | Single/multiple selection with 2D keyboard nav |
|
|
100
100
|
| `search()` | +3.2 KB | Search bar: filter/navigate modes, match highlighting |
|
|
101
|
-
| `scale()` | +4.2 KB | 1M+ items via scroll compression |
|
|
102
101
|
| `groups()` | +5.3 KB | Sticky/inline headers with grid + masonry + table + data integration |
|
|
103
102
|
| `autosize()` | +0.8 KB | Auto-measure items via ResizeObserver |
|
|
104
103
|
| `scrollbar()` | +2.0 KB | Custom scrollbar UI |
|
|
105
|
-
| `grid()` | +
|
|
104
|
+
| `grid()` | +2.4 KB | 2D grid layout |
|
|
106
105
|
| `masonry()` | +4.0 KB | Pinterest-style masonry with lane-aware keyboard nav |
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
106
|
+
| `carousel()` | +2.3 KB | Paged horizontal carousel with snap and keyboard nav |
|
|
107
|
+
| `table()` | +5.8 KB | Data table with columns, resize, sort |
|
|
108
|
+
| `tree()` | +5.0 KB | Collapsible tree with async loading and indent guides |
|
|
109
|
+
| `page()` | +0.8 KB | Window-level scrolling |
|
|
110
|
+
| `sortable()` | +3.0 KB | Drag-and-drop reordering with auto-scroll |
|
|
111
|
+
| `snapshots()` | +1.1 KB | Scroll position save/restore |
|
|
112
112
|
| `transition()` | +1.8 KB | FLIP-based enter/exit animations for insert & remove |
|
|
113
113
|
|
|
114
114
|
## Examples
|
|
@@ -324,7 +324,6 @@ selection({ mode: 'single' | 'multiple', initial?: [...ids] })
|
|
|
324
324
|
data({ adapter: { read }, loading?: { cancelThreshold? } })
|
|
325
325
|
table({ columns, rowHeight, headerHeight?, resizable? })
|
|
326
326
|
autosize() // auto-measure items (requires estimatedHeight)
|
|
327
|
-
scale() // auto-activates at 16.7M px
|
|
328
327
|
scrollbar({ autoHide?, autoHideDelay?, minThumbSize? })
|
|
329
328
|
transition({ duration?: 200, insert?: timing, remove?: timing })
|
|
330
329
|
sortable({ handle?: '.drag-handle' }) // drag-and-drop reordering
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# vlist
|
|
2
2
|
|
|
3
|
-
The virtual list library for every framework. Ultra efficient, batteries-included, and accessible with composable plugins — in
|
|
3
|
+
The virtual list library for every framework. Ultra efficient, batteries-included, and accessible with composable plugins — in 9.7 KB.
|
|
4
4
|
|
|
5
|
-
**v2.
|
|
5
|
+
**v2.4.1** — [Changelog](https://github.com/floor/vlist/blob/main/CHANGELOG.md) · **New:** Bounded logical scroll model for 1M+ items (`scroll: { mode: "bounded" }`), `carousel()` plugin with infinite loop, snap, and focal scaling. `scale()` deprecated.
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/vlist)
|
|
8
8
|
[](https://bundlephobia.com/package/vlist)
|
|
@@ -11,7 +11,7 @@ The virtual list library for every framework. Ultra efficient, batteries-include
|
|
|
11
11
|
|
|
12
12
|
- **Accessible** — WAI-ARIA, 2D keyboard navigation, focus recovery, screen-reader DOM ordering
|
|
13
13
|
- **Zero dependencies** — framework-agnostic core, tiny adapters for Vue, Svelte, Solid, React
|
|
14
|
-
- **
|
|
14
|
+
- **9.7 KB gzipped** — composable plugins with perfect tree-shaking
|
|
15
15
|
- **Constant memory** — ~0.1 MB overhead at any scale, from 10K to 1M+ items
|
|
16
16
|
- **Axis-neutral** — vertical and horizontal scrolling through a single code path, all plugins work in both orientations
|
|
17
17
|
|
|
@@ -56,20 +56,21 @@ const list = createVList({ container: '#app', items, item: { height: 200, templa
|
|
|
56
56
|
|
|
57
57
|
| Plugin | Size | Description |
|
|
58
58
|
|--------|------|-------------|
|
|
59
|
-
| **Base** |
|
|
59
|
+
| **Base** | 9.7 KB | Virtualization, ARIA, keyboard nav, gap, padding, bounded scroll (1M+ items) |
|
|
60
60
|
| `data()` | +4.8 KB | Lazy loading with velocity-aware fetching |
|
|
61
|
-
| `selection()` | +2.
|
|
61
|
+
| `selection()` | +2.7 KB | Single/multiple selection with 2D keyboard nav |
|
|
62
62
|
| `search()` | +3.2 KB | Search bar: filter/navigate modes, match highlighting |
|
|
63
|
-
| `scale()` | +4.2 KB | 1M+ items via scroll compression |
|
|
64
63
|
| `groups()` | +5.3 KB | Sticky/inline headers with grid + masonry + table + data integration |
|
|
65
64
|
| `autosize()` | +0.8 KB | Auto-measure items via ResizeObserver |
|
|
66
65
|
| `scrollbar()` | +2.0 KB | Custom scrollbar UI |
|
|
67
|
-
| `grid()` | +
|
|
66
|
+
| `grid()` | +2.4 KB | 2D grid layout |
|
|
68
67
|
| `masonry()` | +4.0 KB | Pinterest-style masonry with lane-aware keyboard nav |
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
68
|
+
| `carousel()` | +2.3 KB | Paged horizontal carousel with snap and keyboard nav |
|
|
69
|
+
| `table()` | +5.8 KB | Data table with columns, resize, sort |
|
|
70
|
+
| `tree()` | +5.0 KB | Collapsible tree with async loading and indent guides |
|
|
71
|
+
| `page()` | +0.8 KB | Window-level scrolling |
|
|
72
|
+
| `sortable()` | +3.0 KB | Drag-and-drop reordering with auto-scroll |
|
|
73
|
+
| `snapshots()` | +1.1 KB | Scroll position save/restore |
|
|
73
74
|
| `transition()` | +1.8 KB | FLIP-based enter/exit animations for insert & remove |
|
|
74
75
|
|
|
75
76
|
## Framework Adapters
|
package/dist/constants.d.ts
CHANGED
|
@@ -45,6 +45,34 @@ export declare const WHEEL_SENSITIVITY = 1;
|
|
|
45
45
|
export declare const SCROLL_EASING: (t: number) => number;
|
|
46
46
|
/** Default smooth scroll duration (ms) */
|
|
47
47
|
export declare const SCROLL_DURATION = 300;
|
|
48
|
+
/**
|
|
49
|
+
* Runway size as a multiple of the viewport. The bounded content element is
|
|
50
|
+
* sized to `containerSize × this` (capped at total size), giving the native
|
|
51
|
+
* scrollbar room to move before a rebase shifts the logical origin.
|
|
52
|
+
*/
|
|
53
|
+
export declare const BOUNDED_RUNWAY_FACTOR = 2;
|
|
54
|
+
/**
|
|
55
|
+
* Minimum runway multiple. The runway must exceed the viewport so native scroll
|
|
56
|
+
* and touch/trackpad momentum have room before a rebase; below this there would
|
|
57
|
+
* be little-to-no native scrollable range. User-supplied `scroll.runway` values
|
|
58
|
+
* are clamped up to this floor.
|
|
59
|
+
*
|
|
60
|
+
* 1.5 keeps a half-viewport of native scroll travel — enough for the native
|
|
61
|
+
* scrollbar and most touch gestures. The wheel/trackpad path is already fully
|
|
62
|
+
* synthetic (driven in logical space), so it is unaffected by the runway size;
|
|
63
|
+
* a small runway only risks interrupting native touch momentum at the edges.
|
|
64
|
+
*/
|
|
65
|
+
export declare const BOUNDED_RUNWAY_MIN = 1.5;
|
|
66
|
+
/**
|
|
67
|
+
* Lower rebase trigger: when native scrollTop drops below this fraction of the
|
|
68
|
+
* runway (and we are not already at the logical start), shift the origin back.
|
|
69
|
+
*/
|
|
70
|
+
export declare const BOUNDED_REBASE_LOW = 0.25;
|
|
71
|
+
/**
|
|
72
|
+
* Upper rebase trigger: when native scrollTop rises above this fraction of the
|
|
73
|
+
* runway (and we are not already at the logical end), shift the origin forward.
|
|
74
|
+
*/
|
|
75
|
+
export declare const BOUNDED_REBASE_HIGH = 0.75;
|
|
48
76
|
/** Default auto-hide behavior */
|
|
49
77
|
export declare const SCROLLBAR_AUTO_HIDE = true;
|
|
50
78
|
/** Default auto-hide delay in milliseconds */
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist — Logical Scroll Model (RFC-012)
|
|
3
|
+
*
|
|
4
|
+
* The scroll coordinate is logical, not a physical pixel offset into a giant
|
|
5
|
+
* container: `{ index, offsetPx }` — the top visible item plus the pixel offset
|
|
6
|
+
* within it. This is stable under variable and autosized items, because the
|
|
7
|
+
* pixel offset stays physically meaningful when an item's measured size changes
|
|
8
|
+
* (unlike a fraction, which silently shifts the visual position).
|
|
9
|
+
*
|
|
10
|
+
* The pixel equivalent (`index * itemSize + offsetPx` generalised over the size
|
|
11
|
+
* cache) is derived only at the public API boundary, so existing consumers of
|
|
12
|
+
* `getScrollPosition()` and scroll events keep seeing pixel values (RFC-012 G4).
|
|
13
|
+
*
|
|
14
|
+
* This module is pure: it owns no DOM and no engine state. It is the translation
|
|
15
|
+
* layer the `ScrollAdapter` and the render pipeline build on.
|
|
16
|
+
*/
|
|
17
|
+
import type { SizeCache } from "../rendering/sizes";
|
|
18
|
+
/**
|
|
19
|
+
* Logical scroll position: the top visible data item plus the pixel offset
|
|
20
|
+
* scrolled past its leading edge.
|
|
21
|
+
*
|
|
22
|
+
* Invariant after {@link normalizeLogical}: `0 <= offsetPx < size(index)`
|
|
23
|
+
* (except at the list end, where `offsetPx` may exceed the final item to
|
|
24
|
+
* represent overscroll clamping).
|
|
25
|
+
*/
|
|
26
|
+
export interface LogicalScrollPosition {
|
|
27
|
+
/** Index of the top visible item (integer, clamped to `[0, total - 1]`). */
|
|
28
|
+
index: number;
|
|
29
|
+
/** Pixel offset scrolled past the leading edge of `index`. */
|
|
30
|
+
offsetPx: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The migration boundary between the logical scroll model and the rest of the
|
|
34
|
+
* engine and its plugins (RFC-012). Plugins read and write scroll position
|
|
35
|
+
* through this adapter rather than touching `engineState.scrollPosition` or raw
|
|
36
|
+
* DOM `scrollTop` / `scrollLeft` directly.
|
|
37
|
+
*
|
|
38
|
+
* It exposes both the logical primitive and a pixel-equivalent view so existing
|
|
39
|
+
* pixel-based code paths keep working during migration.
|
|
40
|
+
*/
|
|
41
|
+
export interface ScrollAdapter {
|
|
42
|
+
/** Current logical position, normalized. */
|
|
43
|
+
getLogical(): LogicalScrollPosition;
|
|
44
|
+
/** Move to a logical position. Out-of-range input is clamped. */
|
|
45
|
+
setLogical(pos: LogicalScrollPosition): void;
|
|
46
|
+
/** Pixel-equivalent of the current position (for public API / events). */
|
|
47
|
+
getPixelEquivalent(): number;
|
|
48
|
+
/** Move to a pixel-equivalent position. Out-of-range input is clamped. */
|
|
49
|
+
setPixelEquivalent(px: number): void;
|
|
50
|
+
/** Maximum scrollable pixel-equivalent (`totalSize - containerSize`, >= 0). */
|
|
51
|
+
getMaxPixelEquivalent(): number;
|
|
52
|
+
/** Advance the position by a pixel delta (used by native/wheel input). */
|
|
53
|
+
scrollByPx(delta: number): void;
|
|
54
|
+
}
|
|
55
|
+
/** Dependencies an adapter needs to translate and apply scroll positions. */
|
|
56
|
+
export interface ScrollAdapterConfig {
|
|
57
|
+
/** Source of item offsets and sizes. */
|
|
58
|
+
readonly sizeCache: SizeCache;
|
|
59
|
+
/** Read the current pixel-equivalent position from the active scroll source. */
|
|
60
|
+
readonly getPixel: () => number;
|
|
61
|
+
/** Write a (clamped) pixel-equivalent position to the active scroll source. */
|
|
62
|
+
readonly setPixel: (px: number) => void;
|
|
63
|
+
/** Current viewport size along the main axis. */
|
|
64
|
+
readonly getContainerSize: () => number;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Convert a pixel offset into a logical position. The returned position is
|
|
68
|
+
* normalized: `index` is clamped to `[0, total - 1]` and `offsetPx` is the
|
|
69
|
+
* remainder past that item's leading edge.
|
|
70
|
+
*/
|
|
71
|
+
export declare function pixelToLogical(sizeCache: SizeCache, pixel: number): LogicalScrollPosition;
|
|
72
|
+
/**
|
|
73
|
+
* Convert a logical position into a pixel offset. Input is normalized first,
|
|
74
|
+
* so callers may pass an `offsetPx` that overflows its item.
|
|
75
|
+
*/
|
|
76
|
+
export declare function logicalToPixel(sizeCache: SizeCache, pos: LogicalScrollPosition): number;
|
|
77
|
+
/**
|
|
78
|
+
* Canonicalize a logical position so that `0 <= offsetPx < size(index)` where
|
|
79
|
+
* possible, carrying overflow/underflow across item boundaries. `index` is
|
|
80
|
+
* clamped to `[0, total - 1]`. At the list end, any remaining `offsetPx` is
|
|
81
|
+
* preserved (it represents scrolling past the start of the final item).
|
|
82
|
+
*/
|
|
83
|
+
export declare function normalizeLogical(sizeCache: SizeCache, pos: LogicalScrollPosition): LogicalScrollPosition;
|
|
84
|
+
/**
|
|
85
|
+
* Create a {@link ScrollAdapter} over an existing pixel-based scroll source.
|
|
86
|
+
*
|
|
87
|
+
* During migration the underlying source is still the native viewport scroll;
|
|
88
|
+
* later phases can swap `getPixel` / `setPixel` for a bounded-proxy or
|
|
89
|
+
* interception input strategy without changing any plugin that uses the adapter.
|
|
90
|
+
*/
|
|
91
|
+
export declare function createScrollAdapter(config: ScrollAdapterConfig): ScrollAdapter;
|
|
92
|
+
//# sourceMappingURL=adapter.d.ts.map
|
package/dist/core/pipeline.d.ts
CHANGED
|
@@ -33,6 +33,8 @@ export interface RenderConfig {
|
|
|
33
33
|
readonly crossEndVal: string;
|
|
34
34
|
readonly oddClass: string;
|
|
35
35
|
readonly emitter: Emitter<VListEvents> | null;
|
|
36
|
+
indexMap: ((renderIndex: number) => number) | null;
|
|
37
|
+
ariaTotalFn: (() => number) | null;
|
|
36
38
|
}
|
|
37
39
|
export declare function createRenderConfig(classPrefix: string, isX: boolean, startPadding: number, crossPadStart: number, crossPadEnd: number, oddClass: string, gap: number, emitter?: Emitter<VListEvents> | null): RenderConfig;
|
|
38
40
|
/**
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist — Bounded Logical Scroll Handler (RFC-012)
|
|
3
|
+
*
|
|
4
|
+
* An alternative to the native scroll handler ({@link createScrollHandler}) that
|
|
5
|
+
* decouples the native scrollable size from the virtual content size.
|
|
6
|
+
*
|
|
7
|
+
* The content element is sized to a bounded *runway* (a multiple of the
|
|
8
|
+
* viewport, capped at the real virtual size) instead of `totalItems × itemSize`.
|
|
9
|
+
* This sidesteps the browser's ~16.7M px element-size limit without the scale
|
|
10
|
+
* plugin's compression — no compression ratio leaks into offsets or hit-testing.
|
|
11
|
+
*
|
|
12
|
+
* Coordinates:
|
|
13
|
+
* logical = baseOffset + scrollTop (absolute virtual pixel position)
|
|
14
|
+
*
|
|
15
|
+
* - `state.scrollPosition` always holds the *logical* (absolute) position, so
|
|
16
|
+
* the render pipeline's `indexAtOffset(scrollPosition)` keeps working over
|
|
17
|
+
* the full size cache, and the public pixel-equivalent surface is unchanged.
|
|
18
|
+
* - `state.baseOffset` is the virtual offset that maps to native scrollTop=0.
|
|
19
|
+
* The pipeline renders each item at `getOffset(index) - baseOffset`, so items
|
|
20
|
+
* land inside the bounded runway. baseOffset is 0 for small lists (native).
|
|
21
|
+
*
|
|
22
|
+
* Rebasing: as native scrollTop approaches a runway edge we shift baseOffset and
|
|
23
|
+
* scrollTop together by the same delta, preserving `logical`. The synthetic
|
|
24
|
+
* scroll event this triggers reads the same logical position and is dropped by
|
|
25
|
+
* the "logical unchanged" guard — no suppress flag needed. The repositioning of
|
|
26
|
+
* items (their transforms depend on baseOffset) is committed by the explicit
|
|
27
|
+
* render that the rebasing scroll frame performs.
|
|
28
|
+
*/
|
|
29
|
+
import type { EngineState } from "./state";
|
|
30
|
+
import type { ScrollHandler } from "./scroll";
|
|
31
|
+
export interface BoundedScrollHandler extends ScrollHandler {
|
|
32
|
+
/** Move to an absolute logical (virtual pixel) position; renders + schedules idle. */
|
|
33
|
+
setLogical(logicalPx: number): void;
|
|
34
|
+
/** Current absolute logical position (== state.scrollPosition). */
|
|
35
|
+
getLogical(): number;
|
|
36
|
+
/** Maximum scrollable logical position (`virtualTotal - containerSize`, >= 0). */
|
|
37
|
+
getMaxLogical(): number;
|
|
38
|
+
/** Recompute runway/derived values and resize the content element. */
|
|
39
|
+
refresh(totalSize: number): void;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Infinite-loop (wrap) configuration for the bounded handler (carousel, RFC-011).
|
|
43
|
+
*
|
|
44
|
+
* In wrap mode the logical position is never clamped to a maximum: instead it is
|
|
45
|
+
* periodically folded back toward {@link home} by whole laps once it drifts more
|
|
46
|
+
* than {@link thresholdLaps} laps away. Because the consumer maps virtual indices
|
|
47
|
+
* to real items via `index % realTotal`, shifting the logical position (and
|
|
48
|
+
* baseOffset) by a whole lap leaves the rendered items and their on-screen
|
|
49
|
+
* positions unchanged — the loop is seamless. The content element is sized to the
|
|
50
|
+
* runway exactly as in non-wrap mode, so large item counts never blow the
|
|
51
|
+
* browser's element-size limit.
|
|
52
|
+
*/
|
|
53
|
+
export interface WrapConfig {
|
|
54
|
+
/** Current lap period in virtual px (`realTotal × stepSize`). */
|
|
55
|
+
readonly lapSize: () => number;
|
|
56
|
+
/** Logical position to fold back toward (e.g. the middle cycle). */
|
|
57
|
+
readonly home: () => number;
|
|
58
|
+
/** Fold the logical position back toward `home` once it drifts this many laps away. */
|
|
59
|
+
readonly thresholdLaps: number;
|
|
60
|
+
}
|
|
61
|
+
export interface BoundedScrollConfig {
|
|
62
|
+
readonly state: EngineState;
|
|
63
|
+
readonly viewport: HTMLElement;
|
|
64
|
+
readonly content: HTMLElement;
|
|
65
|
+
readonly isX: boolean;
|
|
66
|
+
readonly wheelEnabled: boolean;
|
|
67
|
+
readonly idleTimeout: number;
|
|
68
|
+
readonly scrollTarget?: EventTarget;
|
|
69
|
+
/** Main-axis padding folded into the virtual total (matches native content sizing). */
|
|
70
|
+
readonly mainAxisPadding: number;
|
|
71
|
+
/** Runway size as a multiple of the viewport (defaults to BOUNDED_RUNWAY_FACTOR). */
|
|
72
|
+
readonly runwayFactor?: number;
|
|
73
|
+
/** Infinite-loop config (carousel). When set, the logical position wraps by
|
|
74
|
+
* whole laps toward `home` instead of clamping to a maximum. */
|
|
75
|
+
readonly wrap?: WrapConfig;
|
|
76
|
+
/** Called synchronously per frame — triggers the 2-phase pipeline. */
|
|
77
|
+
readonly onFrame: () => void;
|
|
78
|
+
/** Called when scrolling becomes idle. */
|
|
79
|
+
readonly onIdle: () => void;
|
|
80
|
+
}
|
|
81
|
+
export declare function createBoundedScrollHandler(config: BoundedScrollConfig): BoundedScrollHandler;
|
|
82
|
+
//# sourceMappingURL=runway.d.ts.map
|
package/dist/core/state.d.ts
CHANGED
|
@@ -24,6 +24,12 @@ export interface EngineState {
|
|
|
24
24
|
prevScrollPosition: number;
|
|
25
25
|
/** 1 = forward, -1 = backward, 0 = idle */
|
|
26
26
|
scrollDirection: number;
|
|
27
|
+
/**
|
|
28
|
+
* Bounded logical-scroll origin (RFC-012). The virtual offset that maps to
|
|
29
|
+
* native scrollTop=0. Items render at `getOffset(index) - baseOffset`.
|
|
30
|
+
* Defaults to 0, which makes native mode byte-identical (`offset - 0`).
|
|
31
|
+
*/
|
|
32
|
+
baseOffset: number;
|
|
27
33
|
containerSize: number;
|
|
28
34
|
crossSize: number;
|
|
29
35
|
totalItems: number;
|
|
@@ -32,8 +38,6 @@ export interface EngineState {
|
|
|
32
38
|
renderPending: boolean;
|
|
33
39
|
initialized: boolean;
|
|
34
40
|
destroyed: boolean;
|
|
35
|
-
isCompressed: boolean;
|
|
36
|
-
compressionRatio: number;
|
|
37
41
|
prevAriaTotal: number;
|
|
38
42
|
/**
|
|
39
43
|
* Resize buffers when container changes. Cold path only.
|
package/dist/core/types.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { VListItem, ItemTemplate, ItemState } from "../types";
|
|
8
8
|
import type { SizeCache } from "./sizes";
|
|
9
|
+
import type { ScrollAdapter } from "./adapter";
|
|
9
10
|
import type { EngineState } from "./state";
|
|
10
11
|
import type { Emitter } from "../events";
|
|
11
12
|
/** Phase 1 hook — mutates EngineState in place, zero allocation */
|
|
@@ -60,6 +61,13 @@ export interface ElementPool {
|
|
|
60
61
|
export interface PluginContext<T extends VListItem = VListItem> {
|
|
61
62
|
readonly dom: DOMStructure;
|
|
62
63
|
readonly sizeCache: SizeCache;
|
|
64
|
+
/**
|
|
65
|
+
* Logical scroll model boundary (RFC-012). Plugins read and write scroll
|
|
66
|
+
* position through this adapter rather than touching `getState().scrollPosition`
|
|
67
|
+
* or raw `scrollTop`/`scrollLeft`. Its pixel-equivalent view keeps existing
|
|
68
|
+
* pixel-based code paths working during the migration.
|
|
69
|
+
*/
|
|
70
|
+
readonly scroll: ScrollAdapter;
|
|
63
71
|
readonly pool: ElementPool;
|
|
64
72
|
readonly config: ResolvedConfig;
|
|
65
73
|
readonly emitter: Emitter<import("../types").VListEvents<T>>;
|
|
@@ -72,7 +80,10 @@ export interface PluginContext<T extends VListItem = VListItem> {
|
|
|
72
80
|
enableListboxRole(): void;
|
|
73
81
|
setSizeConfig(config: number | ((index: number) => number)): void;
|
|
74
82
|
setScrollFns(get: () => number, set: (pos: number) => void): void;
|
|
83
|
+
/** Request the bounded scroll handler in infinite-loop (wrap) mode (carousel). */
|
|
84
|
+
setBoundedWrap(config: import("./runway").WrapConfig): void;
|
|
75
85
|
setVirtualTotalFn(fn: () => number): void;
|
|
86
|
+
setIndexMapFn(fn: (renderIndex: number) => number): void;
|
|
76
87
|
getItems(): readonly T[];
|
|
77
88
|
getItem(index: number): T | undefined;
|
|
78
89
|
getState(): EngineState;
|
|
@@ -87,6 +98,8 @@ export interface PluginContext<T extends VListItem = VListItem> {
|
|
|
87
98
|
readonly rawSizeSpec: number | ((index: number, ...args: unknown[]) => number);
|
|
88
99
|
scrollTo(position: number): void;
|
|
89
100
|
smoothScrollTo(target: number | (() => number), duration: number, easing?: (t: number) => number, onComplete?: () => void): void;
|
|
101
|
+
/** Cancel any in-flight smooth-scroll animation on the active handler. */
|
|
102
|
+
cancelScroll(): void;
|
|
90
103
|
disableDefaultScroll(): void;
|
|
91
104
|
disableDefaultResize(): void;
|
|
92
105
|
setScrollTarget(target: EventTarget): void;
|
|
@@ -113,6 +126,7 @@ export interface PluginContext<T extends VListItem = VListItem> {
|
|
|
113
126
|
lr: number;
|
|
114
127
|
scrollIndex: ((itemIndex: number) => number) | null;
|
|
115
128
|
navigate: ((currentIndex: number, key: string, total: number) => number) | null;
|
|
129
|
+
total: (() => number) | null;
|
|
116
130
|
};
|
|
117
131
|
}
|
|
118
132
|
export interface VListPlugin<T extends VListItem = VListItem> {
|
|
@@ -180,6 +194,23 @@ export interface CreateVListConfig<T extends VListItem = VListItem> {
|
|
|
180
194
|
scrollbar?: "none";
|
|
181
195
|
gutter?: "auto" | "stable";
|
|
182
196
|
idleTimeout?: number;
|
|
197
|
+
/**
|
|
198
|
+
* Scroll model (RFC-012). `"native"` (default) sizes the content element to
|
|
199
|
+
* the full virtual size — simple, but hits the browser's ~16.7M px limit.
|
|
200
|
+
* `"bounded"` sizes the content to a viewport-multiple runway and rebases a
|
|
201
|
+
* logical origin near the edges, supporting unbounded item counts without
|
|
202
|
+
* compressing the scroll space.
|
|
203
|
+
*/
|
|
204
|
+
mode?: "native" | "bounded";
|
|
205
|
+
/**
|
|
206
|
+
* Runway size as a multiple of the viewport, used only with
|
|
207
|
+
* `mode: "bounded"` (default 2). The bounded content element is sized to
|
|
208
|
+
* `viewport × runway` (capped at the real virtual size). Larger values mean
|
|
209
|
+
* more native-scroll headroom and less frequent rebasing, at the cost of a
|
|
210
|
+
* bigger content element. Clamped up to a minimum of 1.5 so native scroll and
|
|
211
|
+
* touch momentum always have some room.
|
|
212
|
+
*/
|
|
213
|
+
runway?: number;
|
|
183
214
|
};
|
|
184
215
|
/** Defer initial render to the next animation frame. */
|
|
185
216
|
defer?: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -9,8 +9,6 @@
|
|
|
9
9
|
* @packageDocumentation
|
|
10
10
|
*/
|
|
11
11
|
export { createVList } from "./core/create";
|
|
12
|
-
export { scale } from "./plugins/scale";
|
|
13
|
-
export type { ScalePluginConfig } from "./plugins/scale";
|
|
14
12
|
export { scrollbar } from "./plugins/scrollbar";
|
|
15
13
|
export type { ScrollbarPluginConfig } from "./plugins/scrollbar";
|
|
16
14
|
export { grid } from "./plugins/grid";
|
|
@@ -39,6 +37,11 @@ export { tree } from "./plugins/tree";
|
|
|
39
37
|
export type { TreePluginConfig, FlatNode } from "./plugins/tree";
|
|
40
38
|
export { search, DEFAULT_SEARCH_TEXT } from "./plugins/search";
|
|
41
39
|
export type { SearchPluginConfig, SearchText } from "./plugins/search";
|
|
40
|
+
export { carousel } from "./plugins/carousel";
|
|
41
|
+
export type { CarouselPluginConfig, CarouselVariant, CarouselDirection, CarouselState } from "./plugins/carousel";
|
|
42
|
+
/** @deprecated Use `scroll: { mode: "bounded" }` instead. Will be removed in vlist 3.0. */
|
|
43
|
+
export { scale } from "./plugins/scale";
|
|
44
|
+
export type { ScalePluginConfig } from "./plugins/scale";
|
|
42
45
|
export { createStats } from "./utils/stats";
|
|
43
46
|
export type { Stats, StatsConfig, StatsState } from "./utils/stats";
|
|
44
47
|
export { rebuild } from "./utils/rebuild";
|