vlist 2.0.0 → 2.0.1

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 (77) hide show
  1. package/dist/index.js +1 -28
  2. package/dist/internals.js +1 -60
  3. package/package.json +1 -1
  4. package/dist/constants.js +0 -83
  5. package/dist/core/create.js +0 -740
  6. package/dist/core/dom.js +0 -47
  7. package/dist/core/hooks.js +0 -67
  8. package/dist/core/index.js +0 -13
  9. package/dist/core/pipeline.js +0 -307
  10. package/dist/core/pool.js +0 -42
  11. package/dist/core/scroll.js +0 -137
  12. package/dist/core/sizes.js +0 -6
  13. package/dist/core/state.js +0 -56
  14. package/dist/core/types.js +0 -7
  15. package/dist/core/velocity.js +0 -33
  16. package/dist/events/emitter.js +0 -60
  17. package/dist/events/index.js +0 -6
  18. package/dist/plugins/a11y/index.js +0 -1
  19. package/dist/plugins/a11y/plugin.js +0 -259
  20. package/dist/plugins/async/index.js +0 -12
  21. package/dist/plugins/async/manager.js +0 -568
  22. package/dist/plugins/async/placeholder.js +0 -154
  23. package/dist/plugins/async/plugin.js +0 -311
  24. package/dist/plugins/async/sparse.js +0 -540
  25. package/dist/plugins/autosize/index.js +0 -4
  26. package/dist/plugins/autosize/plugin.js +0 -185
  27. package/dist/plugins/grid/index.js +0 -5
  28. package/dist/plugins/grid/layout.js +0 -275
  29. package/dist/plugins/grid/plugin.js +0 -347
  30. package/dist/plugins/grid/renderer.js +0 -525
  31. package/dist/plugins/grid/types.js +0 -11
  32. package/dist/plugins/groups/async-bridge.js +0 -246
  33. package/dist/plugins/groups/index.js +0 -13
  34. package/dist/plugins/groups/layout.js +0 -294
  35. package/dist/plugins/groups/plugin.js +0 -571
  36. package/dist/plugins/groups/sticky.js +0 -255
  37. package/dist/plugins/groups/types.js +0 -12
  38. package/dist/plugins/masonry/index.js +0 -6
  39. package/dist/plugins/masonry/layout.js +0 -261
  40. package/dist/plugins/masonry/plugin.js +0 -381
  41. package/dist/plugins/masonry/renderer.js +0 -354
  42. package/dist/plugins/masonry/types.js +0 -9
  43. package/dist/plugins/page/index.js +0 -5
  44. package/dist/plugins/page/plugin.js +0 -166
  45. package/dist/plugins/scale/index.js +0 -4
  46. package/dist/plugins/scale/plugin.js +0 -507
  47. package/dist/plugins/scrollbar/controller.js +0 -574
  48. package/dist/plugins/scrollbar/index.js +0 -6
  49. package/dist/plugins/scrollbar/plugin.js +0 -93
  50. package/dist/plugins/scrollbar/scrollbar.js +0 -556
  51. package/dist/plugins/selection/index.js +0 -7
  52. package/dist/plugins/selection/plugin.js +0 -601
  53. package/dist/plugins/selection/state.js +0 -332
  54. package/dist/plugins/snapshots/index.js +0 -5
  55. package/dist/plugins/snapshots/plugin.js +0 -301
  56. package/dist/plugins/sortable/index.js +0 -6
  57. package/dist/plugins/sortable/plugin.js +0 -753
  58. package/dist/plugins/table/header.js +0 -501
  59. package/dist/plugins/table/index.js +0 -12
  60. package/dist/plugins/table/layout.js +0 -211
  61. package/dist/plugins/table/plugin.js +0 -391
  62. package/dist/plugins/table/renderer.js +0 -625
  63. package/dist/plugins/table/types.js +0 -12
  64. package/dist/plugins/transition/index.js +0 -5
  65. package/dist/plugins/transition/plugin.js +0 -405
  66. package/dist/rendering/aria.js +0 -23
  67. package/dist/rendering/index.js +0 -18
  68. package/dist/rendering/measured.js +0 -98
  69. package/dist/rendering/renderer.js +0 -586
  70. package/dist/rendering/scale.js +0 -267
  71. package/dist/rendering/scroll.js +0 -71
  72. package/dist/rendering/sizes.js +0 -193
  73. package/dist/rendering/sort.js +0 -65
  74. package/dist/rendering/viewport.js +0 -268
  75. package/dist/types.js +0 -5
  76. package/dist/utils/padding.js +0 -49
  77. package/dist/utils/stats.js +0 -124
