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.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +558 -0
  3. package/dist/builder/a11y.d.ts +16 -0
  4. package/dist/builder/api.d.ts +21 -0
  5. package/dist/builder/context.d.ts +36 -0
  6. package/dist/builder/core.d.ts +16 -0
  7. package/dist/builder/data.d.ts +69 -0
  8. package/dist/builder/dom.d.ts +15 -0
  9. package/dist/builder/index.d.ts +25 -0
  10. package/dist/builder/materialize.d.ts +165 -0
  11. package/dist/builder/pool.d.ts +10 -0
  12. package/dist/builder/range.d.ts +10 -0
  13. package/dist/builder/scroll.d.ts +24 -0
  14. package/dist/builder/types.d.ts +464 -0
  15. package/dist/builder/velocity.d.ts +23 -0
  16. package/dist/constants.d.ts +58 -0
  17. package/dist/events/emitter.d.ts +18 -0
  18. package/dist/events/index.d.ts +6 -0
  19. package/dist/features/async/feature.d.ts +72 -0
  20. package/dist/features/async/index.d.ts +9 -0
  21. package/dist/features/async/manager.d.ts +103 -0
  22. package/dist/features/async/placeholder.d.ts +54 -0
  23. package/dist/features/async/sparse.d.ts +91 -0
  24. package/dist/features/autosize/feature.d.ts +34 -0
  25. package/dist/features/autosize/index.d.ts +2 -0
  26. package/dist/features/grid/feature.d.ts +48 -0
  27. package/dist/features/grid/index.d.ts +9 -0
  28. package/dist/features/grid/layout.d.ts +29 -0
  29. package/dist/features/grid/renderer.d.ts +71 -0
  30. package/dist/features/grid/types.d.ts +71 -0
  31. package/dist/features/groups/feature.d.ts +74 -0
  32. package/dist/features/groups/index.d.ts +10 -0
  33. package/dist/features/groups/layout.d.ts +47 -0
  34. package/dist/features/groups/sticky.d.ts +21 -0
  35. package/dist/features/groups/types.d.ts +86 -0
  36. package/dist/features/masonry/feature.d.ts +45 -0
  37. package/dist/features/masonry/index.d.ts +9 -0
  38. package/dist/features/masonry/layout.d.ts +29 -0
  39. package/dist/features/masonry/renderer.d.ts +55 -0
  40. package/dist/features/masonry/types.d.ts +68 -0
  41. package/dist/features/page/feature.d.ts +53 -0
  42. package/dist/features/page/index.d.ts +8 -0
  43. package/dist/features/scale/feature.d.ts +42 -0
  44. package/dist/features/scale/index.d.ts +10 -0
  45. package/dist/features/scrollbar/controller.d.ts +121 -0
  46. package/dist/features/scrollbar/feature.d.ts +60 -0
  47. package/dist/features/scrollbar/index.d.ts +8 -0
  48. package/dist/features/scrollbar/scrollbar.d.ts +73 -0
  49. package/dist/features/selection/feature.d.ts +75 -0
  50. package/dist/features/selection/index.d.ts +7 -0
  51. package/dist/features/selection/state.d.ts +115 -0
  52. package/dist/features/snapshots/feature.d.ts +79 -0
  53. package/dist/features/snapshots/index.d.ts +9 -0
  54. package/dist/features/table/feature.d.ts +67 -0
  55. package/dist/features/table/header.d.ts +49 -0
  56. package/dist/features/table/index.d.ts +10 -0
  57. package/dist/features/table/layout.d.ts +26 -0
  58. package/dist/features/table/renderer.d.ts +72 -0
  59. package/dist/features/table/types.d.ts +239 -0
  60. package/dist/index.d.ts +28 -0
  61. package/dist/index.js +1 -0
  62. package/dist/internals.d.ts +21 -0
  63. package/dist/internals.js +1 -0
  64. package/dist/rendering/index.d.ts +12 -0
  65. package/dist/rendering/measured.d.ts +52 -0
  66. package/dist/rendering/renderer.d.ts +111 -0
  67. package/dist/rendering/scale.d.ts +121 -0
  68. package/dist/rendering/scroll.d.ts +23 -0
  69. package/dist/rendering/sizes.d.ts +63 -0
  70. package/dist/rendering/sort.d.ts +33 -0
  71. package/dist/rendering/viewport.d.ts +139 -0
  72. package/dist/size.json +1 -0
  73. package/dist/types.d.ts +487 -0
  74. package/dist/utils/padding.d.ts +38 -0
  75. package/dist/utils/stats.d.ts +49 -0
  76. package/dist/vlist-extras.css +1 -0
  77. package/dist/vlist-grid.css +1 -0
  78. package/dist/vlist-masonry.css +1 -0
  79. package/dist/vlist-table.css +1 -0
  80. package/dist/vlist.css +1 -0
  81. package/package.json +66 -14
  82. package/README.MD +0 -80
  83. package/index.d.ts +0 -3
  84. package/index.js +0 -188
  85. package/virtual-scroll.component.d.ts +0 -37
  86. package/vlist.d.ts +0 -4
  87. package/vlist.metadata.json +0 -1
  88. package/vlist.umd.js +0 -189
