vlist 1.9.1 → 2.0.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.github.md +104 -97
- package/README.md +46 -33
- package/dist/constants.d.ts +11 -6
- package/dist/constants.js +83 -0
- package/dist/core/create.d.ts +10 -0
- package/dist/core/create.js +740 -0
- package/dist/core/dom.d.ts +8 -0
- package/dist/core/dom.js +47 -0
- package/dist/core/hooks.d.ts +16 -0
- package/dist/core/hooks.js +67 -0
- package/dist/core/index.d.ts +17 -0
- package/dist/core/index.js +13 -0
- package/dist/core/pipeline.d.ts +51 -0
- package/dist/core/pipeline.js +307 -0
- package/dist/core/pool.d.ts +9 -0
- package/dist/core/pool.js +42 -0
- package/dist/core/scroll.d.ts +32 -0
- package/dist/core/scroll.js +137 -0
- package/dist/core/sizes.d.ts +8 -0
- package/dist/core/sizes.js +6 -0
- package/dist/core/state.d.ts +47 -0
- package/dist/core/state.js +56 -0
- package/dist/core/types.d.ts +187 -0
- package/dist/core/types.js +7 -0
- package/dist/{builder → core}/velocity.d.ts +1 -1
- package/dist/core/velocity.js +33 -0
- package/dist/events/emitter.js +60 -0
- package/dist/events/index.js +6 -0
- package/dist/index.d.ts +28 -19
- package/dist/index.js +28 -1
- package/dist/internals.d.ts +11 -7
- package/dist/internals.js +60 -1
- package/dist/plugins/a11y/index.d.ts +2 -0
- package/dist/plugins/a11y/index.js +1 -0
- package/dist/plugins/a11y/plugin.d.ts +13 -0
- package/dist/plugins/a11y/plugin.js +259 -0
- package/dist/{features → plugins}/async/index.d.ts +1 -1
- package/dist/plugins/async/index.js +12 -0
- package/dist/{features → plugins}/async/manager.d.ts +5 -1
- package/dist/plugins/async/manager.js +568 -0
- package/dist/plugins/async/placeholder.js +154 -0
- package/dist/plugins/async/plugin.d.ts +48 -0
- package/dist/plugins/async/plugin.js +311 -0
- package/dist/plugins/async/sparse.js +540 -0
- package/dist/plugins/autosize/index.d.ts +5 -0
- package/dist/plugins/autosize/index.js +4 -0
- package/dist/plugins/autosize/plugin.d.ts +19 -0
- package/dist/plugins/autosize/plugin.js +185 -0
- package/dist/plugins/grid/index.d.ts +7 -0
- package/dist/plugins/grid/index.js +5 -0
- package/dist/plugins/grid/layout.js +275 -0
- package/dist/plugins/grid/plugin.d.ts +23 -0
- package/dist/plugins/grid/plugin.js +347 -0
- package/dist/plugins/grid/renderer.js +525 -0
- package/dist/plugins/grid/types.js +11 -0
- package/dist/plugins/groups/async-bridge.js +246 -0
- package/dist/{features → plugins}/groups/index.d.ts +1 -1
- package/dist/plugins/groups/index.js +13 -0
- package/dist/plugins/groups/layout.js +294 -0
- package/dist/plugins/groups/plugin.d.ts +22 -0
- package/dist/plugins/groups/plugin.js +571 -0
- package/dist/plugins/groups/sticky.js +255 -0
- package/dist/plugins/groups/types.js +12 -0
- package/dist/plugins/masonry/index.d.ts +8 -0
- package/dist/plugins/masonry/index.js +6 -0
- package/dist/plugins/masonry/layout.js +261 -0
- package/dist/plugins/masonry/plugin.d.ts +32 -0
- package/dist/plugins/masonry/plugin.js +381 -0
- package/dist/plugins/masonry/renderer.js +354 -0
- package/dist/plugins/masonry/types.js +9 -0
- package/dist/plugins/page/index.d.ts +5 -0
- package/dist/plugins/page/index.js +5 -0
- package/dist/plugins/page/plugin.d.ts +21 -0
- package/dist/plugins/page/plugin.js +166 -0
- package/dist/plugins/scale/index.d.ts +5 -0
- package/dist/plugins/scale/index.js +4 -0
- package/dist/plugins/scale/plugin.d.ts +24 -0
- package/dist/plugins/scale/plugin.js +507 -0
- package/dist/plugins/scrollbar/controller.js +574 -0
- package/dist/plugins/scrollbar/index.d.ts +7 -0
- package/dist/plugins/scrollbar/index.js +6 -0
- package/dist/plugins/scrollbar/plugin.d.ts +20 -0
- package/dist/plugins/scrollbar/plugin.js +93 -0
- package/dist/plugins/scrollbar/scrollbar.js +556 -0
- package/dist/plugins/selection/index.d.ts +6 -0
- package/dist/plugins/selection/index.js +7 -0
- package/dist/plugins/selection/plugin.d.ts +16 -0
- package/dist/plugins/selection/plugin.js +601 -0
- package/dist/{features → plugins}/selection/state.d.ts +8 -0
- package/dist/plugins/selection/state.js +332 -0
- package/dist/plugins/snapshots/index.d.ts +5 -0
- package/dist/plugins/snapshots/index.js +5 -0
- package/dist/plugins/snapshots/plugin.d.ts +17 -0
- package/dist/plugins/snapshots/plugin.js +301 -0
- package/dist/plugins/sortable/index.d.ts +6 -0
- package/dist/plugins/sortable/index.js +6 -0
- package/dist/plugins/sortable/plugin.d.ts +34 -0
- package/dist/plugins/sortable/plugin.js +753 -0
- package/dist/plugins/table/header.js +501 -0
- package/dist/{features → plugins}/table/index.d.ts +1 -1
- package/dist/plugins/table/index.js +12 -0
- package/dist/plugins/table/layout.js +211 -0
- package/dist/plugins/table/plugin.d.ts +20 -0
- package/dist/plugins/table/plugin.js +391 -0
- package/dist/plugins/table/renderer.js +625 -0
- package/dist/plugins/table/types.js +12 -0
- package/dist/plugins/transition/index.d.ts +5 -0
- package/dist/plugins/transition/index.js +5 -0
- package/dist/plugins/transition/plugin.d.ts +22 -0
- package/dist/plugins/transition/plugin.js +405 -0
- package/dist/rendering/aria.js +23 -0
- package/dist/rendering/index.js +18 -0
- package/dist/rendering/measured.js +98 -0
- package/dist/rendering/renderer.js +586 -0
- package/dist/rendering/scale.js +267 -0
- package/dist/rendering/scroll.js +71 -0
- package/dist/rendering/sizes.js +193 -0
- package/dist/rendering/sort.js +65 -0
- package/dist/rendering/viewport.js +268 -0
- package/dist/size.json +1 -1
- package/dist/types.js +5 -0
- package/dist/utils/padding.d.ts +2 -4
- package/dist/utils/padding.js +49 -0
- package/dist/utils/stats.js +124 -0
- package/dist/vlist-grid.css +1 -1
- package/dist/vlist-masonry.css +1 -1
- package/dist/vlist-table.css +1 -1
- package/dist/vlist.css +1 -1
- package/package.json +9 -4
- package/dist/builder/a11y.d.ts +0 -16
- package/dist/builder/api.d.ts +0 -21
- package/dist/builder/context.d.ts +0 -36
- package/dist/builder/core.d.ts +0 -16
- package/dist/builder/data.d.ts +0 -71
- package/dist/builder/dom.d.ts +0 -15
- package/dist/builder/index.d.ts +0 -25
- package/dist/builder/materialize.d.ts +0 -166
- package/dist/builder/pool.d.ts +0 -10
- package/dist/builder/range.d.ts +0 -10
- package/dist/builder/scroll.d.ts +0 -24
- package/dist/builder/types.d.ts +0 -512
- package/dist/features/async/feature.d.ts +0 -72
- package/dist/features/autosize/feature.d.ts +0 -34
- package/dist/features/autosize/index.d.ts +0 -2
- package/dist/features/grid/feature.d.ts +0 -48
- package/dist/features/grid/index.d.ts +0 -9
- package/dist/features/groups/feature.d.ts +0 -75
- package/dist/features/masonry/feature.d.ts +0 -45
- package/dist/features/masonry/index.d.ts +0 -9
- package/dist/features/page/feature.d.ts +0 -109
- package/dist/features/page/index.d.ts +0 -9
- package/dist/features/scale/feature.d.ts +0 -42
- package/dist/features/scale/index.d.ts +0 -10
- package/dist/features/scrollbar/feature.d.ts +0 -81
- package/dist/features/scrollbar/index.d.ts +0 -8
- package/dist/features/selection/feature.d.ts +0 -91
- package/dist/features/selection/index.d.ts +0 -7
- package/dist/features/snapshots/feature.d.ts +0 -79
- package/dist/features/snapshots/index.d.ts +0 -9
- package/dist/features/sortable/feature.d.ts +0 -101
- package/dist/features/sortable/index.d.ts +0 -6
- package/dist/features/table/feature.d.ts +0 -67
- package/dist/features/transition/feature.d.ts +0 -30
- package/dist/features/transition/index.d.ts +0 -9
- /package/dist/{features → plugins}/async/placeholder.d.ts +0 -0
- /package/dist/{features → plugins}/async/sparse.d.ts +0 -0
- /package/dist/{features → plugins}/grid/layout.d.ts +0 -0
- /package/dist/{features → plugins}/grid/renderer.d.ts +0 -0
- /package/dist/{features → plugins}/grid/types.d.ts +0 -0
- /package/dist/{features → plugins}/groups/async-bridge.d.ts +0 -0
- /package/dist/{features → plugins}/groups/layout.d.ts +0 -0
- /package/dist/{features → plugins}/groups/sticky.d.ts +0 -0
- /package/dist/{features → plugins}/groups/types.d.ts +0 -0
- /package/dist/{features → plugins}/masonry/layout.d.ts +0 -0
- /package/dist/{features → plugins}/masonry/renderer.d.ts +0 -0
- /package/dist/{features → plugins}/masonry/types.d.ts +0 -0
- /package/dist/{features → plugins}/scrollbar/controller.d.ts +0 -0
- /package/dist/{features → plugins}/scrollbar/scrollbar.d.ts +0 -0
- /package/dist/{features → plugins}/table/header.d.ts +0 -0
- /package/dist/{features → plugins}/table/layout.d.ts +0 -0
- /package/dist/{features → plugins}/table/renderer.d.ts +0 -0
- /package/dist/{features → plugins}/table/types.d.ts +0 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist - Virtual Scrolling Core
|
|
3
|
+
* Pure functions for virtual scroll calculations
|
|
4
|
+
*
|
|
5
|
+
* Compression support is NOT imported here — it's injected via
|
|
6
|
+
* CompressionState parameters. When compression is inactive
|
|
7
|
+
* (the common case), all calculations use simple size-cache math
|
|
8
|
+
* with zero dependency on the compression module.
|
|
9
|
+
*
|
|
10
|
+
* This keeps the builder core lightweight. The withCompression feature
|
|
11
|
+
* and the monolithic createVList entry point import compression
|
|
12
|
+
* separately and pass the state in.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* A "no compression" state for lists that don't need it.
|
|
16
|
+
* Used by the builder core when withCompression is not installed.
|
|
17
|
+
*/
|
|
18
|
+
export const NO_COMPRESSION = {
|
|
19
|
+
isCompressed: false,
|
|
20
|
+
actualSize: 0,
|
|
21
|
+
virtualSize: 0,
|
|
22
|
+
ratio: 1,
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Create a trivial compression state from a size cache.
|
|
26
|
+
* No compression logic — just reads the total height.
|
|
27
|
+
* For use when the full compression module is not loaded.
|
|
28
|
+
*/
|
|
29
|
+
export const getSimpleCompressionState = (_totalItems, sizeCache) => {
|
|
30
|
+
const h = sizeCache.getTotalSize();
|
|
31
|
+
return {
|
|
32
|
+
isCompressed: false,
|
|
33
|
+
actualSize: h,
|
|
34
|
+
virtualSize: h,
|
|
35
|
+
ratio: 1,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
// =============================================================================
|
|
39
|
+
// Simple (non-compressed) range calculation
|
|
40
|
+
// =============================================================================
|
|
41
|
+
/**
|
|
42
|
+
* Calculate visible range using size cache lookups.
|
|
43
|
+
* Fast path for lists that don't need compression (< ~350 000 items at 48px).
|
|
44
|
+
* Mutates `out` to avoid allocation on the scroll hot path.
|
|
45
|
+
*/
|
|
46
|
+
export const simpleVisibleRange = (scrollPosition, containerHeight, sizeCache, totalItems, _compression, out) => {
|
|
47
|
+
if (totalItems === 0 || containerHeight === 0) {
|
|
48
|
+
out.start = 0;
|
|
49
|
+
out.end = -1;
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
const start = sizeCache.indexAtOffset(scrollPosition);
|
|
53
|
+
let end = sizeCache.indexAtOffset(scrollPosition + containerHeight);
|
|
54
|
+
if (end < totalItems - 1)
|
|
55
|
+
end++;
|
|
56
|
+
out.start = Math.max(0, start);
|
|
57
|
+
out.end = Math.min(totalItems - 1, Math.max(0, end));
|
|
58
|
+
return out;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Calculate render range (adds overscan around visible range).
|
|
62
|
+
* This function is compression-agnostic — works for both paths.
|
|
63
|
+
* Mutates `out` to avoid allocation on the scroll hot path.
|
|
64
|
+
*/
|
|
65
|
+
export const calculateRenderRange = (visibleRange, overscan, totalItems, out) => {
|
|
66
|
+
if (totalItems === 0) {
|
|
67
|
+
out.start = 0;
|
|
68
|
+
out.end = -1;
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
out.start = Math.max(0, visibleRange.start - overscan);
|
|
72
|
+
out.end = Math.min(totalItems - 1, visibleRange.end + overscan);
|
|
73
|
+
return out;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Simple scroll-to-index calculation (non-compressed).
|
|
77
|
+
* Uses size cache offsets directly.
|
|
78
|
+
*/
|
|
79
|
+
export const simpleScrollToIndex = (index, sizeCache, containerHeight, totalItems, _compression, align) => {
|
|
80
|
+
if (totalItems === 0)
|
|
81
|
+
return 0;
|
|
82
|
+
const safeIndex = Math.max(0, Math.min(index, totalItems - 1));
|
|
83
|
+
const itemOffset = sizeCache.getOffset(safeIndex);
|
|
84
|
+
const itemSize = sizeCache.getSize(safeIndex);
|
|
85
|
+
const totalHeight = sizeCache.getTotalSize();
|
|
86
|
+
const maxScroll = Math.max(0, totalHeight - containerHeight);
|
|
87
|
+
let position;
|
|
88
|
+
switch (align) {
|
|
89
|
+
case "center":
|
|
90
|
+
position = itemOffset - containerHeight / 2 + itemSize / 2;
|
|
91
|
+
break;
|
|
92
|
+
case "end":
|
|
93
|
+
position = itemOffset - containerHeight + itemSize;
|
|
94
|
+
break;
|
|
95
|
+
case "start":
|
|
96
|
+
default:
|
|
97
|
+
position = itemOffset;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
return Math.max(0, Math.min(position, maxScroll));
|
|
101
|
+
};
|
|
102
|
+
// =============================================================================
|
|
103
|
+
// Calculate total content height
|
|
104
|
+
// =============================================================================
|
|
105
|
+
/**
|
|
106
|
+
* Calculate total content height.
|
|
107
|
+
* Uses compression's virtualSize when compressed, raw height otherwise.
|
|
108
|
+
*/
|
|
109
|
+
export const calculateTotalSize = (_totalItems, sizeCache, compression) => {
|
|
110
|
+
if (compression && compression.isCompressed) {
|
|
111
|
+
return compression.virtualSize;
|
|
112
|
+
}
|
|
113
|
+
return sizeCache.getTotalSize();
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Calculate actual total size (without compression cap)
|
|
117
|
+
*/
|
|
118
|
+
export const calculateActualSize = (_totalItems, sizeCache) => {
|
|
119
|
+
return sizeCache.getTotalSize();
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Calculate the offset (translateY) for an item
|
|
123
|
+
* For non-compressed lists only
|
|
124
|
+
*/
|
|
125
|
+
export const calculateItemOffset = (index, sizeCache) => {
|
|
126
|
+
return sizeCache.getOffset(index);
|
|
127
|
+
};
|
|
128
|
+
// =============================================================================
|
|
129
|
+
// Scroll helpers
|
|
130
|
+
// =============================================================================
|
|
131
|
+
/**
|
|
132
|
+
* Clamp scroll position to valid range
|
|
133
|
+
*/
|
|
134
|
+
export const clampScrollPosition = (scrollPosition, totalHeight, containerHeight) => {
|
|
135
|
+
const maxScroll = Math.max(0, totalHeight - containerHeight);
|
|
136
|
+
return Math.max(0, Math.min(scrollPosition, maxScroll));
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* Determine scroll direction
|
|
140
|
+
*/
|
|
141
|
+
export const getScrollDirection = (currentScrollTop, previousScrollTop) => {
|
|
142
|
+
return currentScrollTop >= previousScrollTop ? "down" : "up";
|
|
143
|
+
};
|
|
144
|
+
// =============================================================================
|
|
145
|
+
// Viewport State
|
|
146
|
+
// =============================================================================
|
|
147
|
+
/**
|
|
148
|
+
* Create initial viewport state.
|
|
149
|
+
*
|
|
150
|
+
* Accepts an optional `visibleRangeFn` so that compression-aware callers
|
|
151
|
+
* can inject the compressed version. Defaults to `simpleVisibleRange`.
|
|
152
|
+
*/
|
|
153
|
+
export const createViewportState = (containerHeight, sizeCache, totalItems, overscan, compression, visibleRangeFn = simpleVisibleRange) => {
|
|
154
|
+
const visibleRange = { start: 0, end: 0 };
|
|
155
|
+
const renderRange = { start: 0, end: 0 };
|
|
156
|
+
visibleRangeFn(0, containerHeight, sizeCache, totalItems, compression, visibleRange);
|
|
157
|
+
calculateRenderRange(visibleRange, overscan, totalItems, renderRange);
|
|
158
|
+
return {
|
|
159
|
+
scrollPosition: 0,
|
|
160
|
+
containerSize: containerHeight,
|
|
161
|
+
totalSize: compression.virtualSize,
|
|
162
|
+
actualSize: compression.actualSize,
|
|
163
|
+
isCompressed: compression.isCompressed,
|
|
164
|
+
compressionRatio: compression.ratio,
|
|
165
|
+
visibleRange,
|
|
166
|
+
renderRange,
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* Update viewport state after scroll.
|
|
171
|
+
* Mutates state in place for performance on the scroll hot path.
|
|
172
|
+
*/
|
|
173
|
+
export const updateViewportState = (state, scrollPosition, sizeCache, totalItems, overscan, compression, visibleRangeFn = simpleVisibleRange) => {
|
|
174
|
+
visibleRangeFn(scrollPosition, state.containerSize, sizeCache, totalItems, compression, state.visibleRange);
|
|
175
|
+
calculateRenderRange(state.visibleRange, overscan, totalItems, state.renderRange);
|
|
176
|
+
state.scrollPosition = scrollPosition;
|
|
177
|
+
return state;
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Update viewport state when container resizes.
|
|
181
|
+
* Mutates state in place for performance.
|
|
182
|
+
*/
|
|
183
|
+
export const updateViewportSize = (state, containerHeight, sizeCache, totalItems, overscan, compression, visibleRangeFn = simpleVisibleRange) => {
|
|
184
|
+
visibleRangeFn(state.scrollPosition, containerHeight, sizeCache, totalItems, compression, state.visibleRange);
|
|
185
|
+
calculateRenderRange(state.visibleRange, overscan, totalItems, state.renderRange);
|
|
186
|
+
state.containerSize = containerHeight;
|
|
187
|
+
state.totalSize = compression.virtualSize;
|
|
188
|
+
state.actualSize = compression.actualSize;
|
|
189
|
+
state.isCompressed = compression.isCompressed;
|
|
190
|
+
state.compressionRatio = compression.ratio;
|
|
191
|
+
return state;
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* Update viewport state when total items changes.
|
|
195
|
+
* Mutates state in place for performance.
|
|
196
|
+
*/
|
|
197
|
+
export const updateViewportItems = (state, sizeCache, totalItems, overscan, compression, visibleRangeFn = simpleVisibleRange) => {
|
|
198
|
+
visibleRangeFn(state.scrollPosition, state.containerSize, sizeCache, totalItems, compression, state.visibleRange);
|
|
199
|
+
calculateRenderRange(state.visibleRange, overscan, totalItems, state.renderRange);
|
|
200
|
+
state.totalSize = compression.virtualSize;
|
|
201
|
+
state.actualSize = compression.actualSize;
|
|
202
|
+
state.isCompressed = compression.isCompressed;
|
|
203
|
+
state.compressionRatio = compression.ratio;
|
|
204
|
+
return state;
|
|
205
|
+
};
|
|
206
|
+
// =============================================================================
|
|
207
|
+
// calculateScrollToIndex (public API)
|
|
208
|
+
// =============================================================================
|
|
209
|
+
/**
|
|
210
|
+
* Calculate scroll position to bring an index into view.
|
|
211
|
+
*
|
|
212
|
+
* Accepts an optional `scrollToIndexFn` so that compression-aware callers
|
|
213
|
+
* can inject the compressed version. Defaults to `simpleScrollToIndex`.
|
|
214
|
+
*/
|
|
215
|
+
export const calculateScrollToIndex = (index, sizeCache, containerHeight, totalItems, align = "start", compression, scrollToIndexFn = simpleScrollToIndex) => {
|
|
216
|
+
return scrollToIndexFn(index, sizeCache, containerHeight, totalItems, compression, align);
|
|
217
|
+
};
|
|
218
|
+
// =============================================================================
|
|
219
|
+
// Range Utilities
|
|
220
|
+
// =============================================================================
|
|
221
|
+
/**
|
|
222
|
+
* Check if two ranges are equal
|
|
223
|
+
*/
|
|
224
|
+
export const rangesEqual = (a, b) => {
|
|
225
|
+
return a.start === b.start && a.end === b.end;
|
|
226
|
+
};
|
|
227
|
+
/**
|
|
228
|
+
* Check if an index is within a range
|
|
229
|
+
*/
|
|
230
|
+
export const isInRange = (index, range) => {
|
|
231
|
+
return index >= range.start && index <= range.end;
|
|
232
|
+
};
|
|
233
|
+
/**
|
|
234
|
+
* Get the count of items in a range
|
|
235
|
+
*/
|
|
236
|
+
export const getRangeCount = (range) => {
|
|
237
|
+
if (range.end < range.start)
|
|
238
|
+
return 0;
|
|
239
|
+
return range.end - range.start + 1;
|
|
240
|
+
};
|
|
241
|
+
/**
|
|
242
|
+
* Create an array of indices from a range
|
|
243
|
+
*/
|
|
244
|
+
export const rangeToIndices = (range) => {
|
|
245
|
+
const indices = [];
|
|
246
|
+
for (let i = range.start; i <= range.end; i++) {
|
|
247
|
+
indices.push(i);
|
|
248
|
+
}
|
|
249
|
+
return indices;
|
|
250
|
+
};
|
|
251
|
+
/**
|
|
252
|
+
* Calculate which indices need to be added/removed when range changes
|
|
253
|
+
*/
|
|
254
|
+
export const diffRanges = (oldRange, newRange) => {
|
|
255
|
+
const add = [];
|
|
256
|
+
const remove = [];
|
|
257
|
+
for (let i = oldRange.start; i <= oldRange.end; i++) {
|
|
258
|
+
if (i < newRange.start || i > newRange.end) {
|
|
259
|
+
remove.push(i);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
for (let i = newRange.start; i <= newRange.end; i++) {
|
|
263
|
+
if (i < oldRange.start || i > oldRange.end) {
|
|
264
|
+
add.push(i);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return { add, remove };
|
|
268
|
+
};
|
package/dist/size.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"base":{"minified":"
|
|
1
|
+
{"base":{"minified":"21.5","gzipped":"7.8","minBytes":22029,"gzBytes":7991},"a11y":{"minified":"24.5","gzipped":"8.9","minBytes":25124,"gzBytes":9125},"selection":{"minified":"29.8","gzipped":"10.2","minBytes":30498,"gzBytes":10467},"data":{"minified":"34.3","gzipped":"12.5","minBytes":35172,"gzBytes":12764},"scrollbar":{"minified":"28.7","gzipped":"9.8","minBytes":29341,"gzBytes":10061},"sortable":{"minified":"30.9","gzipped":"10.7","minBytes":31632,"gzBytes":10979},"groups":{"minified":"32.8","gzipped":"11.5","minBytes":33587,"gzBytes":11806},"scale":{"minified":"34.7","gzipped":"11.7","minBytes":35515,"gzBytes":11963},"page":{"minified":"23.9","gzipped":"8.6","minBytes":24507,"gzBytes":8767},"snapshots":{"minified":"25.1","gzipped":"9.1","minBytes":25737,"gzBytes":9297},"transition":{"minified":"28.2","gzipped":"9.6","minBytes":28912,"gzBytes":9870},"autosize":{"minified":"23.4","gzipped":"8.5","minBytes":23920,"gzBytes":8688},"grid":{"minified":"27.5","gzipped":"9.9","minBytes":28162,"gzBytes":10133},"table":{"minified":"39.7","gzipped":"13.6","minBytes":40615,"gzBytes":13922},"masonry":{"minified":"31.2","gzipped":"11.4","minBytes":31980,"gzBytes":11626}}
|
package/dist/types.js
ADDED
package/dist/utils/padding.d.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* vlist/utils — Padding Resolution
|
|
3
3
|
* Shared helpers for resolving the CSS-shorthand padding config into
|
|
4
|
-
* concrete pixel values. Used by core (to apply CSS) and by
|
|
4
|
+
* concrete pixel values. Used by core (to apply CSS) and by plugins
|
|
5
5
|
* (to subtract cross-axis padding from container dimensions).
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
|
-
/** Padding config — re-exported from BuilderConfig for convenience */
|
|
9
|
-
export type PaddingConfig = NonNullable<BuilderConfig["padding"]>;
|
|
7
|
+
export type PaddingConfig = number | [number, number] | [number, number, number, number];
|
|
10
8
|
/** Resolved padding — all four sides in pixels */
|
|
11
9
|
export interface ResolvedPadding {
|
|
12
10
|
readonly top: number;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vlist/utils — Padding Resolution
|
|
3
|
+
* Shared helpers for resolving the CSS-shorthand padding config into
|
|
4
|
+
* concrete pixel values. Used by core (to apply CSS) and by plugins
|
|
5
|
+
* (to subtract cross-axis padding from container dimensions).
|
|
6
|
+
*/
|
|
7
|
+
// =============================================================================
|
|
8
|
+
// Resolve All Sides
|
|
9
|
+
// =============================================================================
|
|
10
|
+
/** Zero padding singleton — avoids allocation when padding is undefined */
|
|
11
|
+
const ZERO = { top: 0, right: 0, bottom: 0, left: 0 };
|
|
12
|
+
/**
|
|
13
|
+
* Resolve a padding config into all four sides.
|
|
14
|
+
*
|
|
15
|
+
* - `undefined` → { 0, 0, 0, 0 }
|
|
16
|
+
* - `number` → equal on all sides
|
|
17
|
+
* - `[v, h]` → top/bottom = v, left/right = h
|
|
18
|
+
* - `[t, r, b, l]` → per-side (CSS order)
|
|
19
|
+
*
|
|
20
|
+
* Returns a frozen singleton for the zero case (no allocation).
|
|
21
|
+
*/
|
|
22
|
+
export const resolvePadding = (padding) => {
|
|
23
|
+
if (padding == null)
|
|
24
|
+
return ZERO;
|
|
25
|
+
if (typeof padding === "number") {
|
|
26
|
+
return padding === 0 ? ZERO : { top: padding, right: padding, bottom: padding, left: padding };
|
|
27
|
+
}
|
|
28
|
+
if (padding.length === 2) {
|
|
29
|
+
const [v, h] = padding;
|
|
30
|
+
return (v === 0 && h === 0) ? ZERO : { top: v, right: h, bottom: v, left: h };
|
|
31
|
+
}
|
|
32
|
+
const [top, right, bottom, left] = padding;
|
|
33
|
+
return (top === 0 && right === 0 && bottom === 0 && left === 0)
|
|
34
|
+
? ZERO
|
|
35
|
+
: { top, right, bottom, left };
|
|
36
|
+
};
|
|
37
|
+
// =============================================================================
|
|
38
|
+
// Axis Helpers
|
|
39
|
+
// =============================================================================
|
|
40
|
+
/**
|
|
41
|
+
* Total padding along the main axis (scroll direction).
|
|
42
|
+
* Vertical → top + bottom. Horizontal → left + right.
|
|
43
|
+
*/
|
|
44
|
+
export const mainAxisPaddingFrom = (p, isHorizontal) => isHorizontal ? p.left + p.right : p.top + p.bottom;
|
|
45
|
+
/**
|
|
46
|
+
* Total padding along the cross axis (perpendicular to scroll).
|
|
47
|
+
* Vertical → left + right. Horizontal → top + bottom.
|
|
48
|
+
*/
|
|
49
|
+
export const crossAxisPaddingFrom = (p, isHorizontal) => isHorizontal ? p.top + p.bottom : p.left + p.right;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// src/utils/stats.ts
|
|
2
|
+
// Pure computation module for scroll statistics.
|
|
3
|
+
// Tracks velocity (current + running average), computes cumulative item count
|
|
4
|
+
// and progress from scroll position using geometric mapping.
|
|
5
|
+
//
|
|
6
|
+
// No DOM access, no RAF, no side effects — purely functional state tracker.
|
|
7
|
+
import { MAX_VIRTUAL_SIZE } from "../constants";
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// Constants
|
|
10
|
+
// =============================================================================
|
|
11
|
+
const MAX_VELOCITY = 50;
|
|
12
|
+
const MIN_VELOCITY = 0.1;
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// Factory
|
|
15
|
+
// =============================================================================
|
|
16
|
+
/**
|
|
17
|
+
* Create a stats tracker.
|
|
18
|
+
*
|
|
19
|
+
* All inputs are provided via callbacks so the tracker always reflects
|
|
20
|
+
* the latest values without needing to be recreated when the list changes.
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* const stats = createStats({
|
|
24
|
+
* getScrollPosition: () => list.getScrollPosition(),
|
|
25
|
+
* getTotal: () => items.length,
|
|
26
|
+
* getItemSize: () => ITEM_HEIGHT,
|
|
27
|
+
* getContainerSize: () => containerEl.clientHeight,
|
|
28
|
+
* })
|
|
29
|
+
*
|
|
30
|
+
* const { progress, itemCount, total } = stats.getState()
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function createStats(config) {
|
|
34
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
35
|
+
// Velocity state
|
|
36
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
37
|
+
let currentVelocity = 0;
|
|
38
|
+
let velocitySum = 0;
|
|
39
|
+
let velocityCount = 0;
|
|
40
|
+
function getVelocityAverage() {
|
|
41
|
+
return velocityCount > 0 ? velocitySum / velocityCount : 0;
|
|
42
|
+
}
|
|
43
|
+
function onVelocity(velocity) {
|
|
44
|
+
currentVelocity = velocity;
|
|
45
|
+
if (velocity > MIN_VELOCITY && velocity < MAX_VELOCITY) {
|
|
46
|
+
velocitySum += velocity;
|
|
47
|
+
velocityCount++;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
51
|
+
// Geometric item count
|
|
52
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
53
|
+
/**
|
|
54
|
+
* Compute cumulative item count from scroll position geometrically.
|
|
55
|
+
*
|
|
56
|
+
* scrollPosition is the DOM scrollTop/scrollLeft — in compressed/scaled
|
|
57
|
+
* mode this is the virtual (compressed) scroll position. We map it back
|
|
58
|
+
* to real item indices using the same linear mapping vlist uses internally:
|
|
59
|
+
*
|
|
60
|
+
* columns = getColumns() ?? 1
|
|
61
|
+
* totalRows = ceil(total / columns)
|
|
62
|
+
* totalActualSize = totalRows × itemSize
|
|
63
|
+
* totalVirtualSize = min(totalActualSize, MAX_VIRTUAL_SIZE)
|
|
64
|
+
* maxVirtualScroll = totalVirtualSize − containerSize
|
|
65
|
+
* maxActualScroll = totalActualSize − containerSize
|
|
66
|
+
* ratio = maxActualScroll / maxVirtualScroll
|
|
67
|
+
* actualOffset = scrollPosition × ratio
|
|
68
|
+
* visibleRows = ceil((actualOffset + containerSize) / itemSize)
|
|
69
|
+
* itemCount = min(visibleRows × columns, total)
|
|
70
|
+
*
|
|
71
|
+
* containerSize is provided by the caller (clientHeight for vertical,
|
|
72
|
+
* clientWidth for horizontal).
|
|
73
|
+
*
|
|
74
|
+
* Using scroll-range ratio (not size ratio) ensures that at max scroll
|
|
75
|
+
* the end of the viewport aligns exactly with the last row/column.
|
|
76
|
+
* For grid/masonry, rows are converted to items via the column multiplier.
|
|
77
|
+
*/
|
|
78
|
+
function getItemCount() {
|
|
79
|
+
const total = config.getTotal();
|
|
80
|
+
if (total === 0)
|
|
81
|
+
return 0;
|
|
82
|
+
const itemSize = config.getItemSize();
|
|
83
|
+
if (itemSize <= 0)
|
|
84
|
+
return 0;
|
|
85
|
+
const containerSize = config.getContainerSize();
|
|
86
|
+
if (containerSize <= 0)
|
|
87
|
+
return 0;
|
|
88
|
+
const scrollPosition = config.getScrollPosition();
|
|
89
|
+
// For grid/masonry layouts, vlist virtualizes rows — each row holds
|
|
90
|
+
// `columns` items. Convert total items → total rows for the geometric
|
|
91
|
+
// mapping, then convert visible rows back to items at the end.
|
|
92
|
+
const columns = typeof config.getColumns === "function" ? config.getColumns() : 1;
|
|
93
|
+
const totalRows = Math.ceil(total / columns);
|
|
94
|
+
// Map virtual scroll position back to actual content offset.
|
|
95
|
+
// Use scroll-range ratio so maxScroll maps exactly to the last row.
|
|
96
|
+
const totalActualSize = totalRows * itemSize;
|
|
97
|
+
const totalVirtualSize = Math.min(totalActualSize, MAX_VIRTUAL_SIZE);
|
|
98
|
+
const maxVirtualScroll = totalVirtualSize - containerSize;
|
|
99
|
+
const maxActualScroll = totalActualSize - containerSize;
|
|
100
|
+
const ratio = maxVirtualScroll > 0 ? maxActualScroll / maxVirtualScroll : 1;
|
|
101
|
+
const actualOffset = scrollPosition * ratio;
|
|
102
|
+
const lastVisibleRow = Math.ceil((actualOffset + containerSize) / itemSize);
|
|
103
|
+
return Math.min(lastVisibleRow * columns, total);
|
|
104
|
+
}
|
|
105
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
106
|
+
// Public API
|
|
107
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
108
|
+
function getState() {
|
|
109
|
+
const total = config.getTotal();
|
|
110
|
+
const itemCount = getItemCount();
|
|
111
|
+
const progress = total > 0 ? Math.min(100, Math.max(0, (itemCount / total) * 100)) : 0;
|
|
112
|
+
return {
|
|
113
|
+
progress,
|
|
114
|
+
velocity: currentVelocity,
|
|
115
|
+
velocityAvg: getVelocityAverage(),
|
|
116
|
+
itemCount,
|
|
117
|
+
total,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
getState,
|
|
122
|
+
onVelocity,
|
|
123
|
+
};
|
|
124
|
+
}
|
package/dist/vlist-grid.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.vlist--grid .vlist-
|
|
1
|
+
.vlist--grid .vlist-content{contain:layout style}.vlist-grid-item{position:absolute;top:0;left:0;display:flex;align-items:center;justify-content:center;padding:var(--vlist-item-padding-y) var(--vlist-item-padding-x);background-color:var(--vlist-bg);cursor:default;user-select:none;transition:background-color var(--vlist-transition-duration) var(--vlist-transition-timing);contain:content;box-sizing:border-box;overflow:hidden}.vlist--grid .vlist-item{right:auto;border-bottom:none}.vlist--scrolling .vlist-grid-item{transition:none}.vlist--selectable .vlist-grid-item{cursor:pointer}.vlist--selectable .vlist-grid-item:hover{background-color:var(--vlist-bg-hover)}.vlist-grid-item.vlist-item--odd{background:var(--vlist-bg-striped)}.vlist-grid-item.vlist-item--selected{background-color:var(--vlist-bg-selected)}.vlist-grid-item.vlist-item--odd.vlist-item--selected{background:var(--vlist-bg-selected)}.vlist--selectable .vlist-grid-item.vlist-item--selected:hover{background-color:var(--vlist-bg-selected-hover)}.vlist--selectable .vlist-grid-item.vlist-item--odd.vlist-item--selected:hover{background:var(--vlist-bg-selected-hover)}.vlist-grid-item.vlist-item--focused{outline:2px solid var(--vlist-focus-ring,#3b82f6);outline-offset:-2px;z-index:1}
|
package/dist/vlist-masonry.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.vlist--masonry .vlist-
|
|
1
|
+
.vlist--masonry .vlist-content{contain:layout style}.vlist-masonry-item{position:absolute;top:0;left:0;display:flex;align-items:center;justify-content:center;padding:var(--vlist-item-padding-y) var(--vlist-item-padding-x);background-color:var(--vlist-bg);cursor:default;user-select:none;transition:background-color var(--vlist-transition-duration) var(--vlist-transition-timing);contain:content;box-sizing:border-box;overflow:hidden}.vlist--masonry .vlist-item{right:auto;border-bottom:none}.vlist--scrolling .vlist-masonry-item{transition:none}.vlist--selectable .vlist-masonry-item{cursor:pointer}.vlist--selectable .vlist-masonry-item:hover{background-color:var(--vlist-bg-hover)}.vlist-masonry-item.vlist-item--odd{background:var(--vlist-bg-striped)}.vlist-masonry-item.vlist-item--selected{background-color:var(--vlist-bg-selected)}.vlist-masonry-item.vlist-item--odd.vlist-item--selected{background:var(--vlist-bg-selected)}.vlist--selectable .vlist-masonry-item.vlist-item--selected:hover{background-color:var(--vlist-bg-selected-hover)}.vlist--selectable .vlist-masonry-item.vlist-item--odd.vlist-item--selected:hover{background:var(--vlist-bg-selected-hover)}.vlist-masonry-item.vlist-item--focused{outline:2px solid var(--vlist-focus-ring,#3b82f6);outline-offset:-2px;z-index:1}
|
package/dist/vlist-table.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.vlist--table{display:flex;flex-direction:column;overflow:hidden;min-height:inherit}.vlist--table .vlist-viewport{flex:1;min-height:0;height:auto;overflow:auto}.vlist--table .vlist-table-header{position:static}.vlist--table .vlist-
|
|
1
|
+
.vlist--table{display:flex;flex-direction:column;overflow:hidden;min-height:inherit}.vlist--table .vlist-viewport{flex:1;min-height:0;height:auto;overflow:auto}.vlist--table .vlist-table-header{position:static}.vlist--table .vlist-scrollbar__hover{top:var(--vlist-table-header-height,0px);height:calc(100% - var(--vlist-table-header-height,0px))}.vlist--table .vlist-viewport--custom-scrollbar::-webkit-scrollbar{display:block;width:0}.vlist--table .vlist-content{contain:layout style}.vlist-table-header{position:absolute;top:0;left:0;right:0;z-index:5;overflow:hidden;display:flex;align-items:stretch;box-sizing:border-box;will-change:scroll-position;contain:layout style;background-color:var(--vlist-bg,#ffffff);border-bottom:1px solid var(--vlist-border,#e5e7eb);font-weight:600;font-size:0.8125rem;letter-spacing:0.01em;color:var(--vlist-text-muted,#6b7280)}.vlist-table-header-scroll{position:relative;display:flex;align-items:stretch;height:100%;min-width:100%;flex-shrink:0}.vlist-table-header-cell{position:relative;display:flex;align-items:center;height:100%;box-sizing:border-box;flex-shrink:0;user-select:none;padding-left:var(--vlist-item-padding-x,0.75rem);padding-right:var(--vlist-item-padding-x,0.75rem);border-right:1px solid transparent;transition:background-color 0.15s ease}.vlist-table-header-cell:hover{background-color:var(--vlist-bg-hover,rgba(0,0,0,0.04))}.vlist-table-header-cell--sortable{cursor:pointer}.vlist-table-header-cell--sortable:active{background-color:var(--vlist-bg-selected,rgba(59,130,246,0.12))}.vlist-table-header-cell--center{justify-content:center}.vlist-table-header-cell--right{justify-content:flex-end}.vlist-table-header-content{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;pointer-events:none}.vlist-table-header-sort{margin-left:4px;flex-shrink:0;opacity:0;font-size:0.7em;transition:opacity 0.15s ease;pointer-events:none;color:var(--vlist-text-muted,#6b7280)}.vlist-table-header-resize{position:absolute;top:0;right:-3px;bottom:0;width:5px;cursor:col-resize;z-index:2;touch-action:none;opacity:0;transition:opacity 0.15s ease}.vlist-table-header-resize::after{content:"";position:absolute;top:0;bottom:0;left:50%;width:1px;transform:translateX(-50%);background-color:var(--vlist-border,#e5e7eb);transition:background-color 0.15s ease}.vlist-table-header-resize:hover::after{background-color:var(--vlist-focus-ring,#3b82f6)}.vlist-table-header-cell:hover .vlist-table-header-resize{opacity:1}.vlist-table-header-resize--active{opacity:1}.vlist-table-header-resize--active::after{background-color:var(--vlist-border-selected,#3b82f6)}.vlist--col-resizing,.vlist--col-resizing *{cursor:col-resize !important;user-select:none !important}.vlist-table-row{position:absolute;top:0;left:0;display:flex;box-sizing:border-box;contain:content;border-bottom:none;background-color:var(--vlist-bg,#ffffff);cursor:default;user-select:none;transition:background-color var(--vlist-transition-duration,150ms) var(--vlist-transition-timing,ease-in-out);will-change:transform}.vlist--scrolling .vlist-table-row{transition:none}.vlist-table-row.vlist-item--odd{background:var( --vlist-bg-striped,var(--vlist-bg-hover,rgba(0,0,0,0.02)) )}.vlist--selectable .vlist-table-row{cursor:pointer}.vlist--selectable .vlist-table-row:hover{background-color:var(--vlist-bg-hover,rgba(0,0,0,0.04))}.vlist-table-row.vlist-item--selected{background-color:var(--vlist-bg-selected,rgba(59,130,246,0.12))}.vlist-table-row.vlist-item--odd.vlist-item--selected{background:var(--vlist-bg-selected,rgba(59,130,246,0.12))}.vlist--selectable .vlist-table-row.vlist-item--selected:hover{background-color:var(--vlist-bg-selected-hover,rgba(59,130,246,0.18))}.vlist--selectable .vlist-table-row.vlist-item--odd.vlist-item--selected:hover{background:var(--vlist-bg-selected-hover,rgba(59,130,246,0.18))}.vlist-table-row.vlist-item--focused{outline:2px solid var(--vlist-focus-ring,#3b82f6);outline-offset:-2px;z-index:1}.vlist-table-cell{position:absolute;top:0;bottom:0;display:flex;align-items:center;box-sizing:border-box;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;padding-left:var(--vlist-item-padding-x,0.75rem);padding-right:var(--vlist-item-padding-x,0.75rem);font-size:0.875rem;color:var(--vlist-text,#111827)}.vlist-table-cell--center{text-align:center;justify-content:center}.vlist-table-cell--right{text-align:right;justify-content:flex-end}.vlist--table-row-borders .vlist-table-row{border-bottom:1px solid var(--vlist-border,#e5e7eb)}.vlist--table-col-borders .vlist-table-cell{border-right:1px solid var(--vlist-border,#e5e7eb)}.vlist--table-col-borders .vlist-table-header-cell{border-right-color:var(--vlist-border,#e5e7eb)}.vlist-table-group-header{display:flex;align-items:center;background-color:var(--vlist-group-header-bg,#f3f4f6);cursor:default}.vlist-table-group-header-content{flex:1;min-width:0;font-size:0.75rem;font-weight:600;letter-spacing:0.03em;text-transform:uppercase;color:var(--vlist-text-muted,#6b7280);border-bottom:1px solid var(--vlist-border)}.vlist--table-row-borders .vlist-table-group-header{border-bottom:1px solid var(--vlist-border)}.vlist--table.vlist--grouped .vlist-sticky-header{order:1;z-index:4;border-bottom:none}.vlist--table.vlist--grouped .vlist-viewport{order:2}[data-theme-mode="dark"] .vlist-table-header,.dark .vlist-table-header{background-color:var(--vlist-bg,#111827);color:var(--vlist-text-muted,#9ca3af);border-bottom-color:var(--vlist-border,#374151)}[data-theme-mode="dark"] .vlist-table-header-cell:hover,.dark .vlist-table-header-cell:hover{background-color:var(--vlist-bg-hover,rgba(255,255,255,0.06))}[data-theme-mode="dark"] .vlist-table-header-resize::after,.dark .vlist-table-header-resize::after{background-color:var(--vlist-border,#374151)}[data-theme-mode="dark"] .vlist-table-header-resize:hover::after,.dark .vlist-table-header-resize:hover::after{background-color:var(--vlist-focus-ring,#3b82f6)}[data-theme-mode="dark"] .vlist-table-group-header,.dark .vlist-table-group-header{background-color:var(--vlist-group-header-bg,#1f2937)}[data-theme-mode="dark"] .vlist-table-group-header-content,.dark .vlist-table-group-header-content{color:var(--vlist-text-muted,#9ca3af)}@media (prefers-color-scheme:dark){:root:not([data-theme-mode="light"]):not([data-theme-mode="dark"]) .vlist-table-header{background-color:var(--vlist-bg,#111827);color:var(--vlist-text-muted,#9ca3af);border-bottom-color:var(--vlist-border,#374151)}:root:not([data-theme-mode="light"]):not([data-theme-mode="dark"]) .vlist-table-header-cell:hover{background-color:var(--vlist-bg-hover,rgba(255,255,255,0.06))}:root:not([data-theme-mode="light"]):not([data-theme-mode="dark"]) .vlist-table-header-resize::after{background-color:var(--vlist-border,#374151)}:root:not([data-theme-mode="light"]):not([data-theme-mode="dark"]) .vlist-table-header-resize:hover::after{background-color:var(--vlist-focus-ring,#3b82f6)}:root:not([data-theme-mode="light"]):not([data-theme-mode="dark"]) .vlist-table-group-header{background-color:var(--vlist-group-header-bg,#1f2937)}:root:not([data-theme-mode="light"]):not([data-theme-mode="dark"]) .vlist-table-group-header-content{color:var(--vlist-text-muted,#9ca3af)}}.vlist-table-row.vlist-item--placeholder .vlist-table-cell{color:transparent}.vlist-table-row.vlist-item--placeholder .vlist-table-cell>*{color:transparent;background-color:var(--vlist-border,#e5e7eb);border-radius:4px;animation:vlist-placeholder-pulse 2s ease-in-out infinite;line-height:1;min-width:1em}.vlist-table-row.vlist-item--placeholder .vlist-table-cell>*>*{visibility:hidden}.vlist-table-row.vlist-item--placeholder .vlist-table-cell>*[style]{background:var(--vlist-border,#e5e7eb) !important;color:transparent !important}[data-theme-mode="dark"] .vlist--table-row-borders .vlist-table-group-header,.dark .vlist--table-row-borders .vlist-table-group-header{border-top-color:var(--vlist-border,#374151)}@media (prefers-color-scheme:dark){:root:not([data-theme-mode="light"]):not([data-theme-mode="dark"]) .vlist--table-row-borders .vlist-table-group-header{border-top-color:var(--vlist-border,#374151)}}
|
package/dist/vlist.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
@keyframes vlist-fade-in{from{opacity:0.6}to{opacity:1}}@keyframes vlist-placeholder-pulse{0%,100%{opacity:0.6}50%{opacity:0.4}}:root{--vlist-scrollbar-width:8px;--vlist-scrollbar-track-color:transparent;--vlist-scrollbar-radius:4px;--vlist-custom-scrollbar-width:8px;--vlist-custom-scrollbar-track-color:transparent;--vlist-custom-scrollbar-radius:4px;--vlist-custom-scrollbar-padding-top:2px;--vlist-custom-scrollbar-padding-right:2px;--vlist-custom-scrollbar-padding-bottom:2px;--vlist-custom-scrollbar-padding-left:2px;--vlist-custom-scrollbar-min-thumb-size:15px;--vlist-item-padding-x:1rem;--vlist-item-padding-y:0.75rem;--vlist-border-radius:0.5rem;--vlist-transition-duration:150ms;--vlist-transition-timing:ease-in-out;--vlist-bg:#ffffff;--vlist-bg-hover:rgba(0,0,0,0.04);--vlist-bg-striped:rgba(0,0,0,0.02);--vlist-bg-selected:rgba(59,130,246,0.12);--vlist-bg-selected-hover:rgba(59,130,246,0.18);--vlist-border:#e5e7eb;--vlist-border-selected:#3b82f6;--vlist-text:#111827;--vlist-text-muted:#6b7280;--vlist-focus-ring:#3b82f6;--vlist-group-header-bg:#f3f4f6;--vlist-scrollbar-thumb-color:#d1d5db;--vlist-scrollbar-thumb-hover-color:#9ca3af;--vlist-custom-scrollbar-thumb-color:rgba(0,0,0,0.2);--vlist-custom-scrollbar-thumb-hover-color:rgba(0,0,0,0.3);--vlist-placeholder-bg:rgba(0,0,0,0.2)}[data-theme-mode="dark"]{--vlist-bg:#111827;--vlist-bg-hover:rgba(255,255,255,0.06);--vlist-bg-striped:rgba(255,255,255,0.02);--vlist-bg-selected:rgba(59,130,246,0.2);--vlist-bg-selected-hover:rgba(59,130,246,0.28);--vlist-border:#374151;--vlist-border-selected:#3b82f6;--vlist-text:#f9fafb;--vlist-text-muted:#9ca3af;--vlist-focus-ring:#3b82f6;--vlist-group-header-bg:#1e2433;--vlist-scrollbar-thumb-color:#4b5563;--vlist-scrollbar-thumb-hover-color:#6b7280;--vlist-custom-scrollbar-thumb-color:rgba(255,255,255,0.2);--vlist-custom-scrollbar-thumb-hover-color:rgba(255,255,255,0.3);--vlist-placeholder-bg:rgba(255,255,255,0.3)}@media (prefers-color-scheme:dark){:root:not([data-theme-mode="light"]):not([data-theme-mode="dark"]){--vlist-bg:#111827;--vlist-bg-hover:rgba(255,255,255,0.06);--vlist-bg-striped:rgba(255,255,255,0.02);--vlist-bg-selected:rgba(59,130,246,0.2);--vlist-bg-selected-hover:rgba(59,130,246,0.28);--vlist-border:#374151;--vlist-border-selected:#3b82f6;--vlist-text:#f9fafb;--vlist-text-muted:#9ca3af;--vlist-focus-ring:#3b82f6;--vlist-group-header-bg:#1e2433;--vlist-scrollbar-thumb-color:#4b5563;--vlist-scrollbar-thumb-hover-color:#6b7280;--vlist-custom-scrollbar-thumb-color:rgba(255,255,255,0.2);--vlist-custom-scrollbar-thumb-hover-color:rgba(255,255,255,0.3);--vlist-placeholder-bg:rgba(255,255,255,0.3)}}.dark{--vlist-bg:#111827;--vlist-bg-hover:rgba(255,255,255,0.06);--vlist-bg-striped:rgba(255,255,255,0.02);--vlist-bg-selected:rgba(59,130,246,0.2);--vlist-bg-selected-hover:rgba(59,130,246,0.28);--vlist-border:#374151;--vlist-border-selected:#3b82f6;--vlist-text:#f9fafb;--vlist-text-muted:#9ca3af;--vlist-focus-ring:#3b82f6;--vlist-group-header-bg:#1e2433;--vlist-scrollbar-thumb-color:#4b5563;--vlist-scrollbar-thumb-hover-color:#6b7280;--vlist-custom-scrollbar-thumb-color:rgba(255,255,255,0.2);--vlist-custom-scrollbar-thumb-hover-color:rgba(255,255,255,0.3);--vlist-placeholder-bg:rgba(255,255,255,0.3)}.vlist{position:relative;width:100%;height:100%;box-sizing:border-box;overflow:hidden;background-color:var(--vlist-bg);color:var(--vlist-text);color-scheme:light;border:1px solid var(--vlist-border);border-radius:var(--vlist-border-radius);outline:none}[data-theme-mode="dark"] .vlist,.dark .vlist{color-scheme:dark}@media (prefers-color-scheme:dark){:root:not([data-theme-mode="light"]) .vlist{color-scheme:dark}}.vlist:focus,.vlist:focus-visible,.vlist-
|
|
1
|
+
@keyframes vlist-fade-in{from{opacity:0.6}to{opacity:1}}@keyframes vlist-placeholder-pulse{0%,100%{opacity:0.6}50%{opacity:0.4}}:root{--vlist-scrollbar-width:8px;--vlist-scrollbar-track-color:transparent;--vlist-scrollbar-radius:4px;--vlist-custom-scrollbar-width:8px;--vlist-custom-scrollbar-track-color:transparent;--vlist-custom-scrollbar-radius:4px;--vlist-custom-scrollbar-padding-top:2px;--vlist-custom-scrollbar-padding-right:2px;--vlist-custom-scrollbar-padding-bottom:2px;--vlist-custom-scrollbar-padding-left:2px;--vlist-custom-scrollbar-min-thumb-size:15px;--vlist-item-padding-x:1rem;--vlist-item-padding-y:0.75rem;--vlist-border-radius:0.5rem;--vlist-transition-duration:150ms;--vlist-transition-timing:ease-in-out;--vlist-bg:#ffffff;--vlist-bg-hover:rgba(0,0,0,0.04);--vlist-bg-striped:rgba(0,0,0,0.02);--vlist-bg-selected:rgba(59,130,246,0.12);--vlist-bg-selected-hover:rgba(59,130,246,0.18);--vlist-border:#e5e7eb;--vlist-border-selected:#3b82f6;--vlist-text:#111827;--vlist-text-muted:#6b7280;--vlist-focus-ring:#3b82f6;--vlist-group-header-bg:#f3f4f6;--vlist-scrollbar-thumb-color:#d1d5db;--vlist-scrollbar-thumb-hover-color:#9ca3af;--vlist-custom-scrollbar-thumb-color:rgba(0,0,0,0.2);--vlist-custom-scrollbar-thumb-hover-color:rgba(0,0,0,0.3);--vlist-placeholder-bg:rgba(0,0,0,0.2);--vlist-placeholder-bg-strong:rgba(0,0,0,0.24)}[data-theme-mode="dark"]{--vlist-bg:#111827;--vlist-bg-hover:rgba(255,255,255,0.06);--vlist-bg-striped:rgba(255,255,255,0.02);--vlist-bg-selected:rgba(59,130,246,0.2);--vlist-bg-selected-hover:rgba(59,130,246,0.28);--vlist-border:#374151;--vlist-border-selected:#3b82f6;--vlist-text:#f9fafb;--vlist-text-muted:#9ca3af;--vlist-focus-ring:#3b82f6;--vlist-group-header-bg:#1e2433;--vlist-scrollbar-thumb-color:#4b5563;--vlist-scrollbar-thumb-hover-color:#6b7280;--vlist-custom-scrollbar-thumb-color:rgba(255,255,255,0.2);--vlist-custom-scrollbar-thumb-hover-color:rgba(255,255,255,0.3);--vlist-placeholder-bg:rgba(255,255,255,0.3);--vlist-placeholder-bg-strong:rgba(255,255,255,0.35)}@media (prefers-color-scheme:dark){:root:not([data-theme-mode="light"]):not([data-theme-mode="dark"]){--vlist-bg:#111827;--vlist-bg-hover:rgba(255,255,255,0.06);--vlist-bg-striped:rgba(255,255,255,0.02);--vlist-bg-selected:rgba(59,130,246,0.2);--vlist-bg-selected-hover:rgba(59,130,246,0.28);--vlist-border:#374151;--vlist-border-selected:#3b82f6;--vlist-text:#f9fafb;--vlist-text-muted:#9ca3af;--vlist-focus-ring:#3b82f6;--vlist-group-header-bg:#1e2433;--vlist-scrollbar-thumb-color:#4b5563;--vlist-scrollbar-thumb-hover-color:#6b7280;--vlist-custom-scrollbar-thumb-color:rgba(255,255,255,0.2);--vlist-custom-scrollbar-thumb-hover-color:rgba(255,255,255,0.3);--vlist-placeholder-bg:rgba(255,255,255,0.3);--vlist-placeholder-bg-strong:rgba(255,255,255,0.35)}}.dark{--vlist-bg:#111827;--vlist-bg-hover:rgba(255,255,255,0.06);--vlist-bg-striped:rgba(255,255,255,0.02);--vlist-bg-selected:rgba(59,130,246,0.2);--vlist-bg-selected-hover:rgba(59,130,246,0.28);--vlist-border:#374151;--vlist-border-selected:#3b82f6;--vlist-text:#f9fafb;--vlist-text-muted:#9ca3af;--vlist-focus-ring:#3b82f6;--vlist-group-header-bg:#1e2433;--vlist-scrollbar-thumb-color:#4b5563;--vlist-scrollbar-thumb-hover-color:#6b7280;--vlist-custom-scrollbar-thumb-color:rgba(255,255,255,0.2);--vlist-custom-scrollbar-thumb-hover-color:rgba(255,255,255,0.3);--vlist-placeholder-bg:rgba(255,255,255,0.3);--vlist-placeholder-bg-strong:rgba(255,255,255,0.35)}.vlist{position:relative;width:100%;height:100%;box-sizing:border-box;overflow:hidden;background-color:var(--vlist-bg);color:var(--vlist-text);color-scheme:light;border:1px solid var(--vlist-border);border-radius:var(--vlist-border-radius);outline:none}[data-theme-mode="dark"] .vlist,.dark .vlist{color-scheme:dark}@media (prefers-color-scheme:dark){:root:not([data-theme-mode="light"]) .vlist{color-scheme:dark}}.vlist:focus,.vlist:focus-visible,.vlist-content:focus,.vlist-content:focus-visible{outline:none}.vlist-viewport{width:100%;height:100%;overflow:auto;scrollbar-color:var(--vlist-scrollbar-thumb-color) var(--vlist-scrollbar-track-color)}@supports (-moz-appearance:none) and (scrollbar-width:1px){.vlist-viewport{scrollbar-width:var(--vlist-scrollbar-width)}}.vlist-viewport::-webkit-scrollbar{width:var(--vlist-scrollbar-width);height:var(--vlist-scrollbar-width)}.vlist-viewport::-webkit-scrollbar-track{background:var(--vlist-scrollbar-track-color)}.vlist-viewport::-webkit-scrollbar-thumb{background-color:var(--vlist-scrollbar-thumb-color);border-radius:var(--vlist-scrollbar-radius)}.vlist-viewport::-webkit-scrollbar-thumb:hover{background-color:var(--vlist-scrollbar-thumb-hover-color)}.vlist-content{position:relative;width:100%;contain:layout style}.vlist-item,.vlist-group-header{position:absolute;top:0;left:0;right:0;display:flex;align-items:center;background-color:var(--vlist-bg);cursor:default;user-select:none;opacity:1;transition:background-color var(--vlist-transition-duration) var(--vlist-transition-timing),opacity var(--vlist-transition-duration) var(--vlist-transition-timing);contain:content;box-sizing:border-box;will-change:transform}.vlist--scrolling .vlist-item,.vlist--settling .vlist-item,.vlist--scrolling .vlist-group-header,.vlist--settling .vlist-group-header{transition:none}.vlist--selectable .vlist-item{cursor:pointer}.vlist--selectable .vlist-item:hover{background-color:var(--vlist-bg-hover)}.vlist-item.vlist-item--odd{background:var(--vlist-bg-striped)}.vlist-item.vlist-item--selected{background-color:var(--vlist-bg-selected)}.vlist-item.vlist-item--odd.vlist-item--selected{background:var(--vlist-bg-selected)}.vlist-item.vlist-item--selected:hover{background-color:var(--vlist-bg-selected-hover)}.vlist-item.vlist-item--odd.vlist-item--selected:hover{background:var(--vlist-bg-selected-hover)}.vlist-item.vlist-item--focused{outline:2px solid var(--vlist-focus-ring,#3b82f6);outline-offset:-2px;z-index:1}.vlist-item--replaced{animation:vlist-fade-in 0.3s ease-out}.vlist-item--placeholder{opacity:0.6;animation:vlist-placeholder-pulse 2s ease-in-out infinite}.vlist-item--placeholder *:not(:has(*)){color:transparent;background-color:var(--vlist-placeholder-bg);border-radius:4px;width:fit-content;line-height:1.2;transform:scaleY(0.8)}.vlist-item--placeholder:is(strong,b,h1,h2,h3,h4):not(:has(*)){background-color:var(--vlist-placeholder-bg-strong)}.vlist-item.vlist-item--selected.vlist-item--focused{outline-color:var(--vlist-border-selected)}.vlist-scrollbar{position:absolute;top:var(--vlist-custom-scrollbar-padding-top);right:var(--vlist-custom-scrollbar-padding-right);bottom:var(--vlist-custom-scrollbar-padding-bottom);width:var(--vlist-custom-scrollbar-width);background-color:var(--vlist-custom-scrollbar-track-color);opacity:0;transition:opacity 0.2s ease-out;z-index:10;pointer-events:none}.vlist-scrollbar__hover{position:absolute;top:0;right:0;height:100%;z-index:9;pointer-events:auto}.vlist-scrollbar__hover--horizontal{top:auto;right:auto;bottom:0;left:0;width:100%;height:auto}.vlist-scrollbar--visible{opacity:1;pointer-events:auto}.vlist-scrollbar__thumb{position:absolute;top:0;left:0;right:0;width:100%;background-color:var(--vlist-custom-scrollbar-thumb-color);border-radius:var(--vlist-custom-scrollbar-radius);cursor:pointer;transition:background-color 0.15s ease-out}.vlist-scrollbar__thumb:hover{background-color:var(--vlist-custom-scrollbar-thumb-hover-color)}.vlist-scrollbar--dragging{opacity:1;pointer-events:auto}.vlist-scrollbar--dragging .vlist-scrollbar__thumb{background-color:var(--vlist-custom-scrollbar-thumb-hover-color)}.vlist-group-header{cursor:default;background-color:var(--vlist-group-header-bg,#f3f4f6);z-index:1;overflow:hidden}.vlist-group-header:hover{background-color:var(--vlist-group-header-bg,#f3f4f6)}.vlist-sticky-header{left:0;right:0;background-color:var(--vlist-group-header-bg,#f3f4f6)}.vlist--horizontal{overflow:hidden}.vlist--horizontal .vlist-viewport{overflow-x:auto;overflow-y:hidden}.vlist--horizontal .vlist-content{height:100%;width:auto}.vlist--horizontal .vlist-item,.vlist--horizontal .vlist-group-header{position:absolute;top:0;bottom:0;left:0;right:auto;display:flex;align-items:center;justify-content:center;border-bottom:none;border-right:1px solid var(--vlist-border)}.vlist-scrollbar--horizontal{top:auto;left:var(--vlist-custom-scrollbar-padding-left);right:var(--vlist-custom-scrollbar-padding-right);bottom:var(--vlist-custom-scrollbar-padding-bottom);width:auto;height:var(--vlist-custom-scrollbar-width)}.vlist-scrollbar--horizontal .vlist-scrollbar__thumb{top:0;left:0;width:auto;height:100%}.vlist-viewport--gutter{padding-right:calc( var(--vlist-custom-scrollbar-padding-right) + var(--vlist-custom-scrollbar-width) + var(--vlist-custom-scrollbar-padding-left) )}.vlist--horizontal .vlist-viewport--gutter{padding-right:0;padding-bottom:calc( var(--vlist-custom-scrollbar-padding-bottom) + var(--vlist-custom-scrollbar-width) + var(--vlist-custom-scrollbar-padding-top) )}.vlist-viewport--custom-scrollbar{scrollbar-width:none;-ms-overflow-style:none}.vlist-viewport--custom-scrollbar::-webkit-scrollbar{display:none}.vlist-viewport--no-scrollbar{scrollbar-width:none;-ms-overflow-style:none}.vlist-viewport--no-scrollbar::-webkit-scrollbar{display:none}.vlist-viewport--gutter-stable{scrollbar-gutter:stable}.vlist-sort-ghost{opacity:0.85;box-shadow:0 4px 16px rgba(0,0,0,0.18);cursor:grabbing}.vlist-item--drag-source{opacity:0 !important;pointer-events:none}.vlist--sorting{cursor:grabbing;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}.vlist--sorting *{cursor:inherit !important}.vlist-item--kb-sorting{outline:2px solid var(--vlist-focus-ring,#3b82f6);outline-offset:-2px;background-color:var(--vlist-bg-selected);z-index:1}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vlist",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Lightweight, high-performance virtual list with zero dependencies",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Floor IO",
|
|
@@ -47,8 +47,7 @@
|
|
|
47
47
|
"./package.json": "./package.json"
|
|
48
48
|
},
|
|
49
49
|
"files": [
|
|
50
|
-
"dist
|
|
51
|
-
"dist/internals.js",
|
|
50
|
+
"dist/**/*.js",
|
|
52
51
|
"dist/**/*.d.ts",
|
|
53
52
|
"dist/**/*.css",
|
|
54
53
|
"dist/size.json",
|
|
@@ -63,9 +62,13 @@
|
|
|
63
62
|
"test": "bun test",
|
|
64
63
|
"test:changed": "bun test --changed",
|
|
65
64
|
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json",
|
|
65
|
+
"bench": "bun run scripts/bench.ts",
|
|
66
|
+
"bench:quick": "bun run scripts/bench.ts --quick",
|
|
67
|
+
"bench:full": "bun run scripts/bench.ts --full",
|
|
68
|
+
"bench:compare": "bun run scripts/bench.ts --quick --compare",
|
|
66
69
|
"size": "bun run scripts/measure-size.ts",
|
|
67
70
|
"release": "bun run scripts/release.ts",
|
|
68
|
-
"prepublishOnly": "bun run build --types && cp README.md README.github.md && cp npm-readme.md README.md",
|
|
71
|
+
"prepublishOnly": "bun run build -- --types && cp README.md README.github.md && cp npm-readme.md README.md",
|
|
69
72
|
"postpublish": "cp README.github.md README.md && rm README.github.md"
|
|
70
73
|
},
|
|
71
74
|
"bugs": {
|
|
@@ -73,8 +76,10 @@
|
|
|
73
76
|
},
|
|
74
77
|
"homepage": "https://vlist.io",
|
|
75
78
|
"devDependencies": {
|
|
79
|
+
"@happy-dom/global-registrator": "^20.9.0",
|
|
76
80
|
"@types/bun": "^1.0.0",
|
|
77
81
|
"@types/jsdom": "^27.0.0",
|
|
82
|
+
"happy-dom": "^20.9.0",
|
|
78
83
|
"jsdom": "^28.0.0",
|
|
79
84
|
"typescript": "^5.0.0"
|
|
80
85
|
}
|
package/dist/builder/a11y.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* vlist/builder -- Baseline ARIA keyboard navigation
|
|
3
|
-
*
|
|
4
|
-
* Extracted from core.ts to reduce the base bundle size.
|
|
5
|
-
* Implements the WAI-ARIA listbox pattern: arrow keys move focus,
|
|
6
|
-
* Space/Enter selects, click selects + focuses.
|
|
7
|
-
*/
|
|
8
|
-
import type { VListItem } from "../types";
|
|
9
|
-
import type { MRefs } from "./materialize";
|
|
10
|
-
import type { DOMStructure } from "./dom";
|
|
11
|
-
/**
|
|
12
|
-
* Wire up baseline single-select ARIA keyboard navigation.
|
|
13
|
-
* Registers focus/blur listeners, keydown handler, click handler, and destroy cleanup.
|
|
14
|
-
*/
|
|
15
|
-
export declare const setupBaselineA11y: <T extends VListItem>($: MRefs<T>, dom: DOMStructure, cp: string, ap: string, hz: boolean, ps: number, pe: number, wr: boolean, methods: Map<string, Function>, rendered: Map<number, HTMLElement>, keydownHandlers: Array<(event: KeyboardEvent) => void>, clickHandlers: Array<(event: MouseEvent) => void>, destroyHandlers: Array<() => void>, foc: boolean) => void;
|
|
16
|
-
//# sourceMappingURL=a11y.d.ts.map
|