@@ -1,7 +0,0 @@
1
- /**
2
- * vlist v2 — Core Type Definitions
3
- *
4
- * Zero-allocation pipeline types. All hot-path state lives in TypedArrays
5
- * on the EngineState singleton — no intermediate object allocation.
6
- */
7
- export {};
@@ -1,33 +0,0 @@
1
- /**
2
- * vlist v2 — Velocity Tracking
3
- * Lightweight 2-sample velocity tracker for scroll momentum detection.
4
- */
5
- export const MIN_RELIABLE_SAMPLES = 2;
6
- /** Create a velocity tracker. */
7
- export const createVelocityTracker = (_initialPosition = 0) => ({
8
- velocity: 0,
9
- sampleCount: 0,
10
- });
11
- /** Update velocity from new scroll position. Mutates tracker in place. */
12
- export const updateVelocityTracker = (tracker, newPosition) => {
13
- const now = performance.now();
14
- const lastTime = tracker._lt ?? now;
15
- const lastPos = tracker._lp ?? newPosition;
16
- const dt = now - lastTime;
17
- tracker._lp = newPosition;
18
- tracker._lt = now;
19
- // Zero time delta — record position but can't compute velocity
20
- if (dt === 0) {
21
- tracker.sampleCount = Math.min(tracker.sampleCount + 1, 5);
22
- return tracker;
23
- }
24
- // Stale gap — reset
25
- if (dt > 100) {
26
- tracker.velocity = 0;
27
- tracker.sampleCount = 1;
28
- return tracker;
29
- }
30
- tracker.velocity = Math.abs(newPosition - lastPos) / dt;
31
- tracker.sampleCount = Math.min(tracker.sampleCount + 1, 5);
32
- return tracker;
33
- };
@@ -1,60 +0,0 @@
1
- /**
2
- * vlist - Event Emitter
3
- * Lightweight, type-safe event system
4
- */
5
- // =============================================================================
6
- // Event Emitter
7
- // =============================================================================
8
- /**
9
- * Create a type-safe event emitter
10
- * Functional approach - returns an object with methods
11
- */
12
- export const createEmitter = () => {
13
- const listeners = {};
14
- /**
15
- * Subscribe to an event
16
- */
17
- const on = (event, handler) => {
18
- if (!listeners[event]) {
19
- listeners[event] = new Set();
20
- }
21
- listeners[event].add(handler);
22
- // Return unsubscribe function
23
- return () => off(event, handler);
24
- };
25
- /**
26
- * Unsubscribe from an event
27
- */
28
- const off = (event, handler) => {
29
- listeners[event]?.delete(handler);
30
- };
31
- /**
32
- * Emit an event to all subscribers
33
- */
34
- const emit = (event, payload) => {
35
- listeners[event]?.forEach((handler) => {
36
- try {
37
- handler(payload);
38
- }
39
- catch (error) {
40
- if (process.env.NODE_ENV !== "production") {
41
- console.error(`[vlist] Error in event handler for "${String(event)}":`, error);
42
- }
43
- }
44
- });
45
- };
46
- /**
47
- * Remove all listeners for an event (or all events if no event specified)
48
- */
49
- const clear = (event) => {
50
- if (event) {
51
- delete listeners[event];
52
- }
53
- else {
54
- for (const key in listeners) {
55
- delete listeners[key];
56
- }
57
- }
58
- };
59
- return { on, off, emit, clear };
60
- };
@@ -1,6 +0,0 @@
1
- /**
2
- * vlist - Events Domain
3
- * Event emitter system
4
- */
5
- // Event Emitter
6
- export { createEmitter } from "./emitter";
@@ -1 +0,0 @@
1
- export { a11y } from "./plugin";
@@ -1,259 +0,0 @@
1
- /**
2
- * vlist v2 — A11y Plugin
3
- *
4
- * Baseline keyboard navigation, single-select, focus management, and ARIA.
5
- * Extracted from createVList() so it tree-shakes when selection() is used.
6
- *
7
- * Priority 55 — runs after selection (50). If selection already set
8
- * itemStateFn, this plugin becomes a no-op.
9
- */
10
- export function a11y() {
11
- return {
12
- name: "a11y",
13
- priority: 55,
14
- setup(ctx) {
15
- if (ctx.getItemStateFn())
16
- return;
17
- const dom = ctx.dom;
18
- const config = ctx.config;
19
- const sizeCache = ctx.sizeCache;
20
- const engineState = ctx.getState();
21
- const emitter = ctx.emitter;
22
- const classPrefix = config.classPrefix;
23
- const liveRegion = dom.liveRegion;
24
- let focusIdx = -1;
25
- let focusVis = false;
26
- let selId;
27
- let selIdx = -1;
28
- const getItem = (i) => ctx.getItem(i);
29
- const getTotal = () => engineState.totalItems;
30
- const _focusEvt = { id: 0, index: 0 };
31
- const _selEvt = { selected: [], items: [] };
32
- ctx.setItemStateFn((_i, is) => {
33
- is.selected = selIdx === _i;
34
- is.focused = focusVis && focusIdx === _i;
35
- });
36
- function announce(message) {
37
- liveRegion.textContent = "";
38
- liveRegion.textContent = message;
39
- }
40
- const skip = (from, dir, total) => {
41
- let i = from;
42
- while (i >= 0 && i < total) {
43
- const it = getItem(i);
44
- if (!it || !it.__groupHeader)
45
- return i;
46
- i += dir;
47
- }
48
- i = from - dir;
49
- while (i >= 0 && i < total) {
50
- const it = getItem(i);
51
- if (!it || !it.__groupHeader)
52
- return i;
53
- i -= dir;
54
- }
55
- return from;
56
- };
57
- const scrollIntoView = (idx) => {
58
- const nav = ctx.getNavConfig();
59
- const ci = nav.scrollIndex ? nav.scrollIndex(idx) : idx;
60
- const off = sizeCache.getOffset(ci);
61
- const sz = sizeCache.getSize(ci);
62
- const sp = engineState.scrollPosition;
63
- const cs = engineState.containerSize;
64
- const sP = config.startPadding;
65
- const eP = config.endPadding;
66
- const adjTop = off + sP;
67
- const adjBot = adjTop + sz;
68
- let pos = sp;
69
- if (adjTop < sp)
70
- pos = Math.max(0, off);
71
- else if (adjBot > sp + cs)
72
- pos = adjBot + eP - cs;
73
- if (pos !== sp) {
74
- engineState.scrollPosition = pos;
75
- ctx.scrollTo(pos);
76
- }
77
- };
78
- const commit = (idx, scroll) => {
79
- dom.content.setAttribute("aria-activedescendant", `${classPrefix}-item-${idx}`);
80
- if (scroll)
81
- scrollIntoView(idx);
82
- ctx.forceRender();
83
- };
84
- const move = (next) => {
85
- focusIdx = next;
86
- focusVis = true;
87
- commit(next, true);
88
- const it = getItem(next);
89
- if (it) {
90
- _focusEvt.id = it.id;
91
- _focusEvt.index = next;
92
- emitter.emit("focus:change", _focusEvt);
93
- announce(`Item ${next + 1} of ${getTotal()}`);
94
- }
95
- };
96
- const select = (idx, kbd) => {
97
- focusIdx = idx;
98
- if (kbd)
99
- focusVis = true;
100
- const it = getItem(idx);
101
- if (it && selId === it.id) {
102
- selId = undefined;
103
- selIdx = -1;
104
- }
105
- else {
106
- selId = it?.id;
107
- selIdx = it ? idx : -1;
108
- }
109
- commit(idx, kbd);
110
- if (selId !== undefined && it && selId === it.id) {
111
- _selEvt.selected[0] = selId;
112
- _selEvt.selected.length = 1;
113
- _selEvt.items[0] = it;
114
- _selEvt.items.length = 1;
115
- announce(`Selected, item ${idx + 1} of ${getTotal()}`);
116
- }
117
- else {
118
- _selEvt.selected.length = 0;
119
- _selEvt.items.length = 0;
120
- announce(`Deselected`);
121
- }
122
- emitter.emit("selection:change", _selEvt);
123
- };
124
- // ── Focus handlers ──────────────────────────────────────────
125
- const onFocusIn = () => {
126
- if (engineState.destroyed)
127
- return;
128
- if (!dom.content.matches(":focus-visible"))
129
- return;
130
- const t = getTotal();
131
- if (t === 0)
132
- return;
133
- let tgt = focusIdx >= 0 ? Math.min(focusIdx, t - 1) : 0;
134
- tgt = skip(tgt, 1, t);
135
- move(tgt);
136
- };
137
- const onFocusOut = (e) => {
138
- if (engineState.destroyed)
139
- return;
140
- const rel = e.relatedTarget;
141
- if (rel && dom.root.contains(rel))
142
- return;
143
- focusVis = false;
144
- dom.content.removeAttribute("aria-activedescendant");
145
- ctx.forceRender();
146
- };
147
- dom.content.addEventListener("focusin", onFocusIn);
148
- dom.content.addEventListener("focusout", onFocusOut);
149
- // ── Keyboard handler ────────────────────────────────────────
150
- ctx.registerKeydownHandler((e) => {
151
- if (engineState.destroyed)
152
- return;
153
- const total = getTotal();
154
- if (total === 0)
155
- return;
156
- const p = focusIdx;
157
- let n = p;
158
- if (e.key === " " || e.key === "Enter") {
159
- if (p >= 0) {
160
- const it = getItem(p);
161
- if (it && !it.__groupHeader) {
162
- select(p, true);
163
- }
164
- }
165
- e.preventDefault();
166
- return;
167
- }
168
- const nav = ctx.getNavConfig();
169
- if (nav.navigate) {
170
- switch (e.key) {
171
- case "ArrowUp":
172
- case "ArrowDown":
173
- case "ArrowLeft":
174
- case "ArrowRight":
175
- case "PageUp":
176
- case "PageDown":
177
- case "Home":
178
- case "End":
179
- n = nav.navigate(p, e.key, total);
180
- break;
181
- default: return;
182
- }
183
- }
184
- else {
185
- const ud = nav.ud || 1;
186
- const lr = nav.lr;
187
- const hz = config.horizontal;
188
- switch (e.key) {
189
- case "ArrowUp":
190
- if (hz && !lr)
191
- return;
192
- n = p - (hz ? lr : ud);
193
- break;
194
- case "ArrowDown":
195
- if (hz && !lr)
196
- return;
197
- n = p + (hz ? lr : ud);
198
- break;
199
- case "ArrowLeft":
200
- if (!hz && !lr)
201
- return;
202
- n = p - (hz ? ud : lr);
203
- break;
204
- case "ArrowRight":
205
- if (!hz && !lr)
206
- return;
207
- n = p + (hz ? ud : lr);
208
- break;
209
- case "PageUp":
210
- case "PageDown": {
211
- const sz = sizeCache.getSize(Math.max(0, nav.scrollIndex ? nav.scrollIndex(p) : p));
212
- const visRows = Math.max(1, Math.floor(engineState.containerSize / sz));
213
- const delta = visRows * ud;
214
- n = e.key === "PageUp" ? p - delta : p + delta;
215
- break;
216
- }
217
- case "Home":
218
- n = 0;
219
- break;
220
- case "End":
221
- n = total - 1;
222
- break;
223
- default: return;
224
- }
225
- }
226
- if (n < 0)
227
- n = 0;
228
- else if (n >= total)
229
- n = total - 1;
230
- e.preventDefault();
231
- n = skip(n, n >= p ? 1 : -1, total);
232
- if (n !== p)
233
- move(n);
234
- });
235
- // ── Click handler ───────────────────────────────────────────
236
- ctx.registerClickHandler((e) => {
237
- if (engineState.destroyed)
238
- return;
239
- const el = e.target.closest("[data-index]");
240
- if (!el)
241
- return;
242
- const idx = parseInt(el.dataset.index ?? "-1", 10);
243
- if (idx < 0)
244
- return;
245
- const it = getItem(idx);
246
- if (!it || it.__groupHeader)
247
- return;
248
- focusVis = false;
249
- dom.content.focus({ preventScroll: true });
250
- select(idx, false);
251
- });
252
- // ── Cleanup ─────────────────────────────────────────────────
253
- ctx.registerDestroyHandler(() => {
254
- dom.content.removeEventListener("focusin", onFocusIn);
255
- dom.content.removeEventListener("focusout", onFocusOut);
256
- });
257
- },
258
- };
259
- }
@@ -1,12 +0,0 @@
1
- /**
2
- * vlist - Data Domain
3
- * Data management, sparse storage, and placeholder generation
4
- */
5
- // v2 Plugin
6
- export { data } from "./plugin";
7
- // Data Manager
8
- export { createDataManager, mergeRanges, calculateMissingRanges, isPlaceholderItem, filterPlaceholders, countRealItems, } from "./manager";
9
- // Sparse Storage
10
- export { createSparseStorage, } from "./sparse";
11
- // Placeholder
12
- export { createPlaceholderManager, } from "./placeholder";