@floor/vlist 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,7 @@ Lightweight, high-performance virtual list with zero dependencies and dimension-
9
9
 
10
10
  - **Zero dependencies** — no external libraries
11
11
  - **Ultra memory efficient** — ~0.1-0.2 MB constant overhead regardless of dataset size
12
- - **8–12 KB gzipped** — pay only for features you use (vs 20 KB+ monolithic alternatives)
12
+ - **~8 KB gzipped** — pay only for features you use (vs 20 KB+ monolithic alternatives)
13
13
  - **Builder API** — composable features with perfect tree-shaking
14
14
  - **Grid, masonry, sections, async, selection, scale** — all opt-in
15
15
  - **Horizontal & vertical** — semantically correct orientation support
@@ -19,13 +19,14 @@ Lightweight, high-performance virtual list with zero dependencies and dimension-
19
19
 
20
20
  **30+ interactive examples → [vlist.dev](https://vlist.dev)**
21
21
 
22
- ## v1.0.0 Highlights
22
+ ## Highlights
23
23
 
24
- - 🧱 **Masonry layout** — Pinterest-style shortest-lane placement via `withMasonry()`
25
- - **Performance optimized** — 13-pattern optimization playbook applied across the entire rendering pipeline
26
- - **Dimension-agnostic API** — semantically correct terminology for both orientations
27
- - 🎯 **Horizontal sections** — sticky headers work in horizontal carousels
28
- - 🎨 **Horizontal grid layouts** — 2D grids work in both orientations
24
+
25
+ - **Dimension-agnostic API** — semantically correct terminology for both orientations
26
+ - **Performance optimized** — 13-pattern optimization playbook applied across the entire rendering pipeline
27
+ - **Horizontal sections** — sticky headers work in horizontal carousels
28
+ - **Horizontal grid layouts** — 2D grids work in both orientations
29
+ - **Masonry** — shortest-lane placement via `withMasonry()`
29
30
 
30
31
  ## Installation
31
32
 
@@ -79,20 +80,20 @@ const list = vlist({
79
80
  .build()
80
81
  ```
81
82
 
82
- ### Plugins
83
+ ### Features
83
84
 
84
85
  | Feature | Size | Description |
85
86
  |---------|------|-------------|
86
- | **Base** | 7.7 KB | Core virtualization |
87
- | `withGrid()` | +4.0 KB | 2D grid layout |
88
- | `withMasonry()` | +2.9 KB | Pinterest-style masonry layout |
89
- | `withSections()` | +4.6 KB | Grouped lists with sticky/inline headers |
90
- | `withAsync()` | +5.3 KB | Lazy loading with adapters |
91
- | `withSelection()` | +2.3 KB | Single/multiple selection + keyboard nav |
92
- | `withScale()` | +2.2 KB | 1M+ items via scroll compression |
93
- | `withScrollbar()` | +1.0 KB | Custom scrollbar UI |
94
- | `withPage()` | +0.9 KB | Document-level scrolling |
95
- | `withSnapshots()` | included | Scroll save/restore |
87
+ | **Base** | 8.1 KB | Core virtualization |
88
+ | `withGrid()` | +3.8 KB | 2D grid layout |
89
+ | `withMasonry()` | +2.3 KB | Pinterest-style masonry layout |
90
+ | `withSections()` | +4.1 KB | Grouped lists with sticky/inline headers |
91
+ | `withAsync()` | +3.9 KB | Lazy loading with adapters |
92
+ | `withSelection()` | +1.6 KB | Single/multiple selection + keyboard nav |
93
+ | `withScale()` | +2.6 KB | 1M+ items via scroll compression |
94
+ | `withScrollbar()` | +1.2 KB | Custom scrollbar UI |
95
+ | `withPage()` | +0.4 KB | Document-level scrolling |
96
+ | `withSnapshots()` | +0.5 KB | Scroll save/restore |
96
97
 
97
98
  ## Examples
98
99
 
@@ -190,7 +191,7 @@ See **[vlist.dev](https://vlist.dev)** for live demos of each.
190
191
  ## API
191
192
 
192
193
  ```typescript
193
- const list = vlist(config).use(...plugins).build()
194
+ const list = vlist(config).use(...features).build()
194
195
  ```
195
196
 
196
197
  ### Data
@@ -212,7 +213,11 @@ const list = vlist(config).use(...plugins).build()
212
213
  | `list.scrollToIndex(i, opts?)` | With `{ align, behavior: 'smooth', duration }` |
213
214
  | `list.cancelScroll()` | Cancel smooth scroll animation |
214
215
  | `list.getScrollPosition()` | Current scroll offset |
215
- | `list.getVisibleRange()` | `{ start, end }` of visible indices |
216
+
217
+ ### Snapshots (with `withSnapshots()`)
218
+
219
+ | Method | Description |
220
+ |--------|-------------|
216
221
  | `list.getScrollSnapshot()` | Save scroll state (for SPA navigation) |
217
222
  | `list.restoreScroll(snapshot)` | Restore saved scroll state |
218
223
 
@@ -220,11 +225,11 @@ const list = vlist(config).use(...plugins).build()
220
225
 
221
226
  | Method | Description |
222
227
  |--------|-------------|
223
- | `list.selectItem(id)` | Select item |
224
- | `list.deselectItem(id)` | Deselect item |
225
- | `list.toggleSelection(id)` | Toggle |
228
+ | `list.select(...ids)` | Select item(s) |
229
+ | `list.deselect(...ids)` | Deselect item(s) |
230
+ | `list.toggleSelect(id)` | Toggle |
226
231
  | `list.selectAll()` / `list.clearSelection()` | Bulk operations |
227
- | `list.getSelectedIds()` | Array of selected IDs |
232
+ | `list.getSelected()` | Array of selected IDs |
228
233
  | `list.getSelectedItems()` | Array of selected items |
229
234
 
230
235
  ### Grid (with `withGrid()`)
@@ -263,9 +268,9 @@ list.on('velocity:change', ({ velocity, reliable }) => {})
263
268
  list.destroy()
264
269
  ```
265
270
 
266
- ## Plugin Configuration
271
+ ## Feature Configuration
267
272
 
268
- Each plugin's config is fully typed — hover in your IDE for details.
273
+ Each feature's config is fully typed — hover in your IDE for details.
269
274
 
270
275
  ```typescript
271
276
  withGrid({ columns: 4, gap: 16 })
@@ -288,9 +293,10 @@ Full configuration reference → **[vlist.dev](https://vlist.dev)**
288
293
  | React | [`vlist-react`](https://github.com/floor/vlist-react) | 0.6 KB gzip |
289
294
  | Vue | [`vlist-vue`](https://github.com/floor/vlist-vue) | 0.6 KB gzip |
290
295
  | Svelte | [`vlist-svelte`](https://github.com/floor/vlist-svelte) | 0.5 KB gzip |
296
+ | SolidJS | [`vlist-solidjs`](https://github.com/floor/vlist-solidjs) | 0.5 KB gzip |
291
297
 
292
298
  ```bash
293
- npm install @floor/vlist vlist-react # or vlist-vue / vlist-svelte
299
+ npm install @floor/vlist vlist-react # or vlist-vue / vlist-svelte / vlist-solidjs
294
300
  ```
295
301
 
296
302
  Each adapter README has setup examples and API docs.
@@ -331,11 +337,10 @@ This makes the codebase clearer and eliminates semantic confusion when working w
331
337
 
332
338
  | Configuration | Gzipped |
333
339
  |---------------|---------|
334
- | Base only | 7.7 KB |
335
- | + Grid | 11.7 KB |
336
- | + Sections | 12.3 KB |
337
- | + Async | 13.5 KB |
338
- | All plugins | ~16 KB |
340
+ | Base only | 8.1 KB |
341
+ | + Grid | 11.9 KB |
342
+ | + Sections | 12.2 KB |
343
+ | + Async | 12.0 KB |
339
344
 
340
345
  ### Memory Efficiency
341
346
 
@@ -406,4 +411,4 @@ See [CHANGELOG.md](https://vlist.dev/docs/CHANGELOG.md) for the full release his
406
411
 
407
412
  ---
408
413
 
409
- Built by [Floor IO](https://floor.io)
414
+ Built by [Floor IO](https://floor.io)
@@ -21,5 +21,5 @@
21
21
  * @packageDocumentation
22
22
  */
23
23
  export { vlist } from "./core";
24
- export type { VListBuilder, BuiltVList, BuilderConfig, VListFeature, FeatureFactory, BuilderContext, BuilderState, ResolvedBuilderConfig, } from "./types";
24
+ export type { VListBuilder, VList, BuilderConfig, VListFeature, FeatureFactory, BuilderContext, BuilderState, ResolvedBuilderConfig, } from "./types";
25
25
  //# sourceMappingURL=index.d.ts.map
@@ -243,10 +243,17 @@ export interface VListBuilder<T extends VListItem = VListItem> {
243
243
  /** Register a feature. Chainable. */
244
244
  use(feature: VListFeature<T>): VListBuilder<T>;
245
245
  /** Materialize the virtual list. Creates DOM, initializes features, returns API. */
246
- build(): BuiltVList<T>;
246
+ build(): VList<T>;
247
247
  }
248
- /** Base API always available from builder (data methods, scroll methods, events, lifecycle) */
249
- export interface BuiltVList<T extends VListItem = VListItem> {
248
+ /**
249
+ * VList instance API returned by `vlist(config).build()`.
250
+ *
251
+ * Always-available methods (data, scroll, events, lifecycle) are required.
252
+ * Feature methods (selection, snapshots, grid, etc.) are optional —
253
+ * they exist on the instance only when the corresponding feature is
254
+ * registered via `.use()`.
255
+ */
256
+ export interface VList<T extends VListItem = VListItem> {
250
257
  /** The root DOM element */
251
258
  readonly element: HTMLElement;
252
259
  /** Current items */
package/dist/index.d.ts CHANGED
@@ -15,8 +15,8 @@ export { withGrid } from "./features/grid";
15
15
  export { withMasonry } from "./features/masonry";
16
16
  export { withSelection } from "./features/selection";
17
17
  export { withSnapshots } from "./features/snapshots";
18
- export type { VList, VListConfig, VListItem, VListEvents, ItemTemplate, ItemState, SelectionMode, SelectionConfig, SelectionState, ScrollbarConfig, ScrollbarOptions, ScrollConfig, ScrollToOptions, ScrollSnapshot, VListAdapter, AdapterParams, AdapterResponse, Range, ViewportState, EventHandler, Unsubscribe, } from "./types";
19
- export type { VListBuilder, BuiltVList, BuilderConfig, VListFeature, BuilderContext, } from "./builder";
18
+ export type { VListItem, VListEvents, ItemTemplate, ItemState, SelectionMode, SelectionConfig, SelectionState, ScrollbarConfig, ScrollbarOptions, ScrollConfig, ScrollToOptions, ScrollSnapshot, VListAdapter, AdapterParams, AdapterResponse, Range, ViewportState, EventHandler, Unsubscribe, } from "./types";
19
+ export type { VListBuilder, VList, BuilderConfig, VListFeature, BuilderContext, } from "./builder";
20
20
  export { createGroupLayout as createSectionLayout, buildLayoutItems, createGroupedSizeFn as createSectionedSizeFn, createStickyHeader, isGroupHeader as isSectionHeader, type GroupsConfig as SectionsConfig, type GroupBoundary as SectionBoundary, type LayoutEntry, type GroupHeaderItem as SectionHeaderItem, type GroupLayout as SectionLayout, type StickyHeader, } from "./features/sections";
21
21
  export { createGridLayout, createGridRenderer, type GridConfig, type GridLayout, type GridPosition, type GridRenderer, type ItemRange, } from "./features/grid";
22
22
  export { createMasonryLayout, createMasonryRenderer, type MasonryConfig, type MasonryLayout, type MasonryRenderer, type GetItemFn, type ItemPlacement, } from "./features/masonry";
package/dist/types.d.ts CHANGED
@@ -146,171 +146,6 @@ export interface ItemConfig<T extends VListItem = VListItem> {
146
146
  /** Template function to render each item */
147
147
  template: ItemTemplate<T>;
148
148
  }
149
- /** Main configuration for createVList */
150
- export interface VListConfig<T extends VListItem = VListItem> {
151
- /** Container element or selector */
152
- container: HTMLElement | string;
153
- /**
154
- * Layout orientation (default: 'vertical').
155
- *
156
- * - `'vertical'` — Standard top-to-bottom scrolling (default)
157
- * - `'horizontal'` — Left-to-right scrolling (carousel, timeline, etc.)
158
- *
159
- * When `'horizontal'`:
160
- * - `item.width` is required (main-axis size for virtualization)
161
- * - `item.height` is optional (cross-axis size, can be set via CSS)
162
- * - Items are positioned with `translateX` instead of `translateY`
163
- * - The viewport scrolls on the X axis
164
- *
165
- * Cannot be combined with `groups`, `grid`, or `reverse`.
166
- */
167
- orientation?: "vertical" | "horizontal";
168
- /** Item configuration (height and template) */
169
- item: ItemConfig<T>;
170
- /** Static items array (optional if using adapter) */
171
- items?: T[];
172
- /** Async data adapter for infinite scroll */
173
- adapter?: VListAdapter<T>;
174
- /** Number of extra items to render outside viewport (default: 3) */
175
- overscan?: number;
176
- /** Selection configuration */
177
- selection?: SelectionConfig;
178
- /**
179
- * External scroll element for document/window scrolling.
180
- * @deprecated Use `scroll.element` instead.
181
- */
182
- scrollElement?: Window;
183
- /** Scroll behavior configuration */
184
- scroll?: ScrollConfig;
185
- /**
186
- * Custom scrollbar configuration.
187
- * @deprecated Use `scroll.scrollbar` instead.
188
- */
189
- scrollbar?: ScrollbarConfig;
190
- /** Loading behavior configuration */
191
- loading?: LoadingConfig;
192
- /**
193
- * Scroll idle detection timeout in ms (default: 150).
194
- * @deprecated Use `scroll.idleTimeout` instead.
195
- */
196
- idleTimeout?: number;
197
- /** Custom CSS class prefix (default: 'vlist') */
198
- classPrefix?: string;
199
- /** Accessible label for the listbox (sets aria-label on the root element) */
200
- ariaLabel?: string;
201
- /**
202
- * Groups configuration for sticky headers / grouped lists.
203
- * When set, items are automatically grouped and section headers
204
- * are inserted at group boundaries.
205
- *
206
- * Items MUST be pre-sorted by group — a new header is inserted
207
- * whenever `getGroupForIndex` returns a different value.
208
- */
209
- groups?: GroupsConfig;
210
- /**
211
- * Layout mode (default: 'list').
212
- * - `'list'` — Standard vertical list (one item per row)
213
- * - `'grid'` — 2D grid layout (multiple items per row, requires `grid` config)
214
- *
215
- * In grid mode:
216
- * - Virtualization operates on ROWS, not individual items
217
- * - Each row contains `grid.columns` items side by side
218
- * - Item width is automatically calculated: (containerWidth - gaps) / columns
219
- * - Compression applies to row count, not item count
220
- */
221
- layout?: "list" | "grid";
222
- /**
223
- * Grid configuration (required when `layout: 'grid'`).
224
- *
225
- * ```ts
226
- * createVList({
227
- * container: '#gallery',
228
- * layout: 'grid',
229
- * grid: { columns: 4, gap: 8 },
230
- * item: {
231
- * height: 200,
232
- * template: (item) => `<img src="${item.thumbnail}" />`,
233
- * },
234
- * items: photos,
235
- * });
236
- * ```
237
- */
238
- grid?: GridConfig;
239
- /**
240
- * Reverse mode for chat-style UIs.
241
- * When enabled:
242
- * - The list starts scrolled to the bottom (newest items visible)
243
- * - `appendItems()` auto-scrolls to bottom if the user was already at bottom
244
- * - `prependItems()` preserves scroll position (older messages load above without jumping)
245
- * - With an adapter, "load more" triggers near the TOP (loading older content)
246
- *
247
- * Items stay in chronological order (oldest = index 0, newest = last).
248
- * Cannot be combined with `groups` or `grid` layout.
249
- *
250
- * ```ts
251
- * const chat = createVList({
252
- * container: '#messages',
253
- * reverse: true,
254
- * item: { height: 60, template: messageTemplate },
255
- * items: messages,
256
- * });
257
- *
258
- * chat.appendItems([newMessage]); // auto-scrolls to bottom
259
- * chat.prependItems(olderMessages); // scroll position preserved
260
- * ```
261
- */
262
- reverse?: boolean;
263
- }
264
- /**
265
- * Configuration options that can be updated dynamically without recreating the instance.
266
- * Used by the update() method.
267
- */
268
- export interface VListUpdateConfig {
269
- /**
270
- * Grid configuration (columns and gap).
271
- * Only applicable when layout is 'grid'.
272
- */
273
- grid?: {
274
- columns?: number;
275
- gap?: number;
276
- };
277
- /**
278
- * Item height (for variable height updates).
279
- * Can be a number or a function.
280
- */
281
- itemHeight?: number | ((index: number) => number);
282
- /**
283
- * Selection mode.
284
- * Changes selection behavior without recreating the list.
285
- */
286
- selectionMode?: SelectionMode;
287
- /**
288
- * Overscan value (number of items to render outside viewport).
289
- */
290
- overscan?: number;
291
- }
292
- /** Loading behavior configuration */
293
- export interface LoadingConfig {
294
- /**
295
- * Velocity threshold above which data loading is skipped (px/ms)
296
- * When scrolling faster than this, loading is deferred until scroll stops.
297
- * Default: 25 px/ms
298
- */
299
- cancelThreshold?: number;
300
- /**
301
- * Velocity threshold for preloading (px/ms)
302
- * When scrolling faster than this but slower than cancelThreshold,
303
- * extra items are preloaded in the scroll direction.
304
- * Default: 2 px/ms
305
- */
306
- preloadThreshold?: number;
307
- /**
308
- * Number of extra items to preload ahead of scroll direction
309
- * Only applies when velocity is between preloadThreshold and cancelThreshold.
310
- * Default: 50 items
311
- */
312
- preloadAhead?: number;
313
- }
314
149
  /** Scroll position snapshot for save/restore */
315
150
  export interface ScrollSnapshot {
316
151
  /** First visible item index */
@@ -533,76 +368,4 @@ export interface VListEvents<T extends VListItem = VListItem> extends EventMap {
533
368
  export type EventHandler<T> = (payload: T) => void;
534
369
  /** Unsubscribe function */
535
370
  export type Unsubscribe = () => void;
536
- /** VList instance API */
537
- export interface VList<T extends VListItem = VListItem> {
538
- /** The root DOM element */
539
- readonly element: HTMLElement;
540
- /** Current items */
541
- readonly items: readonly T[];
542
- /** Total item count */
543
- readonly total: number;
544
- /** Set items (replaces all) */
545
- setItems: (items: T[]) => void;
546
- /** Append items */
547
- appendItems: (items: T[]) => void;
548
- /** Prepend items */
549
- prependItems: (items: T[]) => void;
550
- /** Update a single item by ID */
551
- updateItem: (id: string | number, updates: Partial<T>) => void;
552
- /** Remove item by ID */
553
- removeItem: (id: string | number) => void;
554
- /** Reload data (clears and re-fetches if using adapter) */
555
- reload: () => Promise<void>;
556
- /** Load data for the currently visible range without resetting (used by restoreScroll) */
557
- loadVisibleRange: () => Promise<void>;
558
- /** Update configuration without recreating the instance */
559
- update: (config: Partial<VListUpdateConfig>) => void;
560
- /** Scroll to specific index */
561
- scrollToIndex: (index: number, alignOrOptions?: "start" | "center" | "end" | ScrollToOptions) => void;
562
- /** Scroll to specific item by ID */
563
- scrollToItem: (id: string | number, alignOrOptions?: "start" | "center" | "end" | ScrollToOptions) => void;
564
- /** Cancel any in-progress smooth scroll animation */
565
- cancelScroll: () => void;
566
- /** Get current scroll position */
567
- getScrollPosition: () => number;
568
- /** Get a snapshot of the current scroll position for save/restore */
569
- getScrollSnapshot: () => ScrollSnapshot;
570
- /** Restore scroll position (and optionally selection) from a snapshot */
571
- restoreScroll: (snapshot: ScrollSnapshot) => void;
572
- /** Select item(s) by ID */
573
- select: (...ids: Array<string | number>) => void;
574
- /** Deselect item(s) by ID */
575
- deselect: (...ids: Array<string | number>) => void;
576
- /** Toggle selection */
577
- toggleSelect: (id: string | number) => void;
578
- /** Select all items */
579
- selectAll: () => void;
580
- /** Clear selection */
581
- clearSelection: () => void;
582
- /** Get selected item IDs */
583
- getSelected: () => Array<string | number>;
584
- /** Get selected items */
585
- getSelectedItems: () => T[];
586
- /** Subscribe to an event */
587
- on: <K extends keyof VListEvents<T>>(event: K, handler: EventHandler<VListEvents<T>[K]>) => Unsubscribe;
588
- /** Unsubscribe from an event */
589
- off: <K extends keyof VListEvents<T>>(event: K, handler: EventHandler<VListEvents<T>[K]>) => void;
590
- /** Destroy the instance and cleanup */
591
- destroy: () => void;
592
- }
593
- /** Internal state */
594
- export interface InternalState<T extends VListItem = VListItem> {
595
- items: T[];
596
- total: number;
597
- viewport: ViewportState;
598
- selection: SelectionState;
599
- isLoading: boolean;
600
- cursor?: string;
601
- hasMore: boolean;
602
- }
603
- /** Rendered item tracking */
604
- export interface RenderedItem {
605
- index: number;
606
- element: HTMLElement;
607
- }
608
371
  //# sourceMappingURL=types.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floor/vlist",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Lightweight, high-performance virtual list with zero dependencies",
5
5
  "author": {
6
6
  "name": "Floor IO",
@@ -52,6 +52,7 @@
52
52
  "dev": "bun run build.ts --watch",
53
53
  "test": "bun test",
54
54
  "typecheck": "tsc --noEmit",
55
+ "size": "bun run scripts/measure-size.ts",
55
56
  "prepublishOnly": "bun run build --types"
56
57
  },
57
58
  "bugs": {