vlist 1.7.3 → 1.7.5
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 +7 -7
- package/README.md +10 -6
- package/dist/builder/api.d.ts +1 -1
- package/dist/builder/data.d.ts +1 -0
- package/dist/builder/materialize.d.ts +1 -0
- package/dist/builder/types.d.ts +1 -0
- package/dist/features/groups/async-bridge.d.ts +68 -0
- package/dist/features/groups/feature.d.ts +2 -1
- package/dist/features/groups/index.d.ts +1 -0
- package/dist/features/groups/layout.d.ts +1 -1
- package/dist/features/groups/types.d.ts +4 -1
- package/dist/features/selection/feature.d.ts +8 -0
- package/dist/index.js +1 -1
- package/dist/internals.js +1 -1
- package/dist/size.json +1 -1
- package/dist/types.d.ts +37 -1
- package/dist/vlist-table.css +1 -1
- package/dist/vlist.css +1 -1
- package/package.json +1 -1
package/README.github.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The virtual list library for every framework. Accessible by default, batteries-included, with composable features — in 10.5 KB.
|
|
4
4
|
|
|
5
|
-
**v1.7.
|
|
5
|
+
**v1.7.5** — [Changelog](./CHANGELOG.md)
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/vlist)
|
|
8
8
|
[](https://github.com/floor/vlist/actions/workflows/ci.yml)
|
|
@@ -95,18 +95,18 @@ const list = vlist({
|
|
|
95
95
|
| Feature | Size | Description |
|
|
96
96
|
|---------|------|-------------|
|
|
97
97
|
| **Base** | 10.6 KB | Virtualization, ARIA, keyboard nav, gap, padding |
|
|
98
|
-
| `withAsync()` | +4.
|
|
99
|
-
| `withSelection()` | +
|
|
98
|
+
| `withAsync()` | +4.7 KB | Lazy loading with velocity-aware fetching |
|
|
99
|
+
| `withSelection()` | +3.0 KB | Single/multiple selection with 2D keyboard nav |
|
|
100
100
|
| `withScale()` | +3.7 KB | 1M+ items via scroll compression |
|
|
101
|
-
| `withGroups()` | +2
|
|
101
|
+
| `withGroups()` | +4.2 KB | Sticky/inline headers with async group discovery |
|
|
102
102
|
| `withAutoSize()` | +0.9 KB | Auto-measure items via ResizeObserver |
|
|
103
|
-
| `withScrollbar()` | +1.
|
|
103
|
+
| `withScrollbar()` | +1.7 KB | Custom scrollbar UI |
|
|
104
104
|
| `withGrid()` | +3.9 KB | 2D grid layout |
|
|
105
|
-
| `withMasonry()` | +3.
|
|
105
|
+
| `withMasonry()` | +3.3 KB | Pinterest-style masonry with lane-aware keyboard nav |
|
|
106
106
|
| `withTable()` | +5.5 KB | Data table with columns, resize, sort |
|
|
107
107
|
| `withPage()` | +0.8 KB | Window-level scrolling |
|
|
108
108
|
| `withSortable()` | +3.0 KB | Drag-and-drop reordering with auto-scroll |
|
|
109
|
-
| `withSnapshots()` | +
|
|
109
|
+
| `withSnapshots()` | +1.1 KB | Scroll position save/restore |
|
|
110
110
|
|
|
111
111
|
## Examples
|
|
112
112
|
|
package/README.md
CHANGED
|
@@ -55,18 +55,18 @@ const list = vlist({ container: '#app', items, item: { height: 200, template: re
|
|
|
55
55
|
| Feature | Size | Description |
|
|
56
56
|
|---------|------|-------------|
|
|
57
57
|
| **Base** | 10.6 KB | Virtualization, ARIA, keyboard nav, gap, padding |
|
|
58
|
-
| `withAsync()` | +4.
|
|
59
|
-
| `withSelection()` | +
|
|
58
|
+
| `withAsync()` | +4.7 KB | Lazy loading with velocity-aware fetching |
|
|
59
|
+
| `withSelection()` | +3.0 KB | Single/multiple selection with 2D keyboard nav |
|
|
60
60
|
| `withScale()` | +3.7 KB | 1M+ items via scroll compression |
|
|
61
|
-
| `withGroups()` | +2
|
|
61
|
+
| `withGroups()` | +4.2 KB | Sticky/inline headers with async group discovery |
|
|
62
62
|
| `withAutoSize()` | +0.9 KB | Auto-measure items via ResizeObserver |
|
|
63
|
-
| `withScrollbar()` | +1.
|
|
63
|
+
| `withScrollbar()` | +1.7 KB | Custom scrollbar UI |
|
|
64
64
|
| `withGrid()` | +3.9 KB | 2D grid layout |
|
|
65
|
-
| `withMasonry()` | +3.
|
|
65
|
+
| `withMasonry()` | +3.3 KB | Pinterest-style masonry with lane-aware keyboard nav |
|
|
66
66
|
| `withTable()` | +5.5 KB | Data table with columns, resize, sort |
|
|
67
67
|
| `withPage()` | +0.8 KB | Window-level scrolling |
|
|
68
68
|
| `withSortable()` | +3.0 KB | Drag-and-drop reordering with auto-scroll |
|
|
69
|
-
| `withSnapshots()` | +
|
|
69
|
+
| `withSnapshots()` | +1.1 KB | Scroll position save/restore |
|
|
70
70
|
|
|
71
71
|
## Framework Adapters
|
|
72
72
|
|
|
@@ -84,3 +84,7 @@ const list = vlist({ container: '#app', items, item: { height: 200, template: re
|
|
|
84
84
|
## License
|
|
85
85
|
|
|
86
86
|
[MIT](LICENSE) — Built by [Floor IO](https://floor.io)
|
|
87
|
+
|
|
88
|
+
## Acknowledgments
|
|
89
|
+
|
|
90
|
+
Thanks to Alexander Klaiber for graciously transferring the `vlist` package name on npm.
|
package/dist/builder/api.d.ts
CHANGED
|
@@ -17,5 +17,5 @@ import type { DOMStructure } from "./dom";
|
|
|
17
17
|
import type { createElementPool } from "./pool";
|
|
18
18
|
import type { BuilderContext, VListFeature, VList } from "./types";
|
|
19
19
|
import type { MRefs } from "./materialize";
|
|
20
|
-
export declare const createApi: <T extends VListItem = VListItem>($: MRefs<T>, dom: DOMStructure, emitter: Emitter<VListEvents<T>>, rendered: Map<number, HTMLElement>, pool: ReturnType<typeof createElementPool>, methods: Map<string, Function>, sortedFeatures: VListFeature<T>[], destroyHandlers: Array<() => void>, ctx: BuilderContext<T>, isReverse: boolean, wrapEnabled: boolean, handleClick: (event: MouseEvent) => void, handleDblClick: (event: MouseEvent) => void, handleKeydown: (event: KeyboardEvent) => void, onScrollFrame: () => void, resizeObserver: ResizeObserver, disconnectItemObserver: () => void, clearIdleTimer: () => void) => VList<T>;
|
|
20
|
+
export declare const createApi: <T extends VListItem = VListItem>($: MRefs<T>, dom: DOMStructure, emitter: Emitter<VListEvents<T>>, rendered: Map<number, HTMLElement>, pool: ReturnType<typeof createElementPool>, methods: Map<string, Function>, sortedFeatures: VListFeature<T>[], destroyHandlers: Array<() => void>, ctx: BuilderContext<T>, isReverse: boolean, wrapEnabled: boolean, handleClick: (event: MouseEvent) => void, handleDblClick: (event: MouseEvent) => void, handleContextMenu: (event: MouseEvent) => void, handleKeydown: (event: KeyboardEvent) => void, onScrollFrame: () => void, resizeObserver: ResizeObserver, disconnectItemObserver: () => void, clearIdleTimer: () => void) => VList<T>;
|
|
21
21
|
//# sourceMappingURL=api.d.ts.map
|
package/dist/builder/data.d.ts
CHANGED
|
@@ -36,6 +36,7 @@ export interface SimpleDataManager<T extends VListItem = VListItem> {
|
|
|
36
36
|
getStorage: () => unknown;
|
|
37
37
|
getPlaceholders: () => unknown;
|
|
38
38
|
getItem: (index: number) => T | undefined;
|
|
39
|
+
getIndexById: (id: string | number) => number;
|
|
39
40
|
isItemLoaded: (index: number) => boolean;
|
|
40
41
|
getItemsInRange: (start: number, end: number) => T[];
|
|
41
42
|
setTotal: (total: number) => void;
|
|
@@ -146,6 +146,7 @@ export interface MDeps<T extends VListItem = VListItem> {
|
|
|
146
146
|
readonly idleHandlers: Array<() => void>;
|
|
147
147
|
readonly afterScroll: Array<(scrollPosition: number, direction: string) => void>;
|
|
148
148
|
readonly clickHandlers: Array<(event: MouseEvent) => void>;
|
|
149
|
+
readonly contextMenuHandlers: Array<(event: MouseEvent) => void>;
|
|
149
150
|
readonly keydownHandlers: Array<(event: KeyboardEvent) => void>;
|
|
150
151
|
readonly resizeHandlers: Array<(width: number, height: number) => void>;
|
|
151
152
|
readonly destroyHandlers: Array<() => void>;
|
package/dist/builder/types.d.ts
CHANGED
|
@@ -240,6 +240,7 @@ export interface BuilderContext<T extends VListItem = VListItem> {
|
|
|
240
240
|
* These are attached as DOM event listeners during .build().
|
|
241
241
|
*/
|
|
242
242
|
clickHandlers: Array<(event: MouseEvent) => void>;
|
|
243
|
+
contextMenuHandlers: Array<(event: MouseEvent) => void>;
|
|
243
244
|
keydownHandlers: Array<(event: KeyboardEvent) => void>;
|
|
244
245
|
resizeHandlers: Array<(width: number, height: number) => void>;
|
|
245
246
|
contentSizeHandlers: Array<() => void>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist - Async Group Bridge
|
|
3
|
+
* Virtual group layer that bridges withAsync's sparse data manager with
|
|
4
|
+
* withGroups' layout system.
|
|
5
|
+
*
|
|
6
|
+
* Instead of physically inserting header pseudo-items into the data array
|
|
7
|
+
* (which is impossible with sparse storage), this bridge:
|
|
8
|
+
* - Observes items as they load via onItemsLoaded callbacks
|
|
9
|
+
* - Discovers group boundaries incrementally from loaded data
|
|
10
|
+
* - Maps between "data indices" (what the async manager knows) and
|
|
11
|
+
* "layout indices" (what the renderer sees, including header slots)
|
|
12
|
+
* - Provides header/item resolution at render time
|
|
13
|
+
*
|
|
14
|
+
* Group boundaries are only computed for contiguous loaded ranges.
|
|
15
|
+
* Unloaded gaps have no headers — placeholders render as regular items.
|
|
16
|
+
* When gaps fill in, boundaries are recomputed.
|
|
17
|
+
*/
|
|
18
|
+
import type { VListItem } from "../../types";
|
|
19
|
+
import type { GroupBoundary, GroupHeaderItem } from "./types";
|
|
20
|
+
/** Configuration for the async group bridge */
|
|
21
|
+
export interface AsyncBridgeConfig {
|
|
22
|
+
/** Determine group key for an item */
|
|
23
|
+
getGroupForIndex: (index: number, item?: any) => string;
|
|
24
|
+
/** Header height in pixels (resolved for current orientation) */
|
|
25
|
+
headerHeight: number;
|
|
26
|
+
}
|
|
27
|
+
/** Async group bridge instance */
|
|
28
|
+
export interface AsyncGroupBridge {
|
|
29
|
+
/** Process newly loaded items — updates group boundaries */
|
|
30
|
+
onItemsLoaded(items: VListItem[], offset: number, total: number): void;
|
|
31
|
+
/** Total layout entries (data items + discovered headers) */
|
|
32
|
+
readonly totalEntries: number;
|
|
33
|
+
/** Number of discovered groups */
|
|
34
|
+
readonly groupCount: number;
|
|
35
|
+
/** All discovered group boundaries */
|
|
36
|
+
readonly groups: readonly GroupBoundary[];
|
|
37
|
+
/** Check if a layout index is a group header */
|
|
38
|
+
isHeader(layoutIndex: number): boolean;
|
|
39
|
+
/** Get the group header item at a layout index (undefined if not a header) */
|
|
40
|
+
getHeaderItem(layoutIndex: number): GroupHeaderItem | undefined;
|
|
41
|
+
/** Map layout index → data index (-1 if it's a header) */
|
|
42
|
+
layoutToDataIndex(layoutIndex: number): number;
|
|
43
|
+
/** Map data index → layout index */
|
|
44
|
+
dataToLayoutIndex(dataIndex: number): number;
|
|
45
|
+
/** Get header height for a group */
|
|
46
|
+
getHeaderHeight(groupIndex: number): number;
|
|
47
|
+
/** Get the group boundary at a layout index */
|
|
48
|
+
getGroupAtLayoutIndex(layoutIndex: number): GroupBoundary;
|
|
49
|
+
/** Get the group boundary at a data index */
|
|
50
|
+
getGroupAtDataIndex(dataIndex: number): GroupBoundary;
|
|
51
|
+
/** Remove item at data index — shifts group keys and rebuilds */
|
|
52
|
+
removeAt(dataIndex: number): void;
|
|
53
|
+
/** Reset all state (e.g. on reload) */
|
|
54
|
+
reset(): void;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Create an async group bridge.
|
|
58
|
+
*
|
|
59
|
+
* The bridge discovers group boundaries incrementally as items load from
|
|
60
|
+
* the async adapter. It maintains a mapping between data indices (sparse
|
|
61
|
+
* storage) and layout indices (data items + group headers).
|
|
62
|
+
*
|
|
63
|
+
* @param config - Bridge configuration
|
|
64
|
+
* @param getItem - Item accessor from the async data manager
|
|
65
|
+
* @param isItemLoaded - Check if an item at index is loaded (not placeholder)
|
|
66
|
+
*/
|
|
67
|
+
export declare const createAsyncGroupBridge: (config: AsyncBridgeConfig, _getItem: (index: number) => VListItem | undefined, _isItemLoaded: (index: number) => boolean) => AsyncGroupBridge;
|
|
68
|
+
//# sourceMappingURL=async-bridge.d.ts.map
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*
|
|
18
18
|
* Can be combined with:
|
|
19
19
|
* - withGrid for grouped 2D layouts
|
|
20
|
+
* - withAsync for grouped lists with async pagination
|
|
20
21
|
* - reverse: true (sticky header shows current section as you scroll up through history)
|
|
21
22
|
* - orientation: 'horizontal' (sticky headers stick to left edge, push left when next header approaches)
|
|
22
23
|
*/
|
|
@@ -25,7 +26,7 @@ import type { VListFeature } from "../../builder/types";
|
|
|
25
26
|
/** Groups feature configuration */
|
|
26
27
|
export interface GroupsFeatureConfig {
|
|
27
28
|
/** Returns group key for item at index (required) */
|
|
28
|
-
getGroupForIndex: (index: number) => string;
|
|
29
|
+
getGroupForIndex: (index: number, item?: any) => string;
|
|
29
30
|
/** Group header configuration — mirrors the `item` config shape */
|
|
30
31
|
header?: {
|
|
31
32
|
/** Header size in pixels — vertical scrolling (default) */
|
|
@@ -7,4 +7,5 @@ export type { GroupsConfig, GroupBoundary, LayoutEntry, GroupHeaderItem, GroupLa
|
|
|
7
7
|
export { isGroupHeader } from "./types";
|
|
8
8
|
export { createGroupLayout, buildLayoutItems, createGroupedSizeFn, } from "./layout";
|
|
9
9
|
export { createStickyHeader } from "./sticky";
|
|
10
|
+
export { createAsyncGroupBridge, type AsyncBridgeConfig, type AsyncGroupBridge } from "./async-bridge";
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -43,5 +43,5 @@ export declare const createGroupedSizeFn: (layout: GroupLayout, itemSize: number
|
|
|
43
43
|
* @param itemCount - Number of data items
|
|
44
44
|
* @param config - Groups configuration
|
|
45
45
|
*/
|
|
46
|
-
export declare const createGroupLayout: (itemCount: number, config: GroupsConfig) => GroupLayout;
|
|
46
|
+
export declare const createGroupLayout: (itemCount: number, config: GroupsConfig, getItem?: (index: number) => any) => GroupLayout;
|
|
47
47
|
//# sourceMappingURL=layout.d.ts.map
|
|
@@ -67,8 +67,11 @@ export interface GroupLayout {
|
|
|
67
67
|
/**
|
|
68
68
|
* Rebuild the layout from scratch.
|
|
69
69
|
* Call when items change (setItems, append, prepend, remove, etc.)
|
|
70
|
+
*
|
|
71
|
+
* @param itemCount - Number of data items
|
|
72
|
+
* @param getItem - Optional item accessor for passing items to getGroupForIndex
|
|
70
73
|
*/
|
|
71
|
-
rebuild: (itemCount: number) => void;
|
|
74
|
+
rebuild: (itemCount: number, getItem?: (index: number) => any) => void;
|
|
72
75
|
}
|
|
73
76
|
/** Sticky header manager */
|
|
74
77
|
export interface StickyHeader {
|
|
@@ -63,6 +63,14 @@ export interface SelectionFeatureConfig {
|
|
|
63
63
|
* focus indicator doubles as a "current item" marker.
|
|
64
64
|
*/
|
|
65
65
|
focusOnClick?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* How right-click affects selection (default: 'select').
|
|
68
|
+
* - `'select'` — if the item is not selected, clear selection and select it;
|
|
69
|
+
* if already selected, keep current selection (file explorer behavior).
|
|
70
|
+
* - `'keep'` — never change selection on right-click.
|
|
71
|
+
* - `false` — don't register a contextmenu handler at all.
|
|
72
|
+
*/
|
|
73
|
+
contextMenu?: "select" | "keep" | false;
|
|
66
74
|
}
|
|
67
75
|
/**
|
|
68
76
|
* Create a selection feature for the builder.
|