vlist 3.0.0-next.1 → 3.0.0-next.3
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 +111 -65
- package/README.md +70 -54
- package/dist/config.d.ts +105 -14
- package/dist/config.js +1 -1
- package/dist/core/create.d.ts +22 -5
- package/dist/core/dom.d.ts +1 -1
- package/dist/core/hooks.d.ts +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/pipeline.d.ts +1 -1
- package/dist/core/pool.d.ts +1 -1
- package/dist/core/runway.d.ts +6 -2
- package/dist/core/scroll.d.ts +1 -1
- package/dist/core/sizes.d.ts +1 -1
- package/dist/core/state.d.ts +13 -1
- package/dist/core/types.d.ts +134 -62
- package/dist/core/velocity.d.ts +1 -1
- package/dist/index.d.ts +17 -2
- package/dist/index.js +1 -1
- package/dist/internals.d.ts +0 -1
- package/dist/internals.js +1 -1
- package/dist/native.d.ts +6 -10
- package/dist/native.js +1 -1
- package/dist/plugins/a11y/index.d.ts +1 -0
- package/dist/plugins/a11y/plugin.d.ts +1 -1
- package/dist/plugins/autosize/index.d.ts +2 -1
- package/dist/plugins/autosize/plugin.d.ts +13 -2
- package/dist/plugins/carousel/engine.d.ts +1 -1
- package/dist/plugins/carousel/index.d.ts +1 -0
- package/dist/plugins/carousel/plugin.d.ts +30 -2
- package/dist/plugins/carousel/presets.d.ts +1 -1
- package/dist/plugins/data/index.d.ts +1 -0
- package/dist/plugins/data/plugin.d.ts +15 -2
- package/dist/plugins/grid/index.d.ts +2 -1
- package/dist/plugins/grid/plugin.d.ts +14 -2
- package/dist/plugins/groups/index.d.ts +1 -0
- package/dist/plugins/groups/plugin.d.ts +11 -3
- package/dist/plugins/groups/types.d.ts +6 -1
- package/dist/plugins/masonry/index.d.ts +2 -1
- package/dist/plugins/masonry/plugin.d.ts +10 -2
- package/dist/plugins/masonry/renderer.d.ts +5 -1
- package/dist/plugins/page/plugin.d.ts +1 -1
- package/dist/plugins/scrollbar/index.d.ts +2 -1
- package/dist/plugins/scrollbar/plugin.d.ts +7 -2
- package/dist/plugins/search/index.d.ts +1 -0
- package/dist/plugins/search/plugin.d.ts +26 -1
- package/dist/plugins/selection/index.d.ts +2 -1
- package/dist/plugins/selection/plugin.d.ts +23 -2
- package/dist/plugins/snapshots/index.d.ts +1 -0
- package/dist/plugins/snapshots/plugin.d.ts +9 -2
- package/dist/plugins/sortable/index.d.ts +1 -0
- package/dist/plugins/sortable/plugin.d.ts +11 -3
- package/dist/plugins/table/index.d.ts +1 -0
- package/dist/plugins/table/plugin.d.ts +19 -3
- package/dist/plugins/transition/plugin.d.ts +1 -1
- package/dist/plugins/tree/index.d.ts +1 -0
- package/dist/plugins/tree/layout.d.ts +1 -1
- package/dist/plugins/tree/plugin.d.ts +37 -3
- package/dist/plugins/tree/types.d.ts +1 -1
- package/dist/rendering/sizes.d.ts +7 -1
- package/dist/size.json +1 -1
- package/dist/synthetic.d.ts +3 -3
- package/dist/synthetic.js +1 -1
- package/dist/types.d.ts +17 -21
- package/dist/utils/scroll-padding.d.ts +36 -0
- package/dist/utils/stats.d.ts +9 -1
- package/dist/vlist.css +1 -1
- package/package.json +6 -2
- package/dist/plugins/grid/renderer.d.ts +0 -68
package/README.github.md
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
The virtual list library for every framework. Ultra efficient, batteries-included, and accessible with composable plugins.
|
|
4
4
|
|
|
5
|
-
**v3.0.0-next.
|
|
5
|
+
**v3.0.0-next.3** (prerelease on the npm `next` tag) — Native scrolling by default, opt-in `vlist/synthetic`, and removal of deprecated scroll configuration and plugin hooks. See the [changelog](https://github.com/floor/vlist/blob/next/CHANGELOG.md).
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/vlist)
|
|
8
8
|
[](https://bundlephobia.com/package/vlist)
|
|
9
9
|
[](https://github.com/floor/vlist/actions/workflows/ci.yml)
|
|
10
10
|
[](https://github.com/floor/vlist/blob/main/LICENSE)
|
|
11
11
|
|
|
12
|
-
- **Accessible** — WAI-ARIA, 2D keyboard navigation, focus recovery, screen-reader DOM ordering
|
|
12
|
+
- **Accessible** — `a11y()` or a selection mode adds 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.5 KB gzipped (3.0 prerelease)** — composable plugins with perfect tree-shaking
|
|
15
15
|
- **Constant memory** — ~0.1 MB overhead at any scale, from 10K to 1M+ items
|
|
16
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
|
|
@@ -22,7 +22,7 @@ The virtual list library for every framework. Ultra efficient, batteries-include
|
|
|
22
22
|
|
|
23
23
|
| | vlist | TanStack Virtual | react-virtuoso | virtua | vue-virtual-scroller |
|
|
24
24
|
|---|---|---|---|---|---|
|
|
25
|
-
| **A11y
|
|
25
|
+
| **A11y** | WAI-ARIA + 2D keyboard, one plugin | None (DIY) | Partial | Minimal | None |
|
|
26
26
|
| **Grid + Masonry + Table** | All | Grid only | Grid + Table | Grid only | None |
|
|
27
27
|
| **Vue** | 0.6 KB adapter | Yes | — | Yes | 11.8 KB |
|
|
28
28
|
| **Svelte** | 0.5 KB adapter | Yes | — | Yes | — |
|
|
@@ -46,11 +46,11 @@ npm install vlist@next # 3.0 prerelease; latest stays on 2.8
|
|
|
46
46
|
npm install vlist vlist-vue # or vlist-svelte / vlist-solidjs / vlist-react
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
Adapters use
|
|
49
|
+
Adapters use native scrolling by default. With an adapter that forwards `factory`, select synthetic input explicitly:
|
|
50
50
|
|
|
51
51
|
```ts
|
|
52
52
|
import { useVList } from "vlist-react";
|
|
53
|
-
import { createVList } from "vlist/
|
|
53
|
+
import { createVList } from "vlist/synthetic";
|
|
54
54
|
|
|
55
55
|
useVList({
|
|
56
56
|
factory: createVList,
|
|
@@ -59,7 +59,7 @@ useVList({
|
|
|
59
59
|
});
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
The same factory option is available to the other adapters. `vlist/config` defaults to
|
|
62
|
+
The same factory option is available to the other adapters. `vlist/config` defaults to native scrolling. The factory is structural configuration: changing it requires recreating the list.
|
|
63
63
|
|
|
64
64
|
## Quick Start
|
|
65
65
|
|
|
@@ -108,10 +108,21 @@ const list = createVList({
|
|
|
108
108
|
|
|
109
109
|
## Scroll input
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
Native scrolling is the default: import `createVList` and plugins from `vlist`. This preserves browser scrollbars, native touch momentum, boundary handoff and native assistive-technology scrolling. Carousel and sortable work with this factory; horizontal RTL lists are rejected by both entries. Carousel supplies its private wrap runway only when the plugin is imported; bounded scrolling is no longer a public mode.
|
|
112
|
+
|
|
113
|
+
| Entry | Input model | Use |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| `vlist` | Native (default) | Browser scrolling and all supported plugins |
|
|
116
|
+
| `vlist/synthetic` | Synthetic (opt-in) | Huge lists and application-owned touch motion |
|
|
117
|
+
| `vlist/native` | Alias of `vlist` (deprecated) | Compatibility with `3.0.0-next.1`; use `vlist` for new code |
|
|
118
|
+
|
|
119
|
+
### Synthetic input
|
|
120
|
+
|
|
121
|
+
Select synthetic input by importing its factory; there is no `scroll.mode` option. Plugins still come from `vlist`.
|
|
112
122
|
|
|
113
123
|
```typescript
|
|
114
|
-
import { createVList
|
|
124
|
+
import { createVList } from 'vlist/synthetic'
|
|
125
|
+
import { scrollbar } from 'vlist'
|
|
115
126
|
import 'vlist/styles'
|
|
116
127
|
|
|
117
128
|
const list = createVList({
|
|
@@ -121,69 +132,75 @@ const list = createVList({
|
|
|
121
132
|
}, [scrollbar()])
|
|
122
133
|
```
|
|
123
134
|
|
|
124
|
-
|
|
135
|
+
`page()` uses native document scrolling through an external source with either entry. Its content must fit the 16,777,216 px document element limit; creation throws above that limit when the size is known, and later growth warns once. A deferred custom renderer whose size is first committed during rendering also warns once. Use `vlist/synthetic` viewport scrolling for larger lists. Default native viewport lists warn once when content exceeds their browser-size safety limit, pointing to that entry.
|
|
125
136
|
|
|
126
|
-
|
|
127
|
-
import { createVList } from 'vlist/native'
|
|
128
|
-
import { carousel } from 'vlist'
|
|
137
|
+
`carousel()` works with either entry. Synthetic carousel folds preserve touch motion and smooth snapping across whole laps. `page()` and carousel remain incompatible. The shared carousel plugin includes its native wrap handler even when used with synthetic input; the combined size is reported below.
|
|
129
138
|
|
|
130
|
-
|
|
131
|
-
container: '#slides',
|
|
132
|
-
orientation: 'horizontal',
|
|
133
|
-
items: slides,
|
|
134
|
-
item: { width: 320, template: renderSlide },
|
|
135
|
-
}, [carousel()])
|
|
136
|
-
```
|
|
139
|
+
Synthetic input limitations:
|
|
137
140
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
- Horizontal RTL lists require `vlist/native`. Vertical lists and tables support `dir="rtl"` on the container, including cross-axis wheel movement, aligned table headers and keyboard column navigation.
|
|
143
|
-
- Same-axis touch stops at either boundary with no parent handoff, including gestures starting inside an edge-pinned list. Use `vlist/native` when boundary gestures must scroll the parent page.
|
|
144
|
-
- The default entry has no native main-axis scrollbar. Add `scrollbar()` for an accessible custom scrollbar; native visibility options belong to `vlist/native`.
|
|
141
|
+
- Horizontal RTL lists throw at creation — in this entry and in `vlist`. Vertical lists and tables support `dir="rtl"` in both, including cross-axis wheel movement, aligned table headers and keyboard column navigation.
|
|
142
|
+
- Same-axis touch stops at either boundary with no parent handoff, including gestures starting inside an edge-pinned list. Use the native default when boundary gestures must scroll the parent page.
|
|
143
|
+
- There is no native main-axis scrollbar. Add `scrollbar()` for an accessible custom scrollbar. The synthetic entry rejects the `"native"` and `"none"` scrollbar strings.
|
|
145
144
|
- Inertia initializes its frame clock on the first frame after release, adding up to one frame of release latency.
|
|
146
145
|
- Wheel input at an edge is left to the page when it cannot move the list. Native cross-axis scrolling remains available.
|
|
147
146
|
|
|
148
|
-
Measurement corrections from autosize preserve ongoing motion. Existing plugin conflicts still apply. See the [scroll input contract](https://vlist.io/docs/rfcs/RFC-014-Scroll-Input-Model).
|
|
147
|
+
Measurement corrections from autosize preserve ongoing synthetic motion. Existing plugin conflicts still apply. See the [scroll input contract](https://vlist.io/docs/rfcs/RFC-014-Scroll-Input-Model).
|
|
148
|
+
|
|
149
|
+
### Sortable gestures
|
|
150
|
+
|
|
151
|
+
`sortable()` works with either input entry. Mouse and trackpad dragging still starts after `dragThreshold` (5 px by default). Touch and pen without a handle use `touchDelay`, a 350 ms long press; moving at least `dragThreshold` pixels in any direction before it completes yields to scrolling. This delay separates a deliberate hold from a quick flick. With `handle`, dragging starts at the threshold without waiting; touches elsewhere scroll.
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
sortable({ touchDelay: 350, dragThreshold: 5 })
|
|
155
|
+
sortable({ handle: '.drag-handle' })
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
A touch during scrolling catches the motion and does not arm a hold for that contact. Lift and press again once the list is at rest. A second finger or `pointercancel` cancels the pending press or active drag. Edge auto-scroll uses the list's logical position and stops on drop.
|
|
159
|
+
|
|
160
|
+
The touch-only `.vlist-item--touch-sort` class suppresses selection and callouts on the pressed item. A claimed touch/pen drag adds `.vlist-sort-ghost--touch` to the ghost as a visual cue; override that class in CSS to customize it. Keyboard reordering remains Space to grab/drop, arrows to move and Escape to cancel.
|
|
149
161
|
|
|
150
162
|
## Migrating to 3.0
|
|
151
163
|
|
|
152
|
-
|
|
164
|
+
`3.0.0-next.1` used synthetic input by default. `3.0.0-next.2` restores the 2.x default. Import `vlist/synthetic` explicitly to retain synthetic behavior across these prereleases. `vlist/native` remains a deprecated compatibility alias; its `NativeScrollConfig` and `NativeCreateVListConfig` types alias the standard `ScrollConfig` and `CreateVListConfig`.
|
|
165
|
+
|
|
166
|
+
| 2.x option or API | 3.0 replacement |
|
|
153
167
|
|---|---|
|
|
154
|
-
| `scroll.mode` (all values, both entries) | Omit it. `vlist` provides
|
|
155
|
-
| `scroll.runway` (both entries) | Remove it.
|
|
156
|
-
|
|
|
157
|
-
| `PluginContext.setScrollFns`, `disableDefaultScroll` | Use `
|
|
158
|
-
| `
|
|
168
|
+
| `scroll.mode` (all values, both entries) | Omit it. `vlist` provides native scrolling; import `vlist/synthetic` for synthetic input. |
|
|
169
|
+
| `scroll.runway` (both entries) | Remove it. For huge lists use `vlist/synthetic`, which needs no native runway. |
|
|
170
|
+
| `scroll.scrollbar: "native"` or `"none"` | Retained in default `ScrollConfig`. With synthetic input, omit the string and optionally install `scrollbar()`. |
|
|
171
|
+
| `PluginContext.setScrollFns`, `disableDefaultScroll` | Use `ctx.scroll.setSource` to supply an external position source and commit callback. |
|
|
172
|
+
| Flat `PluginContext` members (`registerMethod`, `forceRender`, `sizeCache`, `scrollTo`, …) | Grouped by capability: `ctx.hooks.method`, `ctx.render.force`, `ctx.sizes.cache`, `ctx.scroll.to`, `ctx.items.all`. `pool`, `config`, `emitter`, `template` and `getState()` stay at the top level. |
|
|
173
|
+
| `scale()` and `ScalePluginConfig` | Remove them; use `vlist/synthetic` for the full logical range. Config no longer installs a scale stub. |
|
|
159
174
|
|
|
160
|
-
Removed
|
|
175
|
+
Removed mode/runway options throw a migration error before creating DOM. `vlist/config` wires only what the config asks for: `selection`, the custom scrollbar, `snapshots` and `a11y` are each opt-in, so an adapter list matches a core list built from the same options. It keeps the scrollbar options convenience and its top-level `scrollbar: "none"`; pass `scrollbar: true` for the overlay scrollbar earlier versions added to every list. `baseOffset` stays private engine state; plugins use `ctx.scroll.getRenderOrigin()`. Custom wrap providers now supply their handler factory as the second argument of `ctx.scroll.setBoundedWrap`.
|
|
161
176
|
|
|
162
177
|
## Plugins
|
|
163
178
|
|
|
164
179
|
| Entry / export | Minified | Gzipped |
|
|
165
180
|
|---|---:|---:|
|
|
166
|
-
| **Base (`vlist`)** |
|
|
167
|
-
| `vlist/synthetic`
|
|
168
|
-
| `vlist/native` |
|
|
169
|
-
| `a11y()` |
|
|
170
|
-
| `selection()` |
|
|
171
|
-
| `data()` |
|
|
172
|
-
| `scrollbar()` |
|
|
173
|
-
| `sortable()` |
|
|
174
|
-
| `groups()` |
|
|
175
|
-
| `page()` |
|
|
176
|
-
| `snapshots()` |
|
|
177
|
-
| `transition()` |
|
|
178
|
-
| `autosize()` |
|
|
179
|
-
| `grid()` |
|
|
180
|
-
| `table()` |
|
|
181
|
-
| `masonry()` |
|
|
182
|
-
| `tree()` |
|
|
183
|
-
| `search()` |
|
|
184
|
-
| `carousel()` |
|
|
185
|
-
|
|
186
|
-
|
|
181
|
+
| **Base (`vlist`)** | 25.7 KB | 9.5 KB |
|
|
182
|
+
| `vlist/synthetic` | 31.7 KB | 11.7 KB |
|
|
183
|
+
| `vlist/native` (alias) | 25.7 KB | 9.5 KB |
|
|
184
|
+
| `a11y()` | 29.0 KB | 10.7 KB |
|
|
185
|
+
| `selection()` | 35.1 KB | 12.3 KB |
|
|
186
|
+
| `data()` | 39.4 KB | 14.3 KB |
|
|
187
|
+
| `scrollbar()` | 33.9 KB | 12.4 KB |
|
|
188
|
+
| `sortable()` | 37.5 KB | 13.0 KB |
|
|
189
|
+
| `groups()` | 41.5 KB | 14.7 KB |
|
|
190
|
+
| `page()` | 28.2 KB | 10.4 KB |
|
|
191
|
+
| `snapshots()` | 29.0 KB | 10.6 KB |
|
|
192
|
+
| `transition()` | 32.4 KB | 11.5 KB |
|
|
193
|
+
| `autosize()` | 28.7 KB | 10.5 KB |
|
|
194
|
+
| `grid()` | 32.5 KB | 11.9 KB |
|
|
195
|
+
| `table()` | 44.1 KB | 15.4 KB |
|
|
196
|
+
| `masonry()` | 36.9 KB | 13.6 KB |
|
|
197
|
+
| `tree()` | 41.0 KB | 14.5 KB |
|
|
198
|
+
| `search()` | 34.7 KB | 12.6 KB |
|
|
199
|
+
| `carousel()` | 38.5 KB | 13.9 KB |
|
|
200
|
+
| `vlist/synthetic` + `carousel()` | 44.4 KB | 16.2 KB |
|
|
201
|
+
| `vlist/synthetic` + `sortable()` | 43.5 KB | 15.3 KB |
|
|
202
|
+
|
|
203
|
+
Sizes are tree-shaken totals from `bun run size`, not additive plugin costs. Plugin rows include the native default factory plus that plugin. The base is **9,914 bytes gzipped**, below the 9.9 KB target. `bun run size` fails if a scenario fails to build, an unused plugin leaks into a bundle, or any published size exceeds its gzip budget. Synthetic input is **11,960 bytes** before plugins. The alias row measures the same source factory; the distributed alias re-exports it without duplicating the implementation.
|
|
187
204
|
|
|
188
205
|
## Examples
|
|
189
206
|
|
|
@@ -308,6 +325,13 @@ const list = createVList({
|
|
|
308
325
|
])
|
|
309
326
|
```
|
|
310
327
|
|
|
328
|
+
Search filters client-side over the items the list holds, so it cannot be
|
|
329
|
+
combined with `data()` — creating a list with both throws. With an adapter, the
|
|
330
|
+
rows in memory are only the loaded window; query the remote dataset through the
|
|
331
|
+
adapter instead. It cannot be combined with `tree()` either: filtering a tree
|
|
332
|
+
means keeping the ancestors of each match, which belongs to the plugin that
|
|
333
|
+
owns the layout, and `tree()` offers no such hook.
|
|
334
|
+
|
|
311
335
|
## Custom scrollbar (3.0 preview)
|
|
312
336
|
|
|
313
337
|
On `next`, `scrollbar()` remains a plugin. macOS and Android default to thin,
|
|
@@ -368,7 +392,9 @@ colors take priority while forced colors are active.
|
|
|
368
392
|
|
|
369
393
|
## Accessibility
|
|
370
394
|
|
|
371
|
-
|
|
395
|
+
Accessibility is a plugin, not a default. Add `a11y()`, or a `selection()` mode
|
|
396
|
+
other than `"none"` which brings the same behaviour, and the list follows the
|
|
397
|
+
[WAI-ARIA listbox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/listbox/):
|
|
372
398
|
|
|
373
399
|
- **Arrow keys** move focus between items with a visible focus ring
|
|
374
400
|
- **2D navigation** in grids and masonry — Up/Down by row, Left/Right by cell
|
|
@@ -377,7 +403,9 @@ Every vlist is accessible by default following the [WAI-ARIA listbox pattern](ht
|
|
|
377
403
|
- **Screen-reader DOM ordering** — items reordered on scroll idle for correct reading order
|
|
378
404
|
- **Focus recovery** — maintains focus when items are removed
|
|
379
405
|
|
|
380
|
-
|
|
406
|
+
A list created without either stays display-only: `role="list"`, no item roles
|
|
407
|
+
and out of the tab order. That is the right shape for log viewers and activity
|
|
408
|
+
feeds, and for items carrying their own interactive elements.
|
|
381
409
|
|
|
382
410
|
## API
|
|
383
411
|
|
|
@@ -391,7 +419,7 @@ const list = createVList(config, [plugin1(), plugin2()])
|
|
|
391
419
|
|--------|-------------|
|
|
392
420
|
| `list.setItems(items)` | Replace all items |
|
|
393
421
|
| `list.appendItems(items)` | Add to end (auto-scrolls in reverse mode) |
|
|
394
|
-
| `list.prependItems(items)` | Add to start (
|
|
422
|
+
| `list.prependItems(items)` | Add to start (in reverse mode, the visible rows hold their position) |
|
|
395
423
|
| `list.updateItem(id, partial)` | Update a single item by ID |
|
|
396
424
|
| `list.insertItem(item, index?)` | Insert at index (animated with `transition`) |
|
|
397
425
|
| `list.removeItem(id)` | Remove by ID (animated with `transition`) |
|
|
@@ -435,6 +463,10 @@ list.on('sort:end', ({ fromIndex, toIndex }) => {})
|
|
|
435
463
|
list.on('sort:cancel', ({ originalItems }) => {})
|
|
436
464
|
```
|
|
437
465
|
|
|
466
|
+
The `index` on `item:click`, `item:dblclick` and `item:contextmenu` is the data
|
|
467
|
+
index — the one `getItemAt`, `scrollToIndex` and `removeItem` take — not the
|
|
468
|
+
layout index, which counts group headers and carousel laps.
|
|
469
|
+
|
|
438
470
|
### Properties
|
|
439
471
|
|
|
440
472
|
| Property | Description |
|
|
@@ -482,8 +514,7 @@ Full configuration reference → **[vlist.io](https://vlist.io)**
|
|
|
482
514
|
| `ariaLabel` | — | Accessible label for the listbox |
|
|
483
515
|
| `orientation` | `'vertical'` | `'vertical'` or `'horizontal'` scroll direction |
|
|
484
516
|
| `padding` | `0` | Content inset — number, `[v, h]`, or `[top, right, bottom, left]` |
|
|
485
|
-
| `
|
|
486
|
-
| `reverse` | `false` | Reverse mode for chat UIs |
|
|
517
|
+
| `reverse` | `false` | The list reads bottom-up (chat UIs): a view sitting at the end stays there as `appendItems` adds to it, `prependItems` holds the visible rows in place as older history loads above them, arrow keys invert, and `transition()` animates from the bottom. Item order is yours to supply; the layout itself is not reversed. `masonry()` and `table()` reject it |
|
|
487
518
|
|
|
488
519
|
## Styling
|
|
489
520
|
|
|
@@ -513,23 +544,38 @@ Live benchmarks against 9 competitors → **[vlist.io/benchmarks](https://vlist.
|
|
|
513
544
|
|
|
514
545
|
## TypeScript
|
|
515
546
|
|
|
516
|
-
Fully typed
|
|
547
|
+
Fully typed and generic over your item type. An item only needs an `id`, so ordinary
|
|
548
|
+
interfaces work:
|
|
517
549
|
|
|
518
550
|
```typescript
|
|
519
|
-
import { createVList, grid, type
|
|
551
|
+
import { createVList, grid, selection, type CreateVListConfig } from 'vlist'
|
|
520
552
|
|
|
521
553
|
interface Photo { id: number; url: string; title: string }
|
|
522
554
|
|
|
523
|
-
const
|
|
555
|
+
const config: CreateVListConfig<Photo> = {
|
|
524
556
|
container: '#gallery',
|
|
525
557
|
items: photos,
|
|
526
558
|
item: {
|
|
527
559
|
height: 200,
|
|
528
560
|
template: (photo) => `<img src="${photo.url}" />`,
|
|
529
561
|
},
|
|
530
|
-
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
const list = createVList(config, [grid({ columns: 4 }), selection<Photo>({ mode: 'multiple' })])
|
|
565
|
+
|
|
566
|
+
list.select(photos[0].id) // typed: selection is in the array
|
|
567
|
+
const chosen: Photo[] = list.getSelectedItems()
|
|
568
|
+
// list.expand(1) // compile error: no tree plugin here
|
|
531
569
|
```
|
|
532
570
|
|
|
571
|
+
`createVList` infers the methods each plugin adds, so a list exposes exactly what its
|
|
572
|
+
plugins provide, and a mistyped call does not compile.
|
|
573
|
+
|
|
574
|
+
TypeScript does not allow a partial type argument list. Writing
|
|
575
|
+
`createVList<Photo>(config, plugins)` supplies the item type and leaves the plugin list on
|
|
576
|
+
its default, so plugin methods are not inferred. Type the config as above, or let the item
|
|
577
|
+
type come from your data.
|
|
578
|
+
|
|
533
579
|
## Migrating from v1
|
|
534
580
|
|
|
535
581
|
v2 is a ground-up rewrite — simpler API, 55% smaller base bundle, zero-allocation scroll path. [Full announcement →](https://vlist.io/blog/v2)
|
package/README.md
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
The virtual list library for every framework. Ultra efficient, batteries-included, and accessible with composable plugins.
|
|
4
4
|
|
|
5
|
-
**v3.0.0-next.
|
|
5
|
+
**v3.0.0-next.3** (prerelease on the npm `next` tag) — Native scrolling by default, opt-in `vlist/synthetic`, and removal of deprecated scroll configuration and plugin hooks. See the [changelog](https://github.com/floor/vlist/blob/next/CHANGELOG.md).
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/vlist)
|
|
8
8
|
[](https://bundlephobia.com/package/vlist)
|
|
9
9
|
[](https://github.com/floor/vlist/actions/workflows/ci.yml)
|
|
10
10
|
[](https://github.com/floor/vlist/blob/main/LICENSE)
|
|
11
11
|
|
|
12
|
-
- **Accessible** — WAI-ARIA, 2D keyboard navigation, focus recovery, screen-reader DOM ordering
|
|
12
|
+
- **Accessible** — `a11y()` or a selection mode adds 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.4 KB gzipped (3.0 prerelease)** — 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
|
|
|
@@ -55,10 +55,21 @@ const list = createVList({ container: '#app', items, item: { height: 200, templa
|
|
|
55
55
|
|
|
56
56
|
## Scroll input
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
Native scrolling is the default: import `createVList` and plugins from `vlist`. This preserves browser scrollbars, native touch momentum, boundary handoff and native assistive-technology scrolling. Carousel and sortable work with this factory; horizontal RTL lists are rejected by both entries. Carousel supplies its private wrap runway only when the plugin is imported; bounded scrolling is no longer a public mode.
|
|
59
|
+
|
|
60
|
+
| Entry | Input model | Use |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `vlist` | Native (default) | Browser scrolling and all supported plugins |
|
|
63
|
+
| `vlist/synthetic` | Synthetic (opt-in) | Huge lists and application-owned touch motion |
|
|
64
|
+
| `vlist/native` | Alias of `vlist` (deprecated) | Compatibility with `3.0.0-next.1`; use `vlist` for new code |
|
|
65
|
+
|
|
66
|
+
### Synthetic input
|
|
67
|
+
|
|
68
|
+
Select synthetic input by importing its factory; there is no `scroll.mode` option. Plugins still come from `vlist`.
|
|
59
69
|
|
|
60
70
|
```typescript
|
|
61
|
-
import { createVList
|
|
71
|
+
import { createVList } from 'vlist/synthetic'
|
|
72
|
+
import { scrollbar } from 'vlist'
|
|
62
73
|
import 'vlist/styles'
|
|
63
74
|
|
|
64
75
|
const list = createVList({
|
|
@@ -68,69 +79,74 @@ const list = createVList({
|
|
|
68
79
|
}, [scrollbar()])
|
|
69
80
|
```
|
|
70
81
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
74
|
-
import { createVList } from 'vlist/native'
|
|
75
|
-
import { carousel } from 'vlist'
|
|
76
|
-
|
|
77
|
-
const list = createVList({
|
|
78
|
-
container: '#slides',
|
|
79
|
-
orientation: 'horizontal',
|
|
80
|
-
items: slides,
|
|
81
|
-
item: { width: 320, template: renderSlide },
|
|
82
|
-
}, [carousel()])
|
|
83
|
-
```
|
|
82
|
+
`page()` uses native document scrolling through an external source with either entry. Its content must fit the 16,777,216 px document element limit; creation throws above that limit when the size is known, and later growth warns once. A deferred custom renderer whose size is first committed during rendering also warns once. Use `vlist/synthetic` viewport scrolling for larger lists. Default native viewport lists warn once when content exceeds their browser-size safety limit, pointing to that entry.
|
|
84
83
|
|
|
85
|
-
`
|
|
84
|
+
`carousel()` works with either entry. Synthetic carousel folds preserve touch motion and smooth snapping across whole laps. `page()` and carousel remain incompatible. The shared carousel plugin includes its native wrap handler even when used with synthetic input; the combined size is reported below.
|
|
86
85
|
|
|
87
|
-
|
|
86
|
+
Synthetic input limitations:
|
|
88
87
|
|
|
89
|
-
- Horizontal RTL lists
|
|
90
|
-
- Same-axis touch stops at either boundary with no parent handoff, including gestures starting inside an edge-pinned list. Use
|
|
91
|
-
-
|
|
88
|
+
- Horizontal RTL lists throw at creation — in this entry and in `vlist`. Vertical lists and tables support `dir="rtl"` in both, including cross-axis wheel movement, aligned table headers and keyboard column navigation.
|
|
89
|
+
- Same-axis touch stops at either boundary with no parent handoff, including gestures starting inside an edge-pinned list. Use the native default when boundary gestures must scroll the parent page.
|
|
90
|
+
- There is no native main-axis scrollbar. Add `scrollbar()` for an accessible custom scrollbar. The synthetic entry rejects the `"native"` and `"none"` scrollbar strings.
|
|
92
91
|
- Inertia initializes its frame clock on the first frame after release, adding up to one frame of release latency.
|
|
93
92
|
- Wheel input at an edge is left to the page when it cannot move the list. Native cross-axis scrolling remains available.
|
|
94
93
|
|
|
95
|
-
Measurement corrections from autosize preserve ongoing motion. Existing plugin conflicts still apply. See the [scroll input contract](https://vlist.io/docs/rfcs/RFC-014-Scroll-Input-Model).
|
|
94
|
+
Measurement corrections from autosize preserve ongoing synthetic motion. Existing plugin conflicts still apply. See the [scroll input contract](https://vlist.io/docs/rfcs/RFC-014-Scroll-Input-Model).
|
|
95
|
+
|
|
96
|
+
### Sortable gestures
|
|
97
|
+
|
|
98
|
+
`sortable()` works with either input entry. Mouse and trackpad dragging still starts after `dragThreshold` (5 px by default). Touch and pen without a handle use `touchDelay`, a 350 ms long press; moving at least `dragThreshold` pixels in any direction before it completes yields to scrolling. This delay separates a deliberate hold from a quick flick. With `handle`, dragging starts at the threshold without waiting; touches elsewhere scroll.
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
sortable({ touchDelay: 350, dragThreshold: 5 })
|
|
102
|
+
sortable({ handle: '.drag-handle' })
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
A touch during scrolling catches the motion and does not arm a hold for that contact. Lift and press again once the list is at rest. A second finger or `pointercancel` cancels the pending press or active drag. Edge auto-scroll uses the list's logical position and stops on drop.
|
|
106
|
+
|
|
107
|
+
The touch-only `.vlist-item--touch-sort` class suppresses selection and callouts on the pressed item. A claimed touch/pen drag adds `.vlist-sort-ghost--touch` to the ghost as a visual cue; override that class in CSS to customize it. Keyboard reordering remains Space to grab/drop, arrows to move and Escape to cancel.
|
|
96
108
|
|
|
97
109
|
## Migrating to 3.0
|
|
98
110
|
|
|
99
|
-
|
|
111
|
+
`3.0.0-next.1` used synthetic input by default. `3.0.0-next.2` restores the 2.x default. Import `vlist/synthetic` explicitly to retain synthetic behavior across these prereleases. `vlist/native` remains a deprecated compatibility alias; its `NativeScrollConfig` and `NativeCreateVListConfig` types alias the standard `ScrollConfig` and `CreateVListConfig`.
|
|
112
|
+
|
|
113
|
+
| 2.x option or API | 3.0 replacement |
|
|
100
114
|
|---|---|
|
|
101
|
-
| `scroll.mode` (all values, both entries) | Omit it. `vlist` provides
|
|
102
|
-
| `scroll.runway` (both entries) | Remove it.
|
|
103
|
-
|
|
|
115
|
+
| `scroll.mode` (all values, both entries) | Omit it. `vlist` provides native scrolling; import `vlist/synthetic` for synthetic input. |
|
|
116
|
+
| `scroll.runway` (both entries) | Remove it. For huge lists use `vlist/synthetic`, which needs no native runway. |
|
|
117
|
+
| `scroll.scrollbar: "native"` or `"none"` | Retained in default `ScrollConfig`. With synthetic input, omit the string and optionally install `scrollbar()`. |
|
|
104
118
|
| `PluginContext.setScrollFns`, `disableDefaultScroll` | Use `setScrollSource` to supply an external position source and commit callback. |
|
|
105
|
-
| `scale()` and `ScalePluginConfig` | Remove
|
|
119
|
+
| `scale()` and `ScalePluginConfig` | Remove them; use `vlist/synthetic` for the full logical range. Config no longer installs a scale stub. |
|
|
106
120
|
|
|
107
|
-
Removed
|
|
121
|
+
Removed mode/runway options throw a migration error before creating DOM. `vlist/config` wires only what the config asks for: `selection`, the custom scrollbar, `snapshots` and `a11y` are each opt-in, so an adapter list matches a core list built from the same options. It keeps the scrollbar options convenience and its top-level `scrollbar: "none"`; pass `scrollbar: true` for the overlay scrollbar earlier versions added to every list. `baseOffset` stays private engine state; plugins use `ctx.scroll.getRenderOrigin()`. Custom wrap providers now supply their handler factory as the second argument of `ctx.scroll.setBoundedWrap`.
|
|
108
122
|
|
|
109
123
|
## Plugins
|
|
110
124
|
|
|
111
125
|
| Entry / export | Minified | Gzipped |
|
|
112
126
|
|---|---:|---:|
|
|
113
|
-
| **Base (`vlist`)** |
|
|
114
|
-
| `vlist/synthetic`
|
|
115
|
-
| `vlist/native` |
|
|
116
|
-
| `a11y()` |
|
|
117
|
-
| `selection()` |
|
|
118
|
-
| `data()` |
|
|
119
|
-
| `scrollbar()` |
|
|
120
|
-
| `sortable()` |
|
|
121
|
-
| `groups()` |
|
|
122
|
-
| `page()` |
|
|
123
|
-
| `snapshots()` |
|
|
124
|
-
| `transition()` |
|
|
125
|
-
| `autosize()` |
|
|
126
|
-
| `grid()` |
|
|
127
|
-
| `table()` |
|
|
128
|
-
| `masonry()` |
|
|
129
|
-
| `tree()` |
|
|
130
|
-
| `search()` |
|
|
131
|
-
| `carousel()` |
|
|
132
|
-
|
|
133
|
-
|
|
127
|
+
| **Base (`vlist`)** | 25.7 KB | 9.4 KB |
|
|
128
|
+
| `vlist/synthetic` | 31.7 KB | 11.7 KB |
|
|
129
|
+
| `vlist/native` (alias) | 25.7 KB | 9.5 KB |
|
|
130
|
+
| `a11y()` | 29.0 KB | 10.7 KB |
|
|
131
|
+
| `selection()` | 35.1 KB | 12.3 KB |
|
|
132
|
+
| `data()` | 39.4 KB | 14.3 KB |
|
|
133
|
+
| `scrollbar()` | 33.9 KB | 12.3 KB |
|
|
134
|
+
| `sortable()` | 37.4 KB | 12.9 KB |
|
|
135
|
+
| `groups()` | 41.4 KB | 14.7 KB |
|
|
136
|
+
| `page()` | 28.2 KB | 10.3 KB |
|
|
137
|
+
| `snapshots()` | 29.0 KB | 10.6 KB |
|
|
138
|
+
| `transition()` | 32.4 KB | 11.4 KB |
|
|
139
|
+
| `autosize()` | 28.8 KB | 10.5 KB |
|
|
140
|
+
| `grid()` | 32.6 KB | 11.8 KB |
|
|
141
|
+
| `table()` | 44.2 KB | 15.3 KB |
|
|
142
|
+
| `masonry()` | 37.0 KB | 13.5 KB |
|
|
143
|
+
| `tree()` | 41.0 KB | 14.5 KB |
|
|
144
|
+
| `search()` | 34.7 KB | 12.6 KB |
|
|
145
|
+
| `carousel()` | 38.5 KB | 13.9 KB |
|
|
146
|
+
| `vlist/synthetic` + `carousel()` | 44.4 KB | 16.1 KB |
|
|
147
|
+
| `vlist/synthetic` + `sortable()` | 43.4 KB | 15.2 KB |
|
|
148
|
+
|
|
149
|
+
Sizes are tree-shaken totals from `bun run size`, not additive plugin costs. Plugin rows include the native default factory plus that plugin. The base is **9,914 bytes gzipped**, below the 9.9 KB target. `bun run size` fails if a scenario fails to build, an unused plugin leaks into a bundle, or any published size exceeds its gzip budget. Synthetic input is **11,960 bytes** before plugins. The alias row measures the same source factory; the distributed alias re-exports it without duplicating the implementation.
|
|
134
150
|
|
|
135
151
|
## Framework Adapters
|
|
136
152
|
|
|
@@ -141,11 +157,11 @@ Sizes are tree-shaken totals from `bun run size`, not additive plugin costs. Plu
|
|
|
141
157
|
| SolidJS | [`vlist-solidjs`](https://github.com/floor/vlist-solidjs) | 0.5 KB |
|
|
142
158
|
| React | [`vlist-react`](https://github.com/floor/vlist-react) | 0.6 KB |
|
|
143
159
|
|
|
144
|
-
Adapters use
|
|
160
|
+
Adapters use native scrolling by default. With an adapter that forwards `factory`, select synthetic input explicitly:
|
|
145
161
|
|
|
146
162
|
```ts
|
|
147
163
|
import { useVList } from "vlist-react";
|
|
148
|
-
import { createVList } from "vlist/
|
|
164
|
+
import { createVList } from "vlist/synthetic";
|
|
149
165
|
|
|
150
166
|
useVList({
|
|
151
167
|
factory: createVList,
|
|
@@ -154,7 +170,7 @@ useVList({
|
|
|
154
170
|
});
|
|
155
171
|
```
|
|
156
172
|
|
|
157
|
-
The same factory option is available to the other adapters. `vlist/config` defaults to
|
|
173
|
+
The same factory option is available to the other adapters. `vlist/config` defaults to native scrolling. The factory is structural configuration: changing it requires recreating the list.
|
|
158
174
|
|
|
159
175
|
## Docs & Examples
|
|
160
176
|
|