vlist 0.1.2 → 1.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +558 -0
- package/dist/builder/a11y.d.ts +16 -0
- package/dist/builder/api.d.ts +21 -0
- package/dist/builder/context.d.ts +36 -0
- package/dist/builder/core.d.ts +16 -0
- package/dist/builder/data.d.ts +69 -0
- package/dist/builder/dom.d.ts +15 -0
- package/dist/builder/index.d.ts +25 -0
- package/dist/builder/materialize.d.ts +165 -0
- package/dist/builder/pool.d.ts +10 -0
- package/dist/builder/range.d.ts +10 -0
- package/dist/builder/scroll.d.ts +24 -0
- package/dist/builder/types.d.ts +464 -0
- package/dist/builder/velocity.d.ts +23 -0
- package/dist/constants.d.ts +58 -0
- package/dist/events/emitter.d.ts +18 -0
- package/dist/events/index.d.ts +6 -0
- package/dist/features/async/feature.d.ts +72 -0
- package/dist/features/async/index.d.ts +9 -0
- package/dist/features/async/manager.d.ts +103 -0
- package/dist/features/async/placeholder.d.ts +54 -0
- package/dist/features/async/sparse.d.ts +91 -0
- package/dist/features/autosize/feature.d.ts +34 -0
- package/dist/features/autosize/index.d.ts +2 -0
- package/dist/features/grid/feature.d.ts +48 -0
- package/dist/features/grid/index.d.ts +9 -0
- package/dist/features/grid/layout.d.ts +29 -0
- package/dist/features/grid/renderer.d.ts +71 -0
- package/dist/features/grid/types.d.ts +71 -0
- package/dist/features/groups/feature.d.ts +74 -0
- package/dist/features/groups/index.d.ts +10 -0
- package/dist/features/groups/layout.d.ts +47 -0
- package/dist/features/groups/sticky.d.ts +21 -0
- package/dist/features/groups/types.d.ts +86 -0
- package/dist/features/masonry/feature.d.ts +45 -0
- package/dist/features/masonry/index.d.ts +9 -0
- package/dist/features/masonry/layout.d.ts +29 -0
- package/dist/features/masonry/renderer.d.ts +55 -0
- package/dist/features/masonry/types.d.ts +68 -0
- package/dist/features/page/feature.d.ts +53 -0
- package/dist/features/page/index.d.ts +8 -0
- package/dist/features/scale/feature.d.ts +42 -0
- package/dist/features/scale/index.d.ts +10 -0
- package/dist/features/scrollbar/controller.d.ts +121 -0
- package/dist/features/scrollbar/feature.d.ts +60 -0
- package/dist/features/scrollbar/index.d.ts +8 -0
- package/dist/features/scrollbar/scrollbar.d.ts +73 -0
- package/dist/features/selection/feature.d.ts +75 -0
- package/dist/features/selection/index.d.ts +7 -0
- package/dist/features/selection/state.d.ts +115 -0
- package/dist/features/snapshots/feature.d.ts +79 -0
- package/dist/features/snapshots/index.d.ts +9 -0
- package/dist/features/table/feature.d.ts +67 -0
- package/dist/features/table/header.d.ts +49 -0
- package/dist/features/table/index.d.ts +10 -0
- package/dist/features/table/layout.d.ts +26 -0
- package/dist/features/table/renderer.d.ts +72 -0
- package/dist/features/table/types.d.ts +239 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +1 -0
- package/dist/internals.d.ts +21 -0
- package/dist/internals.js +1 -0
- package/dist/rendering/index.d.ts +12 -0
- package/dist/rendering/measured.d.ts +52 -0
- package/dist/rendering/renderer.d.ts +111 -0
- package/dist/rendering/scale.d.ts +121 -0
- package/dist/rendering/scroll.d.ts +23 -0
- package/dist/rendering/sizes.d.ts +63 -0
- package/dist/rendering/sort.d.ts +33 -0
- package/dist/rendering/viewport.d.ts +139 -0
- package/dist/size.json +1 -0
- package/dist/types.d.ts +487 -0
- package/dist/utils/padding.d.ts +38 -0
- package/dist/utils/stats.d.ts +49 -0
- package/dist/vlist-extras.css +1 -0
- package/dist/vlist-grid.css +1 -0
- package/dist/vlist-masonry.css +1 -0
- package/dist/vlist-table.css +1 -0
- package/dist/vlist.css +1 -0
- package/package.json +66 -14
- package/README.MD +0 -80
- package/index.d.ts +0 -3
- package/index.js +0 -188
- package/virtual-scroll.component.d.ts +0 -37
- package/vlist.d.ts +0 -4
- package/vlist.metadata.json +0 -1
- package/vlist.umd.js +0 -189
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist - Custom Scrollbar
|
|
3
|
+
* Provides visual scroll indication for compressed mode where native scrollbar is hidden
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Visual track and thumb
|
|
7
|
+
* - Thumb size proportional to visible content
|
|
8
|
+
* - Click on track to jump to position
|
|
9
|
+
* - Drag thumb to scroll
|
|
10
|
+
* - Auto-hide after idle (optional)
|
|
11
|
+
* - Show on hover with configurable hover zone (optional)
|
|
12
|
+
* - CSS variables for customization
|
|
13
|
+
* - Horizontal mode support (direction-aware axis)
|
|
14
|
+
*/
|
|
15
|
+
/** Scrollbar configuration */
|
|
16
|
+
export interface ScrollbarConfig {
|
|
17
|
+
/** Enable scrollbar (default: true when compressed) */
|
|
18
|
+
enabled?: boolean;
|
|
19
|
+
/** Auto-hide scrollbar after idle (default: true) */
|
|
20
|
+
autoHide?: boolean;
|
|
21
|
+
/** Auto-hide delay in milliseconds (default: 1000) */
|
|
22
|
+
autoHideDelay?: number;
|
|
23
|
+
/** Minimum thumb size in pixels (default: 30) */
|
|
24
|
+
minThumbSize?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Show scrollbar when hovering near the scrollbar edge (default: true).
|
|
27
|
+
* When true, an invisible hover zone is placed along the scrollbar edge.
|
|
28
|
+
* Moving the mouse into this zone reveals the scrollbar; it stays visible
|
|
29
|
+
* as long as the cursor remains over the zone or the track.
|
|
30
|
+
*/
|
|
31
|
+
showOnHover?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Width of the invisible hover zone in pixels (default: 16).
|
|
34
|
+
* Only used when `showOnHover` is true.
|
|
35
|
+
* A wider zone makes the scrollbar easier to discover;
|
|
36
|
+
* a narrower zone avoids interference with content near the edge.
|
|
37
|
+
*/
|
|
38
|
+
hoverZoneWidth?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Show scrollbar when the mouse enters the list viewport (default: true).
|
|
41
|
+
* When false, the scrollbar only appears on scroll or when hovering
|
|
42
|
+
* near the scrollbar edge (if `showOnHover` is true).
|
|
43
|
+
*/
|
|
44
|
+
showOnViewportEnter?: boolean;
|
|
45
|
+
}
|
|
46
|
+
/** Scrollbar instance */
|
|
47
|
+
export interface Scrollbar {
|
|
48
|
+
/** Show the scrollbar */
|
|
49
|
+
show: () => void;
|
|
50
|
+
/** Hide the scrollbar */
|
|
51
|
+
hide: () => void;
|
|
52
|
+
/** Update scrollbar dimensions based on content/container size */
|
|
53
|
+
updateBounds: (totalHeight: number, containerHeight: number) => void;
|
|
54
|
+
/** Update thumb position based on scroll position */
|
|
55
|
+
updatePosition: (scrollTop: number) => void;
|
|
56
|
+
/** Check if scrollbar is visible */
|
|
57
|
+
isVisible: () => boolean;
|
|
58
|
+
/** Destroy and cleanup */
|
|
59
|
+
destroy: () => void;
|
|
60
|
+
}
|
|
61
|
+
/** Callback for scroll position changes */
|
|
62
|
+
export type ScrollCallback = (position: number) => void;
|
|
63
|
+
/**
|
|
64
|
+
* Create a scrollbar instance
|
|
65
|
+
*
|
|
66
|
+
* @param viewport - The viewport element to attach scrollbar to
|
|
67
|
+
* @param onScroll - Callback when scrollbar interaction causes scroll
|
|
68
|
+
* @param config - Scrollbar configuration
|
|
69
|
+
* @param classPrefix - CSS class prefix (default: 'vlist')
|
|
70
|
+
* @param horizontal - Whether the scrollbar is horizontal (default: false)
|
|
71
|
+
*/
|
|
72
|
+
export declare const createScrollbar: (viewport: HTMLElement, onScroll: ScrollCallback, config?: ScrollbarConfig, classPrefix?: string, horizontal?: boolean) => Scrollbar;
|
|
73
|
+
//# sourceMappingURL=scrollbar.d.ts.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist/selection - Builder Feature
|
|
3
|
+
* Wraps the selection domain into a VListFeature for the composable builder.
|
|
4
|
+
*
|
|
5
|
+
* Priority: 50 (runs after renderer and data are ready)
|
|
6
|
+
*
|
|
7
|
+
* Follows the WAI-ARIA APG "Recommended" multi-select listbox model:
|
|
8
|
+
*
|
|
9
|
+
* What it wires:
|
|
10
|
+
* - Click handler on items container — toggles selection on item click
|
|
11
|
+
* - Shift+click in multiple mode selects a contiguous range from last-selected item
|
|
12
|
+
* - Keyboard handler on root — ArrowUp/Down/PageUp/PageDown/Home/End for focus, Space/Enter for toggle
|
|
13
|
+
* - In single mode with followFocus: true, selection follows focus on arrow keys
|
|
14
|
+
* - In multiple mode, arrow keys move focus only; Space/Enter toggles selection
|
|
15
|
+
* - Shift+Arrow toggles the destination item (additive, one at a time)
|
|
16
|
+
* - Shift+Space selects a contiguous range from last-selected item to focused item
|
|
17
|
+
* - Ctrl+Shift+Home/End selects from focused item to first/last item
|
|
18
|
+
* - Ctrl+A / Cmd+A selects all (or deselects all if all are selected)
|
|
19
|
+
* - ARIA attributes — aria-selected on items, aria-activedescendant on root
|
|
20
|
+
* - Live region — announces selection changes to screen readers
|
|
21
|
+
* - Render integration — registers internal getters (_getSelectedIds,
|
|
22
|
+
* _getFocusedIndex) so renderers read real selection state directly,
|
|
23
|
+
* eliminating the previous querySelectorAll-based DOM bypass.
|
|
24
|
+
*
|
|
25
|
+
* Added methods: select, deselect, toggleSelect, selectAll, clearSelection,
|
|
26
|
+
* getSelected, getSelectedItems, selectNext, selectPrevious
|
|
27
|
+
*
|
|
28
|
+
* Internal methods (for renderer integration, not public API):
|
|
29
|
+
* _getSelectedIds — returns the live Set<string|number> of selected IDs
|
|
30
|
+
* _getFocusedIndex — returns the current focused index
|
|
31
|
+
*
|
|
32
|
+
* Added events: item:click, selection:change
|
|
33
|
+
*/
|
|
34
|
+
import type { VListItem, SelectionMode } from "../../types";
|
|
35
|
+
import type { VListFeature } from "../../builder/types";
|
|
36
|
+
/** Selection feature configuration */
|
|
37
|
+
export interface SelectionFeatureConfig {
|
|
38
|
+
/** Selection mode (default: 'single') */
|
|
39
|
+
mode?: SelectionMode;
|
|
40
|
+
/** Initially selected item IDs */
|
|
41
|
+
initial?: Array<string | number>;
|
|
42
|
+
/**
|
|
43
|
+
* When true, arrow keys select the focused item in single mode
|
|
44
|
+
* (WAI-ARIA "selection follows focus" pattern). Default: false.
|
|
45
|
+
* Ignored in multiple mode (focus and selection are always independent).
|
|
46
|
+
*/
|
|
47
|
+
followFocus?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Which item Shift+Arrow toggles in multiple mode.
|
|
50
|
+
* - `'origin'` (default) — toggles the item you're moving FROM (macOS-style).
|
|
51
|
+
* - `'destination'` — toggles the item you're moving TO
|
|
52
|
+
* (WAI-ARIA APG recommended model).
|
|
53
|
+
*
|
|
54
|
+
* Only affects Shift+Arrow keys; has no effect on Shift+Space,
|
|
55
|
+
* Shift+Click, or Ctrl+Shift+Home/End.
|
|
56
|
+
*/
|
|
57
|
+
shiftArrowToggle?: "origin" | "destination";
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Create a selection feature for the builder.
|
|
61
|
+
*
|
|
62
|
+
* ```ts
|
|
63
|
+
* import { vlist } from 'vlist/builder'
|
|
64
|
+
* import { withSelection } from 'vlist/selection'
|
|
65
|
+
*
|
|
66
|
+
* const list = vlist({ ... })
|
|
67
|
+
* .use(withSelection({ mode: 'multiple', initial: ['id-1'] }))
|
|
68
|
+
* .build()
|
|
69
|
+
*
|
|
70
|
+
* list.select('id-2')
|
|
71
|
+
* list.getSelected() // ['id-1', 'id-2']
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export declare const withSelection: <T extends VListItem = VListItem>(config?: SelectionFeatureConfig) => VListFeature<T>;
|
|
75
|
+
//# sourceMappingURL=feature.d.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist - Selection Domain
|
|
3
|
+
* Selection state management
|
|
4
|
+
*/
|
|
5
|
+
export { withSelection, type SelectionFeatureConfig } from "./feature";
|
|
6
|
+
export { createSelectionState, selectItems, deselectItems, toggleSelection, selectAll, clearSelection, setFocusedIndex, moveFocusUp, moveFocusDown, moveFocusToFirst, moveFocusToLast, moveFocusByPage, selectFocused, selectRange, getSelectedIds, getSelectedItems, getSelectionCount, isSelected, isSelectionEmpty, claimPlaceholderSelection, type SelectionState, } from "./state";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist - Selection State Management
|
|
3
|
+
* Pure functions for managing selection state
|
|
4
|
+
*/
|
|
5
|
+
import type { VListItem, SelectionMode, SelectionState } from "../../types";
|
|
6
|
+
export type { SelectionState } from "../../types";
|
|
7
|
+
/**
|
|
8
|
+
* Transfer selection from a placeholder ID to a real item ID.
|
|
9
|
+
*
|
|
10
|
+
* When select-all or shift-click ranges include unloaded async items, the
|
|
11
|
+
* selection Set contains index-based placeholder IDs (`__placeholder_{index}`).
|
|
12
|
+
* When those items load, this function swaps the placeholder entry for the
|
|
13
|
+
* real item ID so the item renders as selected.
|
|
14
|
+
*
|
|
15
|
+
* @returns `true` if a transfer occurred (the item was selected via its placeholder).
|
|
16
|
+
*/
|
|
17
|
+
export declare const claimPlaceholderSelection: (selectedIds: Set<string | number>, index: number, itemId: string | number) => boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Create initial selection state
|
|
20
|
+
* Pure function - no side effects
|
|
21
|
+
*/
|
|
22
|
+
export declare const createSelectionState: (initial?: Array<string | number>) => SelectionState;
|
|
23
|
+
/**
|
|
24
|
+
* Select items by ID
|
|
25
|
+
* Pure function - returns new state
|
|
26
|
+
*/
|
|
27
|
+
export declare const selectItems: (state: SelectionState, ids: Array<string | number>, mode: SelectionMode) => SelectionState;
|
|
28
|
+
/**
|
|
29
|
+
* Deselect items by ID
|
|
30
|
+
* Pure function - returns new state
|
|
31
|
+
*/
|
|
32
|
+
export declare const deselectItems: (state: SelectionState, ids: Array<string | number>) => SelectionState;
|
|
33
|
+
/**
|
|
34
|
+
* Toggle item selection
|
|
35
|
+
* Pure function - returns new state
|
|
36
|
+
*/
|
|
37
|
+
export declare const toggleSelection: (state: SelectionState, id: string | number, mode: SelectionMode) => SelectionState;
|
|
38
|
+
/**
|
|
39
|
+
* Select all items
|
|
40
|
+
* Pure function - returns new state
|
|
41
|
+
*/
|
|
42
|
+
export declare const selectAll: <T extends VListItem>(state: SelectionState, items: T[], mode: SelectionMode) => SelectionState;
|
|
43
|
+
/**
|
|
44
|
+
* Clear all selection
|
|
45
|
+
* Pure function - returns new state
|
|
46
|
+
*/
|
|
47
|
+
export declare const clearSelection: (state: SelectionState) => SelectionState;
|
|
48
|
+
/**
|
|
49
|
+
* Set focused index
|
|
50
|
+
* Mutates state in-place to avoid allocation on hot path
|
|
51
|
+
*/
|
|
52
|
+
export declare const setFocusedIndex: (state: SelectionState, index: number) => SelectionState;
|
|
53
|
+
/**
|
|
54
|
+
* Move focus up
|
|
55
|
+
* Mutates state in-place to avoid allocation on hot path
|
|
56
|
+
* @param delta - Number of items to move by (default 1). Useful for grid layouts where row navigation moves by `columns`.
|
|
57
|
+
*/
|
|
58
|
+
export declare const moveFocusUp: (state: SelectionState, totalItems: number, wrap?: boolean, delta?: number) => SelectionState;
|
|
59
|
+
/**
|
|
60
|
+
* Move focus down
|
|
61
|
+
* Mutates state in-place to avoid allocation on hot path
|
|
62
|
+
* @param delta - Number of items to move by (default 1). Useful for grid layouts where row navigation moves by `columns`.
|
|
63
|
+
*/
|
|
64
|
+
export declare const moveFocusDown: (state: SelectionState, totalItems: number, wrap?: boolean, delta?: number) => SelectionState;
|
|
65
|
+
/**
|
|
66
|
+
* Move focus to first item
|
|
67
|
+
* Mutates state in-place to avoid allocation on hot path
|
|
68
|
+
*/
|
|
69
|
+
export declare const moveFocusToFirst: (state: SelectionState, totalItems: number) => SelectionState;
|
|
70
|
+
/**
|
|
71
|
+
* Move focus to last item
|
|
72
|
+
* Mutates state in-place to avoid allocation on hot path
|
|
73
|
+
*/
|
|
74
|
+
export declare const moveFocusToLast: (state: SelectionState, totalItems: number) => SelectionState;
|
|
75
|
+
/**
|
|
76
|
+
* Move focus by page (for Page Up/Down)
|
|
77
|
+
* Mutates state in-place to avoid allocation on hot path
|
|
78
|
+
*/
|
|
79
|
+
export declare const moveFocusByPage: (state: SelectionState, totalItems: number, pageSize: number, direction: "up" | "down") => SelectionState;
|
|
80
|
+
/**
|
|
81
|
+
* Check if an item is selected
|
|
82
|
+
* Pure function - no side effects
|
|
83
|
+
*/
|
|
84
|
+
export declare const isSelected: (state: SelectionState, id: string | number) => boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Get selected IDs as array
|
|
87
|
+
* Pure function - no side effects
|
|
88
|
+
*/
|
|
89
|
+
export declare const getSelectedIds: (state: SelectionState) => Array<string | number>;
|
|
90
|
+
/**
|
|
91
|
+
* Get selected items using ID lookup (O(k) where k = selected count)
|
|
92
|
+
* Pure function - no side effects
|
|
93
|
+
*/
|
|
94
|
+
export declare const getSelectedItems: <T extends VListItem>(state: SelectionState, getItemById: (id: string | number) => T | undefined) => T[];
|
|
95
|
+
/**
|
|
96
|
+
* Get selection count
|
|
97
|
+
* Pure function - no side effects
|
|
98
|
+
*/
|
|
99
|
+
export declare const getSelectionCount: (state: SelectionState) => number;
|
|
100
|
+
/**
|
|
101
|
+
* Check if selection is empty
|
|
102
|
+
* Pure function - no side effects
|
|
103
|
+
*/
|
|
104
|
+
export declare const isSelectionEmpty: (state: SelectionState) => boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Handle keyboard selection (Space/Enter on focused item)
|
|
107
|
+
* Pure function - returns new state
|
|
108
|
+
*/
|
|
109
|
+
export declare const selectFocused: <T extends VListItem>(state: SelectionState, items: T[], mode: SelectionMode) => SelectionState;
|
|
110
|
+
/**
|
|
111
|
+
* Handle shift+click range selection
|
|
112
|
+
* Pure function - returns new state
|
|
113
|
+
*/
|
|
114
|
+
export declare const selectRange: <T extends VListItem>(state: SelectionState, items: T[], fromIndex: number, toIndex: number, mode: SelectionMode) => SelectionState;
|
|
115
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist/snapshots - Builder Feature
|
|
3
|
+
* Adds scroll save/restore for SPA navigation and tab switching.
|
|
4
|
+
*
|
|
5
|
+
* Priority: 50 (runs last — needs all other features initialized)
|
|
6
|
+
*
|
|
7
|
+
* What it wires:
|
|
8
|
+
* - getScrollSnapshot() — captures current scroll position (item index + sub-pixel offset)
|
|
9
|
+
* - restoreScroll() — restores scroll position from snapshot
|
|
10
|
+
* - autoSave — automatically saves snapshots to sessionStorage on scroll idle
|
|
11
|
+
* and selection change, and auto-restores on next build()
|
|
12
|
+
*
|
|
13
|
+
* Snapshots capture the first visible item index and the pixel offset within
|
|
14
|
+
* that item — not raw scrollTop. This means:
|
|
15
|
+
* - Snapshots survive list recreation (navigate away and back)
|
|
16
|
+
* - Snapshots work correctly with compression (1M+ items)
|
|
17
|
+
* - Snapshots include selection state if selection is installed
|
|
18
|
+
*
|
|
19
|
+
* Added methods: getScrollSnapshot, restoreScroll
|
|
20
|
+
*
|
|
21
|
+
* Helper:
|
|
22
|
+
* - getAutoSaveSnapshot(key) — read a saved snapshot from sessionStorage
|
|
23
|
+
* so that withAsync can derive `autoLoad` and `total` from it.
|
|
24
|
+
*/
|
|
25
|
+
import type { VListItem, ScrollSnapshot } from "../../types";
|
|
26
|
+
import type { VListFeature } from "../../builder/types";
|
|
27
|
+
/** Configuration for the snapshots feature. */
|
|
28
|
+
export interface SnapshotConfig {
|
|
29
|
+
/**
|
|
30
|
+
* Snapshot to restore automatically after `build()` completes.
|
|
31
|
+
*
|
|
32
|
+
* When provided, `restoreScroll(restore)` is scheduled via
|
|
33
|
+
* `queueMicrotask` — it runs right after `.build()` returns but
|
|
34
|
+
* before the browser paints, so the user never sees position 0.
|
|
35
|
+
*
|
|
36
|
+
* ```ts
|
|
37
|
+
* const saved = sessionStorage.getItem('scroll');
|
|
38
|
+
* const snapshot = saved ? JSON.parse(saved) : undefined;
|
|
39
|
+
*
|
|
40
|
+
* const list = vlist({ ... })
|
|
41
|
+
* .use(withAsync({
|
|
42
|
+
* adapter,
|
|
43
|
+
* autoLoad: !snapshot, // skip autoLoad when restoring
|
|
44
|
+
* total: snapshot?.total, // from snapshot — no hardcoded constant needed
|
|
45
|
+
* }))
|
|
46
|
+
* .use(withSnapshots({ restore: snapshot })) // auto-restores after build()
|
|
47
|
+
* .build();
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
restore?: ScrollSnapshot;
|
|
51
|
+
/**
|
|
52
|
+
* Automatically save and restore snapshots via sessionStorage.
|
|
53
|
+
*
|
|
54
|
+
* Pass a string key to enable — snapshots are saved to
|
|
55
|
+
* `sessionStorage` under that key whenever scroll becomes idle
|
|
56
|
+
* or selection changes. On the next `build()`, the snapshot is
|
|
57
|
+
* automatically restored if a matching key exists.
|
|
58
|
+
*
|
|
59
|
+
* This replaces the manual save/restore pattern — no event
|
|
60
|
+
* listeners, debounce timers, or sessionStorage calls needed.
|
|
61
|
+
*
|
|
62
|
+
* ```ts
|
|
63
|
+
* // That's it — save and restore are fully automatic:
|
|
64
|
+
* const list = vlist({ ... })
|
|
65
|
+
* .use(withAsync({ adapter }))
|
|
66
|
+
* .use(withSnapshots({ autoSave: 'my-list-scroll' }))
|
|
67
|
+
* .build();
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* When `autoSave` is set, the `restore` option is ignored —
|
|
71
|
+
* the saved snapshot is read from sessionStorage automatically.
|
|
72
|
+
* The `autoLoad` and `total` options on `withAsync` are also
|
|
73
|
+
* configured automatically (autoLoad is skipped when restoring,
|
|
74
|
+
* and total is read from the snapshot).
|
|
75
|
+
*/
|
|
76
|
+
autoSave?: string;
|
|
77
|
+
}
|
|
78
|
+
export declare const withSnapshots: <T extends VListItem = VListItem>(config?: SnapshotConfig) => VListFeature<T>;
|
|
79
|
+
//# sourceMappingURL=feature.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist/snapshots - Scroll Save/Restore
|
|
3
|
+
* Feature for SPA navigation and tab switching
|
|
4
|
+
*
|
|
5
|
+
* Usage: import { withSnapshots } from 'vlist/snapshots'
|
|
6
|
+
*/
|
|
7
|
+
export { withSnapshots } from "./feature";
|
|
8
|
+
export type { SnapshotConfig } from "./feature";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist/table - Builder Feature
|
|
3
|
+
* Switches from list layout to a data table with columns, resizable headers,
|
|
4
|
+
* sticky header row, and cell-based rendering.
|
|
5
|
+
*
|
|
6
|
+
* Priority: 10 (runs first — replaces the renderer before anything else renders)
|
|
7
|
+
*
|
|
8
|
+
* What it wires:
|
|
9
|
+
* - Replaces render functions — swaps the core render loop with table-aware rendering
|
|
10
|
+
* - Sticky header — creates a fixed header row above the viewport
|
|
11
|
+
* - Column layout — manages column widths, offsets, and resize logic
|
|
12
|
+
* - Resize interaction — drag handles on header column borders
|
|
13
|
+
* - Sort events — click on sortable header emits column:sort
|
|
14
|
+
* - Horizontal scroll sync — header scrolls in sync with the viewport
|
|
15
|
+
* - CSS class — adds .vlist--table to the root element
|
|
16
|
+
* - Variable row heights — supports fixed and function-based heights
|
|
17
|
+
*
|
|
18
|
+
* Critical design: This feature uses ctx.setRenderFns() to completely replace
|
|
19
|
+
* the core's render loop, NOT ctx.replaceRenderer() which is a no-op in the
|
|
20
|
+
* materialize context. This follows the same pattern as withGrid.
|
|
21
|
+
*
|
|
22
|
+
* Restrictions:
|
|
23
|
+
* - Cannot be combined with withGrid or withMasonry (conflicting layout modes)
|
|
24
|
+
* - Cannot be combined with orientation: 'horizontal' (tables are always vertical)
|
|
25
|
+
*
|
|
26
|
+
* Can be combined with:
|
|
27
|
+
* - withSelection (row selection works as-is)
|
|
28
|
+
* - withScrollbar (custom scrollbar)
|
|
29
|
+
* - withAsync (async data loading)
|
|
30
|
+
* - withSnapshots (scroll position save/restore)
|
|
31
|
+
* - withScale (large dataset compression)
|
|
32
|
+
*/
|
|
33
|
+
import type { VListItem } from "../../types";
|
|
34
|
+
import type { VListFeature } from "../../builder/types";
|
|
35
|
+
import type { TableConfig } from "./types";
|
|
36
|
+
/** Table feature configuration — re-exported as TableFeatureConfig */
|
|
37
|
+
export type TableFeatureConfig<T extends VListItem = VListItem> = TableConfig<T>;
|
|
38
|
+
/**
|
|
39
|
+
* Create a table feature for the builder.
|
|
40
|
+
*
|
|
41
|
+
* Switches from list layout to a data table with column headers, resizable
|
|
42
|
+
* columns, and cell-based row rendering.
|
|
43
|
+
*
|
|
44
|
+
* ```ts
|
|
45
|
+
* import { vlist } from 'vlist/builder'
|
|
46
|
+
* import { withTable } from 'vlist/table'
|
|
47
|
+
*
|
|
48
|
+
* const table = vlist({
|
|
49
|
+
* container: '#my-table',
|
|
50
|
+
* item: { height: 40, template: () => '' },
|
|
51
|
+
* items: users,
|
|
52
|
+
* })
|
|
53
|
+
* .use(withTable({
|
|
54
|
+
* columns: [
|
|
55
|
+
* { key: 'name', label: 'Name', width: 200 },
|
|
56
|
+
* { key: 'email', label: 'Email', width: 300 },
|
|
57
|
+
* { key: 'role', label: 'Role', width: 120 },
|
|
58
|
+
* ],
|
|
59
|
+
* rowHeight: 40,
|
|
60
|
+
* headerHeight: 44,
|
|
61
|
+
* resizable: true,
|
|
62
|
+
* }))
|
|
63
|
+
* .build()
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare const withTable: <T extends VListItem = VListItem>(config: TableFeatureConfig<T>) => VListFeature<T>;
|
|
67
|
+
//# sourceMappingURL=feature.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist/table - Header
|
|
3
|
+
* Manages the sticky header row that sits above the scrolling viewport.
|
|
4
|
+
*
|
|
5
|
+
* The header is a positioned DOM element inserted into the vlist root
|
|
6
|
+
* container (above the viewport, like the sticky group header). It contains
|
|
7
|
+
* one cell per column, each showing the column label. Resize handles are
|
|
8
|
+
* rendered at the right edge of each resizable column's header cell.
|
|
9
|
+
*
|
|
10
|
+
* Layout:
|
|
11
|
+
* .vlist (root, position: relative)
|
|
12
|
+
* ├── .vlist-table-header (position: absolute, top: 0, z-index: 5)
|
|
13
|
+
* │ ├── .vlist-table-header-cell [col 0]
|
|
14
|
+
* │ │ ├── .vlist-table-header-content (label)
|
|
15
|
+
* │ │ ├── .vlist-table-header-sort (sort indicator)
|
|
16
|
+
* │ │ └── .vlist-table-header-resize (drag handle)
|
|
17
|
+
* │ ├── .vlist-table-header-cell [col 1]
|
|
18
|
+
* │ │ └── ...
|
|
19
|
+
* │ └── ...
|
|
20
|
+
* └── .vlist-viewport (scrollable, top offset by headerHeight)
|
|
21
|
+
*
|
|
22
|
+
* Resize interaction:
|
|
23
|
+
* mousedown on handle → pointermove updates column width → pointerup commits
|
|
24
|
+
* During drag, a class is added to the root for cursor override.
|
|
25
|
+
*
|
|
26
|
+
* Sort interaction:
|
|
27
|
+
* click on a sortable header cell emits column:sort via the provided callback.
|
|
28
|
+
* The header renders a visual indicator (▲/▼) for the active sort column.
|
|
29
|
+
*
|
|
30
|
+
* Horizontal scroll sync:
|
|
31
|
+
* The header's scrollLeft is kept in sync with the viewport's scrollLeft
|
|
32
|
+
* via the `syncScroll` method, called from the feature's afterScroll hook.
|
|
33
|
+
*/
|
|
34
|
+
import type { VListItem } from "../../types";
|
|
35
|
+
import type { TableHeader, ColumnSortEvent, ColumnClickEvent } from "./types";
|
|
36
|
+
/**
|
|
37
|
+
* Create a TableHeader instance.
|
|
38
|
+
*
|
|
39
|
+
* @param root - The vlist root element (.vlist)
|
|
40
|
+
* @param viewport - The vlist viewport element (for scroll sync)
|
|
41
|
+
* @param headerHeight - Height of the header row in pixels
|
|
42
|
+
* @param classPrefix - CSS class prefix (default: 'vlist')
|
|
43
|
+
* @param onResize - Callback when a column is resized (receives column index and new width)
|
|
44
|
+
* @param onSort - Callback when a sortable header is clicked
|
|
45
|
+
* @param onClick - Callback when any header cell is clicked
|
|
46
|
+
* @returns TableHeader instance
|
|
47
|
+
*/
|
|
48
|
+
export declare const createTableHeader: <T extends VListItem = VListItem>(root: HTMLElement, viewport: HTMLElement, headerHeight: number, classPrefix: string, onResize: (columnIndex: number, newWidth: number) => void, onSort?: (event: ColumnSortEvent) => void, onClick?: (event: ColumnClickEvent) => void) => TableHeader<T>;
|
|
49
|
+
//# sourceMappingURL=header.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist - Table Domain
|
|
3
|
+
* Data table layout with columns, resizable headers, and cell rendering
|
|
4
|
+
*/
|
|
5
|
+
export { withTable, type TableFeatureConfig } from "./feature";
|
|
6
|
+
export { createTableLayout } from "./layout";
|
|
7
|
+
export { createTableHeader } from "./header";
|
|
8
|
+
export { createTableRenderer, type TableRendererInstance } from "./renderer";
|
|
9
|
+
export type { TableConfig, TableColumn, TableLayout, TableHeader, TableRenderer, ResolvedColumn, ColumnResizeEvent, ColumnSortEvent, ColumnClickEvent, } from "./types";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist/table - Layout
|
|
3
|
+
* Manages column widths, offsets, and resize operations.
|
|
4
|
+
*
|
|
5
|
+
* Column width resolution strategy:
|
|
6
|
+
* 1. Columns with explicit `width` get their requested width (clamped to min/max)
|
|
7
|
+
* 2. Remaining container space is distributed equally among columns without `width`
|
|
8
|
+
* 3. If all columns have explicit widths and total < container, no stretching occurs
|
|
9
|
+
* 4. If total column width > container, the table scrolls horizontally
|
|
10
|
+
*
|
|
11
|
+
* All offset calculations are O(n) where n = number of columns (typically small).
|
|
12
|
+
* Resize operations recalculate offsets for columns after the resized one.
|
|
13
|
+
*/
|
|
14
|
+
import type { VListItem } from "../../types";
|
|
15
|
+
import type { TableColumn, TableLayout } from "./types";
|
|
16
|
+
/**
|
|
17
|
+
* Create a TableLayout instance.
|
|
18
|
+
*
|
|
19
|
+
* @param columnDefs - Column definitions from config
|
|
20
|
+
* @param globalMinWidth - Default min column width (from TableConfig.minColumnWidth)
|
|
21
|
+
* @param globalMaxWidth - Default max column width (from TableConfig.maxColumnWidth)
|
|
22
|
+
* @param globalResizable - Default resizable flag (from TableConfig.resizable)
|
|
23
|
+
* @returns TableLayout with column resolution and resize capabilities
|
|
24
|
+
*/
|
|
25
|
+
export declare const createTableLayout: <T extends VListItem = VListItem>(columnDefs: TableColumn<T>[], globalMinWidth?: number, globalMaxWidth?: number, globalResizable?: boolean) => TableLayout<T>;
|
|
26
|
+
//# sourceMappingURL=layout.d.ts.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist/table - Renderer
|
|
3
|
+
* Renders virtualized rows with cell-based layout within the virtual scroll container.
|
|
4
|
+
*
|
|
5
|
+
* Each row is an absolutely positioned element (like the list renderer),
|
|
6
|
+
* containing child elements for each cell. Cells are sized and positioned
|
|
7
|
+
* according to the resolved column layout from TableLayout.
|
|
8
|
+
*
|
|
9
|
+
* Key design decisions:
|
|
10
|
+
* - Rows are the unit of virtualization (same as list mode — 1:1 with items)
|
|
11
|
+
* - Each row contains N cell elements (one per column)
|
|
12
|
+
* - Row positioning is translateY-based (from the size cache)
|
|
13
|
+
* - Cell positioning uses absolute left + width from the column layout
|
|
14
|
+
* - Element pooling avoids createElement cost (row-level pooling)
|
|
15
|
+
* - Change tracking skips template re-evaluation when data + state unchanged
|
|
16
|
+
* - Release grace period prevents boundary thrashing (hover blink, transition replay)
|
|
17
|
+
* - DocumentFragment batched insertion for new elements
|
|
18
|
+
*
|
|
19
|
+
* Performance:
|
|
20
|
+
* - O(1) Set-based visibility diffing (not O(n) .some())
|
|
21
|
+
* - Template re-evaluation skipped when item id + selection/focus state unchanged
|
|
22
|
+
* - Position update skipped when coordinates unchanged (position tracking)
|
|
23
|
+
* - Cell widths are only updated when column layout changes (not every scroll frame)
|
|
24
|
+
* - Released elements removed from DOM immediately, pooled for reuse
|
|
25
|
+
*
|
|
26
|
+
* DOM structure per row:
|
|
27
|
+
* .vlist-item.vlist-table-row (position: absolute, translateY)
|
|
28
|
+
* ├── .vlist-table-cell [col 0] (position: absolute, left, width)
|
|
29
|
+
* ├── .vlist-table-cell [col 1]
|
|
30
|
+
* └── ...
|
|
31
|
+
*/
|
|
32
|
+
import type { VListItem, Range } from "../../types";
|
|
33
|
+
import type { SizeCache } from "../../rendering/sizes";
|
|
34
|
+
import type { CompressionContext } from "../../rendering/renderer";
|
|
35
|
+
import type { TableLayout, TableColumn } from "./types";
|
|
36
|
+
/** Table renderer instance */
|
|
37
|
+
export interface TableRendererInstance<T extends VListItem = VListItem> {
|
|
38
|
+
/** Render rows for a range, with cell-based layout */
|
|
39
|
+
render: (items: T[], range: Range, selectedIds: Set<string | number>, focusedIndex: number, compressionCtx?: CompressionContext) => void;
|
|
40
|
+
/** Update item positions (for compressed scrolling) */
|
|
41
|
+
updatePositions: (compressionCtx: CompressionContext) => void;
|
|
42
|
+
/** Update a single row (e.g., after selection change) */
|
|
43
|
+
updateItem: (index: number, item: T, isSelected: boolean, isFocused: boolean) => void;
|
|
44
|
+
/** Update only CSS classes on a rendered row */
|
|
45
|
+
updateItemClasses: (index: number, isSelected: boolean, isFocused: boolean) => void;
|
|
46
|
+
/** Get rendered row element by item index */
|
|
47
|
+
getElement: (index: number) => HTMLElement | undefined;
|
|
48
|
+
/** Update cell positions and widths after column resize */
|
|
49
|
+
updateColumnLayout: (layout: TableLayout<T>) => void;
|
|
50
|
+
/** Set the group header check function (called by withGroups integration) */
|
|
51
|
+
setGroupHeaderFn: (fn: ((item: T) => boolean) | null, template: ((key: string, groupIndex: number) => HTMLElement | string) | null) => void;
|
|
52
|
+
/** Clear all rendered rows */
|
|
53
|
+
clear: () => void;
|
|
54
|
+
/** Destroy renderer and cleanup */
|
|
55
|
+
destroy: () => void;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Create a TableRenderer instance.
|
|
59
|
+
*
|
|
60
|
+
* @param container - The .vlist-items container element
|
|
61
|
+
* @param sizeCache - Size cache for row offset lookups
|
|
62
|
+
* @param layout - Table layout for column widths/offsets
|
|
63
|
+
* @param columns - Column definitions (for cell templates)
|
|
64
|
+
* @param classPrefix - CSS class prefix
|
|
65
|
+
* @param ariaIdPrefix - Prefix for ARIA IDs
|
|
66
|
+
* @param columnBorders - Whether to show vertical borders between cells
|
|
67
|
+
* @param rowBorders - Whether to show horizontal borders between rows
|
|
68
|
+
* @param getTotalItems - Function to get total item count (for ARIA)
|
|
69
|
+
* @returns TableRendererInstance
|
|
70
|
+
*/
|
|
71
|
+
export declare const createTableRenderer: <T extends VListItem = VListItem>(container: HTMLElement, getSizeCache: () => SizeCache, layout: TableLayout<T>, _columns: TableColumn<T>[], classPrefix: string, ariaIdPrefix: string, getTotalItems: () => number, striped?: boolean | "data" | "even" | "odd", stripeIndexFn?: () => (index: number) => number) => TableRendererInstance<T>;
|
|
72
|
+
//# sourceMappingURL=renderer.d.ts.map
|