@@ -0,0 +1,103 @@
1
+ /**
2
+ * vlist - Data Management
3
+ * Handles data with sparse storage for million+ item support
4
+ */
5
+ import type { VListItem, VListAdapter, Range } from "../../types";
6
+ import { type SparseStorage, type SparseStorageConfig } from "./sparse";
7
+ import { type PlaceholderConfig, type PlaceholderManager } from "./placeholder";
8
+ /** Data manager configuration */
9
+ export interface DataManagerConfig<T extends VListItem = VListItem> {
10
+ /** Async data adapter */
11
+ adapter?: VListAdapter<T>;
12
+ /** Initial items (optional) */
13
+ initialItems?: T[];
14
+ /** Initial total count (if known) */
15
+ initialTotal?: number;
16
+ /** Sparse storage configuration */
17
+ storage?: SparseStorageConfig;
18
+ /** Placeholder configuration */
19
+ placeholder?: PlaceholderConfig;
20
+ /** Items per load request (default: 50) */
21
+ pageSize?: number;
22
+ /** Callback when state changes */
23
+ onStateChange?: (state: DataState<T>) => void;
24
+ /** Callback when items are loaded */
25
+ onItemsLoaded?: (items: T[], offset: number, total: number) => void;
26
+ /** Callback when items are evicted */
27
+ onItemsEvicted?: (count: number) => void;
28
+ }
29
+ /** Data state */
30
+ export interface DataState<_T extends VListItem = VListItem> {
31
+ /** Total items (declared, may be larger than loaded) */
32
+ total: number;
33
+ /** Number of items in memory */
34
+ cached: number;
35
+ /** Whether data is loading */
36
+ isLoading: boolean;
37
+ /** Pending load ranges */
38
+ pendingRanges: Range[];
39
+ /** Error from last operation */
40
+ error: Error | undefined;
41
+ /** Whether more items exist */
42
+ hasMore: boolean;
43
+ /** Current cursor (for cursor pagination) */
44
+ cursor: string | undefined;
45
+ }
46
+ /** Data manager instance */
47
+ export interface DataManager<T extends VListItem = VListItem> {
48
+ /** Get current state */
49
+ getState: () => DataState<T>;
50
+ /** Get total item count (direct getter, no allocation) */
51
+ getTotal: () => number;
52
+ /** Get cached item count (direct getter, no allocation) */
53
+ getCached: () => number;
54
+ /** Check if currently loading (direct getter, no allocation) */
55
+ getIsLoading: () => boolean;
56
+ /** Check if more items available (direct getter, no allocation) */
57
+ getHasMore: () => boolean;
58
+ /** Get sparse storage */
59
+ getStorage: () => SparseStorage<T>;
60
+ /** Get placeholder manager */
61
+ getPlaceholders: () => PlaceholderManager<T>;
62
+ /** Get item at index (may return placeholder if not loaded) */
63
+ getItem: (index: number) => T | undefined;
64
+ /** Get item by ID */
65
+ getItemById: (id: string | number) => T | undefined;
66
+ /** Get index by ID (-1 if not found) */
67
+ getIndexById: (id: string | number) => number;
68
+ /** Check if item at index is loaded (not placeholder) */
69
+ isItemLoaded: (index: number) => boolean;
70
+ /** Get items in range (includes placeholders for unloaded) */
71
+ getItemsInRange: (start: number, end: number) => T[];
72
+ /** Set total item count */
73
+ setTotal: (total: number) => void;
74
+ /** Set items at offset */
75
+ setItems: (items: T[], offset?: number, total?: number) => void;
76
+ /** Update item at index */
77
+ updateItem: (index: number, updates: Partial<T>) => boolean;
78
+ /** Remove item by ID */
79
+ removeItem: (id: string | number) => boolean;
80
+ /** Load items for a range */
81
+ loadRange: (start: number, end: number) => Promise<void>;
82
+ /** Ensure range is loaded (no-op if already loaded) */
83
+ ensureRange: (start: number, end: number) => Promise<void>;
84
+ /** Load initial data */
85
+ loadInitial: () => Promise<void>;
86
+ /** Load more items (infinite scroll) */
87
+ loadMore: () => Promise<boolean>;
88
+ /** Reload all data */
89
+ reload: () => Promise<void>;
90
+ /** Evict items far from visible range */
91
+ evictDistant: (visibleStart: number, visibleEnd: number) => void;
92
+ /** Clear all data */
93
+ clear: () => void;
94
+ /** Reset to initial state */
95
+ reset: () => void;
96
+ }
97
+ /**
98
+ * Create a data manager with sparse storage support
99
+ */
100
+ export declare const createDataManager: <T extends VListItem = VListItem>(config?: DataManagerConfig<T>) => DataManager<T>;
101
+ export { mergeRanges, calculateMissingRanges } from "./sparse";
102
+ export { isPlaceholderItem, filterPlaceholders, countRealItems, } from "./placeholder";
103
+ //# sourceMappingURL=manager.d.ts.map
@@ -0,0 +1,54 @@
1
+ /**
2
+ * vlist - Placeholder System
3
+ * Smart placeholder generation for loading states
4
+ *
5
+ * Key features:
6
+ * - Captures per-item field lengths from the first loaded batch
7
+ * - Cycles through real data profiles for natural size variance
8
+ * - Same item template renders both real and placeholder items
9
+ * - Renderer adds CSS class for visual styling (no JS branching needed)
10
+ */
11
+ import type { VListItem } from "../../types";
12
+ /** Placeholder configuration */
13
+ export interface PlaceholderConfig {
14
+ /** Character used for masking text (default: 'x') */
15
+ maskCharacter?: string;
16
+ /** Maximum items to sample for length profiling (default: 20) */
17
+ maxSampleSize?: number;
18
+ }
19
+ /** Placeholder manager instance */
20
+ export interface PlaceholderManager<T extends VListItem = VListItem> {
21
+ /** Analyze data structure from sample items */
22
+ analyzeStructure: (items: T[]) => void;
23
+ /** Check if structure has been analyzed */
24
+ hasAnalyzedStructure: () => boolean;
25
+ /** Generate a single placeholder item */
26
+ generate: (index: number) => T;
27
+ /** Generate multiple placeholder items */
28
+ generateRange: (start: number, end: number) => T[];
29
+ /** Clear analyzed structure */
30
+ clear: () => void;
31
+ }
32
+ /**
33
+ * Create a placeholder manager that generates realistic placeholder items
34
+ * by capturing per-item field lengths from the first loaded data batch.
35
+ *
36
+ * Placeholders carry the same field names as real items, filled with
37
+ * mask characters sized to match actual data. The renderer detects
38
+ * placeholders via the `_isPlaceholder` flag and applies a CSS class
39
+ * — no template branching required.
40
+ */
41
+ export declare const createPlaceholderManager: <T extends VListItem = VListItem>(config?: PlaceholderConfig) => PlaceholderManager<T>;
42
+ /**
43
+ * Check if an item is a placeholder
44
+ */
45
+ export declare const isPlaceholderItem: (item: unknown) => boolean;
46
+ /**
47
+ * Filter out placeholder items from an array
48
+ */
49
+ export declare const filterPlaceholders: <T extends VListItem>(items: T[]) => T[];
50
+ /**
51
+ * Count non-placeholder items in an array
52
+ */
53
+ export declare const countRealItems: <T extends VListItem>(items: (T | undefined)[]) => number;
54
+ //# sourceMappingURL=placeholder.d.ts.map
@@ -0,0 +1,91 @@
1
+ /**
2
+ * vlist - Sparse Storage
3
+ * Efficient storage for million+ item virtual lists
4
+ */
5
+ import type { VListItem, Range } from "../../types";
6
+ /** Configuration for sparse storage */
7
+ export interface SparseStorageConfig {
8
+ /** Number of items per chunk (default: 100) */
9
+ chunkSize?: number;
10
+ /** Maximum items to keep in memory (default: 5000) */
11
+ maxCachedItems?: number;
12
+ /** Extra items to keep around visible range during eviction (default: 200) */
13
+ evictionBuffer?: number;
14
+ /** Callback when items are evicted */
15
+ onEvict?: (evictedCount: number, evictedRanges: number[]) => void;
16
+ }
17
+ /** Storage statistics */
18
+ export interface SparseStorageStats {
19
+ /** Total items declared (may be larger than loaded) */
20
+ totalItems: number;
21
+ /** Number of items currently in memory */
22
+ cachedItems: number;
23
+ /** Number of chunks in memory */
24
+ cachedChunks: number;
25
+ /** Chunk size */
26
+ chunkSize: number;
27
+ /** Maximum cached items allowed */
28
+ maxCachedItems: number;
29
+ /** Memory efficiency (cachedItems / totalItems) */
30
+ memoryEfficiency: number;
31
+ }
32
+ /** Sparse storage instance */
33
+ export interface SparseStorage<T extends VListItem = VListItem> {
34
+ readonly chunkSize: number;
35
+ readonly maxCachedItems: number;
36
+ /** Get total item count */
37
+ getTotal: () => number;
38
+ /** Set total item count (for virtual scrolling height) */
39
+ setTotal: (total: number) => void;
40
+ /** Get item at index (undefined if not loaded) */
41
+ get: (index: number) => T | undefined;
42
+ /** Check if item at index is loaded */
43
+ has: (index: number) => boolean;
44
+ /** Set item at index */
45
+ set: (index: number, item: T) => void;
46
+ /** Set multiple items starting at offset */
47
+ setRange: (offset: number, items: T[]) => void;
48
+ /** Delete item at index */
49
+ delete: (index: number) => boolean;
50
+ /** Get items in range (includes undefined for unloaded) */
51
+ getRange: (start: number, end: number) => (T | undefined)[];
52
+ /** Check if range is fully loaded */
53
+ isRangeLoaded: (start: number, end: number) => boolean;
54
+ /** Get loaded ranges */
55
+ getLoadedRanges: () => Range[];
56
+ /** Find unloaded ranges within a given range */
57
+ findUnloadedRanges: (start: number, end: number) => Range[];
58
+ /** Get chunk index for item index */
59
+ getChunkIndex: (itemIndex: number) => number;
60
+ /** Check if chunk is loaded */
61
+ isChunkLoaded: (chunkIndex: number) => boolean;
62
+ /** Mark chunk as accessed (for LRU) */
63
+ touchChunk: (chunkIndex: number) => void;
64
+ /** Mark all chunks in a range as accessed with a single Date.now() call */
65
+ touchChunksForRange: (start: number, end: number) => void;
66
+ /** Evict chunks far from visible range */
67
+ evictDistant: (visibleStart: number, visibleEnd: number) => number;
68
+ /** Force eviction to meet memory limit */
69
+ evictToLimit: () => number;
70
+ /** Get storage statistics */
71
+ getStats: () => SparseStorageStats;
72
+ /** Get cached item count */
73
+ getCachedCount: () => number;
74
+ /** Clear all data */
75
+ clear: () => void;
76
+ /** Reset to initial state */
77
+ reset: () => void;
78
+ }
79
+ /**
80
+ * Create sparse storage for efficient large list handling
81
+ */
82
+ export declare const createSparseStorage: <T extends VListItem = VListItem>(config?: SparseStorageConfig) => SparseStorage<T>;
83
+ /**
84
+ * Merge adjacent/overlapping ranges
85
+ */
86
+ export declare const mergeRanges: (ranges: Range[]) => Range[];
87
+ /**
88
+ * Calculate ranges that need to be loaded
89
+ */
90
+ export declare const calculateMissingRanges: (needed: Range, loaded: Range[], chunkSize: number) => Range[];
91
+ //# sourceMappingURL=sparse.d.ts.map
@@ -0,0 +1,34 @@
1
+ /**
2
+ * vlist/autosize — Auto-Size Measurement Feature
3
+ *
4
+ * Enables dynamic item measurement via ResizeObserver for items with
5
+ * unknown sizes (Mode B). Items are rendered with no explicit main-axis
6
+ * size, measured once by a ResizeObserver, and then pinned to their
7
+ * measured size for all subsequent renders.
8
+ *
9
+ * Priority: 5 (runs before grid/masonry at 10 so the measured cache
10
+ * is in place before layout features wrap it)
11
+ *
12
+ * Requires: `item.estimatedHeight` or `item.estimatedWidth` in config
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { vlist, withAutoSize } from '@floor/vlist';
17
+ *
18
+ * vlist({
19
+ * container: '#app',
20
+ * item: { estimatedHeight: 60, template: (item) => `<div>${item.name}</div>` },
21
+ * items: data,
22
+ * })
23
+ * .use(withAutoSize())
24
+ * .build();
25
+ * ```
26
+ */
27
+ import type { VListItem } from "../../types";
28
+ import type { VListFeature } from "../../builder/types";
29
+ /**
30
+ * Create an auto-size measurement feature for items with unknown sizes.
31
+ * Reads `estimatedHeight`/`estimatedWidth` from the builder config.
32
+ */
33
+ export declare const withAutoSize: <T extends VListItem = VListItem>() => VListFeature<T>;
34
+ //# sourceMappingURL=feature.d.ts.map
@@ -0,0 +1,2 @@
1
+ export { withAutoSize } from "./feature";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,48 @@
1
+ /**
2
+ * vlist/grid - Builder Feature
3
+ * Switches from list layout to a 2D grid with configurable columns and gap.
4
+ *
5
+ * Priority: 10 (runs first — replaces the renderer before anything else renders)
6
+ *
7
+ * What it wires:
8
+ * - Replaces renderer — swaps the list renderer with a grid renderer
9
+ * - Redefines virtual total — the virtualizer sees rows, not items
10
+ * - Column width calculation — recalculated on resize
11
+ * - Item positioning — each item gets translateX (column) and translateY (row)
12
+ * - CSS class — adds .vlist--grid to the root element
13
+ *
14
+ * Restrictions:
15
+ * - Cannot be combined with orientation: 'horizontal'
16
+ * - Cannot be combined with reverse: true
17
+ *
18
+ * Can be combined with withGroups for grouped 2D layouts.
19
+ */
20
+ import type { VListItem } from "../../types";
21
+ import type { VListFeature } from "../../builder/types";
22
+ /** Grid feature configuration */
23
+ export interface GridFeatureConfig {
24
+ /** Number of columns (required, >= 1) */
25
+ columns: number;
26
+ /** Gap between items in pixels (default: 0) */
27
+ gap?: number;
28
+ }
29
+ /**
30
+ * Create a grid feature for the builder.
31
+ *
32
+ * Switches from list layout to a 2D grid with configurable columns and gap.
33
+ *
34
+ * ```ts
35
+ * import { vlist } from 'vlist/builder'
36
+ * import { withGrid } from 'vlist/grid'
37
+ *
38
+ * const gallery = vlist({
39
+ * container: '#gallery',
40
+ * item: { height: 200, template: renderPhoto },
41
+ * items: photos,
42
+ * })
43
+ * .use(withGrid({ columns: 4, gap: 8 }))
44
+ * .build()
45
+ * ```
46
+ */
47
+ export declare const withGrid: <T extends VListItem = VListItem>(config: GridFeatureConfig) => VListFeature<T>;
48
+ //# sourceMappingURL=feature.d.ts.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * vlist - Grid Domain
3
+ * 2D grid/card layout with virtualized rows
4
+ */
5
+ export { withGrid, type GridFeatureConfig } from "./feature";
6
+ export { createGridLayout } from "./layout";
7
+ export { createGridRenderer, type GridRenderer } from "./renderer";
8
+ export type { GridConfig, GridLayout, GridPosition, ItemRange } from "./types";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,29 @@
1
+ /**
2
+ * vlist - Grid Layout
3
+ * Pure O(1) calculations for mapping between flat item indices and grid positions.
4
+ *
5
+ * The grid transforms a flat list into rows:
6
+ * Items: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
7
+ * Grid (columns=4):
8
+ * Row 0: [0, 1, 2, 3]
9
+ * Row 1: [4, 5, 6, 7]
10
+ * Row 2: [8, 9] ← partially filled last row
11
+ *
12
+ * All operations are O(1) — integer division and modulo only.
13
+ */
14
+ import type { GridConfig, GridLayout } from "./types";
15
+ /**
16
+ * Extended grid config with optional groups support
17
+ */
18
+ export interface GridConfigWithGroups extends GridConfig {
19
+ /** Optional: check if an item index is a group header (for groups-aware layout) */
20
+ isHeaderFn?: (index: number) => boolean;
21
+ }
22
+ /**
23
+ * Create a GridLayout instance.
24
+ *
25
+ * @param config - Grid configuration (columns, gap, optional isHeaderFn)
26
+ * @returns GridLayout with O(1) mapping functions (or groups-aware if isHeaderFn provided)
27
+ */
28
+ export declare const createGridLayout: (config: GridConfigWithGroups) => GridLayout;
29
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1,71 @@
1
+ /**
2
+ * vlist - Grid Renderer
3
+ * Renders items in a 2D grid layout within the virtual scroll container.
4
+ *
5
+ * Extends the base renderer pattern but positions items using both
6
+ * row offsets (translateY from the size cache) and column offsets
7
+ * (translateX calculated from column index and container width).
8
+ *
9
+ * Key differences from the list renderer:
10
+ * - Items are positioned with translate(x, y) instead of just translateY(y)
11
+ * - Item width is set to columnWidth (containerWidth / columns - gaps)
12
+ * - The "index" in the rendered map is the FLAT ITEM INDEX (not row index)
13
+ * - Row offsets come from the size cache (which operates on row indices)
14
+ * - Column offsets are calculated from itemIndex % columns
15
+ *
16
+ * Performance:
17
+ * - Element pooling avoids createElement cost
18
+ * - Template re-evaluation skipped when item data + state unchanged (change tracking)
19
+ * - Position update skipped when coordinates unchanged (position tracking)
20
+ * - O(1) Set-based visibility diffing (not O(n) .some())
21
+ * - Release grace period prevents boundary thrashing (hover blink, transition replay)
22
+ * - Released elements removed from DOM immediately
23
+ * - DocumentFragment batched insertion for new elements
24
+ */
25
+ import type { VListItem, ItemTemplate, Range } from "../../types";
26
+ import type { SizeCache } from "../../rendering/sizes";
27
+ import type { CompressionContext } from "../../rendering/renderer";
28
+ import type { GridLayout } from "./types";
29
+ /** Grid renderer instance */
30
+ export interface GridRenderer<T extends VListItem = VListItem> {
31
+ /** Render items for a flat item range, positioned in a 2D grid */
32
+ render: (items: T[], range: Range, selectedIds: Set<string | number>, focusedIndex: number, compressionCtx?: CompressionContext) => void;
33
+ /** Update item positions (for compressed scrolling) */
34
+ updatePositions: (compressionCtx: CompressionContext) => void;
35
+ /** Update a single item (used by selection feature for focused item changes) */
36
+ updateItem: (index: number, item: T, isSelected: boolean, isFocused: boolean) => void;
37
+ /** Update only CSS classes on a rendered item (no template re-evaluation) */
38
+ updateItemClasses: (index: number, isSelected: boolean, isFocused: boolean) => void;
39
+ /** Get rendered item element by flat item index */
40
+ getElement: (index: number) => HTMLElement | undefined;
41
+ /**
42
+ * Reorder DOM children to match logical item order (by data-index).
43
+ * Called on scroll idle so screen readers encounter items in the correct
44
+ * sequence. Items are position:absolute so visual layout is unaffected.
45
+ */
46
+ sortDOM: () => void;
47
+ /** Update container width (call on resize) */
48
+ updateContainerWidth: (width: number) => void;
49
+ /** Clear all rendered items */
50
+ clear: () => void;
51
+ /** Destroy renderer and cleanup */
52
+ destroy: () => void;
53
+ }
54
+ /**
55
+ * Create a grid renderer for managing DOM elements in a 2D layout.
56
+ *
57
+ * The grid renderer receives flat item ranges (not row ranges) and
58
+ * positions each item at the correct (row, col) coordinate.
59
+ *
60
+ * @param itemsContainer - The DOM element that holds rendered items
61
+ * @param template - Item template function
62
+ * @param sizeCache - Size cache operating on ROW indices
63
+ * @param gridLayout - Grid layout for row/col calculations
64
+ * @param classPrefix - CSS class prefix
65
+ * @param initialContainerWidth - Initial container width for column sizing
66
+ * @param totalItemsGetter - Optional getter for total item count (for aria-setsize)
67
+ * @param ariaIdPrefix - Optional unique prefix for element IDs (for aria-activedescendant)
68
+ * @param isHorizontal - Whether layout is horizontal (scrolls right)
69
+ */
70
+ export declare const createGridRenderer: <T extends VListItem = VListItem>(itemsContainer: HTMLElement, template: ItemTemplate<T>, sizeCache: SizeCache, gridLayout: GridLayout, classPrefix: string, initialContainerWidth: number, totalItemsGetter?: () => number, ariaIdPrefix?: string, isHorizontal?: boolean) => GridRenderer<T>;
71
+ //# sourceMappingURL=renderer.d.ts.map
@@ -0,0 +1,71 @@
1
+ /**
2
+ * vlist - Grid Types
3
+ * Types for grid/card layout mode
4
+ *
5
+ * Grid layout transforms a flat list of items into a 2D grid where:
6
+ * - Virtualization operates on ROWS (not individual items)
7
+ * - Each row contains `columns` items side by side
8
+ * - Items are positioned using row/column coordinates
9
+ * - Compression applies to row count, not item count
10
+ */
11
+ import type { GridConfig } from "../../types";
12
+ export type { GridConfig };
13
+ /** Row/column position of an item */
14
+ export interface GridPosition {
15
+ /** Row index (0-based) */
16
+ row: number;
17
+ /** Column index (0-based) */
18
+ col: number;
19
+ }
20
+ /** Flat item range corresponding to a row range */
21
+ export interface ItemRange {
22
+ /** First item index (inclusive) */
23
+ start: number;
24
+ /** Last item index (inclusive) */
25
+ end: number;
26
+ }
27
+ /**
28
+ * GridLayout — maps between flat item indices and row/column positions.
29
+ *
30
+ * The virtualizer sees "rows" as its unit of work. Each row contains
31
+ * up to `columns` items. The last row may be partially filled.
32
+ *
33
+ * All operations are O(1) — just integer division and modulo.
34
+ */
35
+ export interface GridLayout {
36
+ /** Number of columns */
37
+ readonly columns: number;
38
+ /** Gap between items in pixels */
39
+ readonly gap: number;
40
+ /** Update grid configuration without recreating the layout */
41
+ update: (config: Partial<GridConfig>) => void;
42
+ /** Get total number of rows for a given item count */
43
+ getTotalRows: (totalItems: number) => number;
44
+ /** Get the row/col position for a flat item index */
45
+ getPosition: (itemIndex: number) => GridPosition;
46
+ /** Get the row index for a flat item index */
47
+ getRow: (itemIndex: number) => number;
48
+ /** Get the column index for a flat item index */
49
+ getCol: (itemIndex: number) => number;
50
+ /**
51
+ * Get the flat item range for a range of rows.
52
+ * The last row may be partially filled (end is clamped to totalItems - 1).
53
+ */
54
+ getItemRange: (rowStart: number, rowEnd: number, totalItems: number) => ItemRange;
55
+ /**
56
+ * Get the flat item index from a row and column.
57
+ * Returns -1 if out of bounds.
58
+ */
59
+ getItemIndex: (row: number, col: number, totalItems: number) => number;
60
+ /**
61
+ * Calculate column width given container width.
62
+ * Accounts for gaps: width = (containerWidth - (columns - 1) * gap) / columns
63
+ */
64
+ getColumnWidth: (containerWidth: number) => number;
65
+ /**
66
+ * Calculate the X offset for a column index.
67
+ * offset = col * (columnWidth + gap)
68
+ */
69
+ getColumnOffset: (col: number, containerWidth: number) => number;
70
+ }
71
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,74 @@
1
+ /**
2
+ * vlist/groups - Builder Feature
3
+ * Adds grouped lists with sticky headers.
4
+ *
5
+ * Priority: 10 (runs first — transforms item list and height function before rendering)
6
+ *
7
+ * What it wires:
8
+ * - Transforms item list — inserts header items at group boundaries
9
+ * - Replaces height function — headers use headerHeight, data items use configured item.height
10
+ * - Unified template — dispatches to headerTemplate for headers, user template for items
11
+ * - Sticky header DOM — creates a positioned header element that updates as you scroll
12
+ * - Index mapping — translates between data indices and layout indices
13
+ * - CSS class — adds .vlist--grouped to the root element
14
+ *
15
+ * Restrictions:
16
+ * - Items must be pre-sorted by group
17
+ *
18
+ * Can be combined with:
19
+ * - withGrid for grouped 2D layouts
20
+ * - reverse: true (sticky header shows current section as you scroll up through history)
21
+ * - orientation: 'horizontal' (sticky headers stick to left edge, push left when next header approaches)
22
+ */
23
+ import type { VListItem } from "../../types";
24
+ import type { VListFeature } from "../../builder/types";
25
+ /** Groups feature configuration */
26
+ export interface GroupsFeatureConfig {
27
+ /** Returns group key for item at index (required) */
28
+ getGroupForIndex: (index: number) => string;
29
+ /** Group header configuration — mirrors the `item` config shape */
30
+ header?: {
31
+ /** Header size in pixels — vertical scrolling (default) */
32
+ height?: number;
33
+ /** Header size in pixels — horizontal scrolling */
34
+ width?: number;
35
+ /** Render function for headers (required) */
36
+ template: (key: string, groupIndex: number) => HTMLElement | string;
37
+ };
38
+ /** @deprecated Use `header.height` instead. */
39
+ headerHeight?: number;
40
+ /** @deprecated Use `header.template` instead. */
41
+ headerTemplate?: (key: string, groupIndex: number) => HTMLElement | string;
42
+ /** Enable sticky headers — iOS Contacts style (default: true) */
43
+ sticky?: boolean;
44
+ }
45
+ /**
46
+ * Create a groups feature for the builder.
47
+ *
48
+ * Adds grouped lists with sticky section headers.
49
+ *
50
+ * ```ts
51
+ * import { vlist, withGroups } from '@floor/vlist'
52
+ *
53
+ * const contacts = vlist({
54
+ * container: '#contacts',
55
+ * item: { height: 56, template: renderContact },
56
+ * items: sortedContacts,
57
+ * })
58
+ * .use(withGroups({
59
+ * getGroupForIndex: (i) => sortedContacts[i].lastName[0],
60
+ * header: {
61
+ * height: 32,
62
+ * template: (letter) => {
63
+ * const el = document.createElement('div')
64
+ * el.className = 'letter-header'
65
+ * el.textContent = letter
66
+ * return el
67
+ * },
68
+ * },
69
+ * }))
70
+ * .build()
71
+ * ```
72
+ */
73
+ export declare const withGroups: <T extends VListItem = VListItem>(groupsRawConfig: GroupsFeatureConfig) => VListFeature<T>;
74
+ //# sourceMappingURL=feature.d.ts.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * vlist - Groups Domain
3
+ * Sticky headers and grouped lists
4
+ */
5
+ export { withGroups, type GroupsFeatureConfig } from "./feature";
6
+ export type { GroupsConfig, GroupBoundary, LayoutEntry, GroupHeaderItem, GroupLayout, StickyHeader, } from "./types";
7
+ export { isGroupHeader } from "./types";
8
+ export { createGroupLayout, buildLayoutItems, createGroupedSizeFn, } from "./layout";
9
+ export { createStickyHeader } from "./sticky";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,47 @@
1
+ /**
2
+ * vlist - Group Layout
3
+ * Computes group boundaries and maps between data indices and layout indices.
4
+ *
5
+ * The layout transforms a flat items array into a "layout" that includes
6
+ * group header pseudo-items interspersed at group boundaries:
7
+ *
8
+ * Data: [item0, item1, item2, item3, item4, item5]
9
+ * Groups: [ A, A, A, B, B, C ]
10
+ * Layout: [headerA, item0, item1, item2, headerB, item3, item4, headerC, item5]
11
+ * Index: [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
12
+ *
13
+ * All lookups are O(log g) where g = number of groups, using binary search
14
+ * on the sorted group boundaries array.
15
+ */
16
+ import type { GroupsConfig, GroupBoundary, GroupLayout, GroupHeaderItem } from "./types";
17
+ import type { VListItem } from "../../types";
18
+ /**
19
+ * Build the transformed layout items array with header pseudo-items inserted
20
+ * at group boundaries.
21
+ *
22
+ * @param items - Original data items
23
+ * @param groups - Computed group boundaries
24
+ * @returns Array of items and header pseudo-items in layout order
25
+ */
26
+ export declare const buildLayoutItems: <T extends VListItem>(items: T[], groups: readonly GroupBoundary[]) => Array<T | GroupHeaderItem>;
27
+ /**
28
+ * Create a size function for the layout (items + headers).
29
+ *
30
+ * Maps layout indices to sizes, accounting for both group headers and data items.
31
+ *
32
+ * @param layout - The group layout instance
33
+ * @param itemSize - Original item size config (number or function)
34
+ * @returns A size function (layoutIndex) => number suitable for SizeCache
35
+ */
36
+ export declare const createGroupedSizeFn: (layout: GroupLayout, itemSize: number | ((index: number) => number), sticky?: boolean) => ((layoutIndex: number) => number);
37
+ /**
38
+ * Create a GroupLayout instance.
39
+ *
40
+ * The layout computes group boundaries from items and provides efficient
41
+ * O(log g) mappings between data indices and layout indices.
42
+ *
43
+ * @param itemCount - Number of data items
44
+ * @param config - Groups configuration
45
+ */
46
+ export declare const createGroupLayout: (itemCount: number, config: GroupsConfig) => GroupLayout;
47
+ //# sourceMappingURL=layout.d.ts.map