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.
- package/dist/index.js +1 -28
- package/dist/internals.js +1 -60
- package/package.json +1 -1
- package/dist/constants.js +0 -83
- package/dist/core/create.js +0 -740
- package/dist/core/dom.js +0 -47
- package/dist/core/hooks.js +0 -67
- package/dist/core/index.js +0 -13
- package/dist/core/pipeline.js +0 -307
- package/dist/core/pool.js +0 -42
- package/dist/core/scroll.js +0 -137
- package/dist/core/sizes.js +0 -6
- package/dist/core/state.js +0 -56
- package/dist/core/types.js +0 -7
- package/dist/core/velocity.js +0 -33
- package/dist/events/emitter.js +0 -60
- package/dist/events/index.js +0 -6
- package/dist/plugins/a11y/index.js +0 -1
- package/dist/plugins/a11y/plugin.js +0 -259
- package/dist/plugins/async/index.js +0 -12
- package/dist/plugins/async/manager.js +0 -568
- package/dist/plugins/async/placeholder.js +0 -154
- package/dist/plugins/async/plugin.js +0 -311
- package/dist/plugins/async/sparse.js +0 -540
- package/dist/plugins/autosize/index.js +0 -4
- package/dist/plugins/autosize/plugin.js +0 -185
- package/dist/plugins/grid/index.js +0 -5
- package/dist/plugins/grid/layout.js +0 -275
- package/dist/plugins/grid/plugin.js +0 -347
- package/dist/plugins/grid/renderer.js +0 -525
- package/dist/plugins/grid/types.js +0 -11
- package/dist/plugins/groups/async-bridge.js +0 -246
- package/dist/plugins/groups/index.js +0 -13
- package/dist/plugins/groups/layout.js +0 -294
- package/dist/plugins/groups/plugin.js +0 -571
- package/dist/plugins/groups/sticky.js +0 -255
- package/dist/plugins/groups/types.js +0 -12
- package/dist/plugins/masonry/index.js +0 -6
- package/dist/plugins/masonry/layout.js +0 -261
- package/dist/plugins/masonry/plugin.js +0 -381
- package/dist/plugins/masonry/renderer.js +0 -354
- package/dist/plugins/masonry/types.js +0 -9
- package/dist/plugins/page/index.js +0 -5
- package/dist/plugins/page/plugin.js +0 -166
- package/dist/plugins/scale/index.js +0 -4
- package/dist/plugins/scale/plugin.js +0 -507
- package/dist/plugins/scrollbar/controller.js +0 -574
- package/dist/plugins/scrollbar/index.js +0 -6
- package/dist/plugins/scrollbar/plugin.js +0 -93
- package/dist/plugins/scrollbar/scrollbar.js +0 -556
- package/dist/plugins/selection/index.js +0 -7
- package/dist/plugins/selection/plugin.js +0 -601
- package/dist/plugins/selection/state.js +0 -332
- package/dist/plugins/snapshots/index.js +0 -5
- package/dist/plugins/snapshots/plugin.js +0 -301
- package/dist/plugins/sortable/index.js +0 -6
- package/dist/plugins/sortable/plugin.js +0 -753
- package/dist/plugins/table/header.js +0 -501
- package/dist/plugins/table/index.js +0 -12
- package/dist/plugins/table/layout.js +0 -211
- package/dist/plugins/table/plugin.js +0 -391
- package/dist/plugins/table/renderer.js +0 -625
- package/dist/plugins/table/types.js +0 -12
- package/dist/plugins/transition/index.js +0 -5
- package/dist/plugins/transition/plugin.js +0 -405
- package/dist/rendering/aria.js +0 -23
- package/dist/rendering/index.js +0 -18
- package/dist/rendering/measured.js +0 -98
- package/dist/rendering/renderer.js +0 -586
- package/dist/rendering/scale.js +0 -267
- package/dist/rendering/scroll.js +0 -71
- package/dist/rendering/sizes.js +0 -193
- package/dist/rendering/sort.js +0 -65
- package/dist/rendering/viewport.js +0 -268
- package/dist/types.js +0 -5
- package/dist/utils/padding.js +0 -49
- package/dist/utils/stats.js +0 -124
|
@@ -1,405 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* vlist v2 — Transition Plugin
|
|
3
|
-
*
|
|
4
|
-
* FLIP-based enter/exit animations for insertItem and removeItem.
|
|
5
|
-
* Without this plugin, insert/remove are instantaneous.
|
|
6
|
-
*
|
|
7
|
-
* Priority: 45 (before selection, after most layout plugins)
|
|
8
|
-
*/
|
|
9
|
-
const DEFAULT_DURATION = 200;
|
|
10
|
-
const MAX_DURATION = 1000;
|
|
11
|
-
const DEFAULT_EASING = "cubic-bezier(0.2, 0, 0, 1)";
|
|
12
|
-
function resolveTiming(base, override) {
|
|
13
|
-
if (override === false)
|
|
14
|
-
return null;
|
|
15
|
-
return {
|
|
16
|
-
duration: Math.min(override?.duration ?? base.duration, MAX_DURATION),
|
|
17
|
-
easing: override?.easing ?? base.easing,
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
// =============================================================================
|
|
21
|
-
// Factory
|
|
22
|
-
// =============================================================================
|
|
23
|
-
export function transition(config) {
|
|
24
|
-
const baseDuration = config?.duration ?? DEFAULT_DURATION;
|
|
25
|
-
const baseEasing = config?.easing ?? DEFAULT_EASING;
|
|
26
|
-
const base = { duration: baseDuration, easing: baseEasing };
|
|
27
|
-
const removeTiming = resolveTiming(base, config?.remove);
|
|
28
|
-
const insertTiming = resolveTiming(base, config?.insert);
|
|
29
|
-
let removePending = null;
|
|
30
|
-
let addPending = null;
|
|
31
|
-
return {
|
|
32
|
-
name: "transition",
|
|
33
|
-
conflicts: ["grid", "table", "masonry"],
|
|
34
|
-
priority: 45,
|
|
35
|
-
setup(ctx) {
|
|
36
|
-
const { sizeCache: sc, emitter } = ctx;
|
|
37
|
-
const cfg = ctx.config;
|
|
38
|
-
const dom = ctx.dom;
|
|
39
|
-
const state = ctx.getState();
|
|
40
|
-
const origin = cfg.reverse ? "bottom center" : "top center";
|
|
41
|
-
const prop = cfg.horizontal ? "translateX" : "translateY";
|
|
42
|
-
const toLayout = ctx.getMethod("_dataToLayoutIndex") ?? null;
|
|
43
|
-
const dataToLayout = (dataIndex) => toLayout ? toLayout(dataIndex) : dataIndex;
|
|
44
|
-
const findById = (id) => {
|
|
45
|
-
const eid = String(id).replace(/"/g, '\\"');
|
|
46
|
-
const el = dom.content.querySelector(`[data-id="${eid}"]`);
|
|
47
|
-
const layoutIndex = el ? parseInt(el.dataset.index ?? "-1", 10) : -1;
|
|
48
|
-
return { el, layoutIndex };
|
|
49
|
-
};
|
|
50
|
-
const commitStyles = () => {
|
|
51
|
-
const children = dom.content.children;
|
|
52
|
-
for (let i = 0; i < children.length; i++) {
|
|
53
|
-
children[i].style.transition = "none";
|
|
54
|
-
}
|
|
55
|
-
dom.content.offsetHeight;
|
|
56
|
-
for (let i = 0; i < children.length; i++) {
|
|
57
|
-
children[i].style.transition = "";
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
// ── Animated removeItem ──────────────────────────────────
|
|
61
|
-
if (removeTiming) {
|
|
62
|
-
const removeItem = (id) => {
|
|
63
|
-
if (removePending)
|
|
64
|
-
removePending();
|
|
65
|
-
const active = typeof document !== "undefined" ? document.activeElement : null;
|
|
66
|
-
const focIdx = active && dom.content.contains(active)
|
|
67
|
-
? parseInt(active.dataset?.index ?? "-1", 10)
|
|
68
|
-
: -1;
|
|
69
|
-
const found = findById(id);
|
|
70
|
-
const layoutIndex = found.layoutIndex;
|
|
71
|
-
const removedEl = found.el;
|
|
72
|
-
const itemSize = layoutIndex >= 0 ? sc.getSize(layoutIndex) : 0;
|
|
73
|
-
const originalOffset = layoutIndex >= 0 ? sc.getOffset(layoutIndex) : 0;
|
|
74
|
-
if (!removedEl || layoutIndex < 0) {
|
|
75
|
-
const result = ctx.removeItemById(id);
|
|
76
|
-
if (result < 0)
|
|
77
|
-
return false;
|
|
78
|
-
ctx.forceRender();
|
|
79
|
-
commitStyles();
|
|
80
|
-
emitter.emit("data:change", { type: "remove", id });
|
|
81
|
-
if (focIdx >= 0) {
|
|
82
|
-
const t = state.totalItems;
|
|
83
|
-
if (t > 0)
|
|
84
|
-
ctx.getRenderedElement(Math.min(focIdx, t - 1))?.focus();
|
|
85
|
-
}
|
|
86
|
-
emitter.emit("remove:end", { id });
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
// FIRST — clone before removal
|
|
90
|
-
const exitClone = removedEl.cloneNode(true);
|
|
91
|
-
exitClone.style.pointerEvents = "none";
|
|
92
|
-
exitClone.style.overflow = "hidden";
|
|
93
|
-
exitClone.removeAttribute("data-index");
|
|
94
|
-
exitClone.removeAttribute("data-id");
|
|
95
|
-
exitClone.removeAttribute("id");
|
|
96
|
-
exitClone.removeAttribute("aria-selected");
|
|
97
|
-
exitClone.classList.remove(`${cfg.classPrefix}-item--selected`);
|
|
98
|
-
const scrollProp = cfg.horizontal ? "scrollLeft" : "scrollTop";
|
|
99
|
-
const oldScroll = dom.viewport[scrollProp];
|
|
100
|
-
// LAST — remove data + reconcile
|
|
101
|
-
const result = ctx.removeItemById(id);
|
|
102
|
-
if (result < 0)
|
|
103
|
-
return false;
|
|
104
|
-
ctx.forceRender();
|
|
105
|
-
commitStyles();
|
|
106
|
-
emitter.emit("data:change", { type: "remove", id });
|
|
107
|
-
const scrollDelta = oldScroll - dom.viewport[scrollProp];
|
|
108
|
-
exitClone.style.zIndex = "1";
|
|
109
|
-
dom.content.appendChild(exitClone);
|
|
110
|
-
const rt = removeTiming;
|
|
111
|
-
const animOptions = { duration: rt.duration, easing: rt.easing };
|
|
112
|
-
const animations = [];
|
|
113
|
-
const cloneStart = Math.round(originalOffset - scrollDelta);
|
|
114
|
-
animations.push(exitClone.animate([
|
|
115
|
-
{ transform: `${prop}(${cloneStart}px) scaleY(1)`, opacity: 1, transformOrigin: origin },
|
|
116
|
-
{ transform: `${prop}(${Math.round(originalOffset)}px) scaleY(0)`, opacity: 0, transformOrigin: origin },
|
|
117
|
-
], animOptions));
|
|
118
|
-
const allOnClamp = scrollDelta > 0;
|
|
119
|
-
const itemChildren = dom.content.children;
|
|
120
|
-
for (let i = 0; i < itemChildren.length; i++) {
|
|
121
|
-
const el = itemChildren[i];
|
|
122
|
-
if (el === exitClone)
|
|
123
|
-
continue;
|
|
124
|
-
const idx = parseInt(el.dataset?.index ?? "-1", 10);
|
|
125
|
-
if (idx < 0 || (!allOnClamp && idx < layoutIndex))
|
|
126
|
-
continue;
|
|
127
|
-
const newOffset = sc.getOffset(idx);
|
|
128
|
-
const oldVisual = idx >= layoutIndex
|
|
129
|
-
? Math.round(newOffset + itemSize - scrollDelta)
|
|
130
|
-
: Math.round(newOffset - scrollDelta);
|
|
131
|
-
const newVisual = Math.round(newOffset);
|
|
132
|
-
if (oldVisual === newVisual)
|
|
133
|
-
continue;
|
|
134
|
-
animations.push(el.animate([
|
|
135
|
-
{ transform: `${prop}(${oldVisual}px)` },
|
|
136
|
-
{ transform: `${prop}(${newVisual}px)` },
|
|
137
|
-
], animOptions));
|
|
138
|
-
}
|
|
139
|
-
let settled = false;
|
|
140
|
-
const finalize = () => {
|
|
141
|
-
if (settled)
|
|
142
|
-
return;
|
|
143
|
-
settled = true;
|
|
144
|
-
removePending = null;
|
|
145
|
-
exitClone.remove();
|
|
146
|
-
for (const a of animations) {
|
|
147
|
-
if (a.playState !== "finished")
|
|
148
|
-
a.cancel();
|
|
149
|
-
}
|
|
150
|
-
if (focIdx >= 0) {
|
|
151
|
-
const t = state.totalItems;
|
|
152
|
-
if (t > 0)
|
|
153
|
-
ctx.getRenderedElement(Math.min(focIdx, t - 1))?.focus();
|
|
154
|
-
}
|
|
155
|
-
emitter.emit("remove:end", { id });
|
|
156
|
-
};
|
|
157
|
-
removePending = finalize;
|
|
158
|
-
Promise.all(animations.map(a => a.finished)).then(finalize, finalize);
|
|
159
|
-
setTimeout(finalize, rt.duration + 50);
|
|
160
|
-
return true;
|
|
161
|
-
};
|
|
162
|
-
// ── Batch removeItems ──────────────────────────────────
|
|
163
|
-
const removeItems = (ids) => {
|
|
164
|
-
if (ids.length === 0)
|
|
165
|
-
return 0;
|
|
166
|
-
if (ids.length === 1)
|
|
167
|
-
return removeItem(ids[0]) ? 1 : 0;
|
|
168
|
-
if (removePending)
|
|
169
|
-
removePending();
|
|
170
|
-
if (addPending)
|
|
171
|
-
addPending();
|
|
172
|
-
const active = typeof document !== "undefined" ? document.activeElement : null;
|
|
173
|
-
const focIdx = active && dom.content.contains(active)
|
|
174
|
-
? parseInt(active.dataset?.index ?? "-1", 10)
|
|
175
|
-
: -1;
|
|
176
|
-
const targets = [];
|
|
177
|
-
for (const id of ids) {
|
|
178
|
-
const found = findById(id);
|
|
179
|
-
targets.push({
|
|
180
|
-
id,
|
|
181
|
-
layoutIndex: found.layoutIndex,
|
|
182
|
-
el: found.el,
|
|
183
|
-
size: found.layoutIndex >= 0 ? sc.getSize(found.layoutIndex) : 0,
|
|
184
|
-
offset: found.layoutIndex >= 0 ? sc.getOffset(found.layoutIndex) : 0,
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
targets.sort((a, b) => b.layoutIndex - a.layoutIndex);
|
|
188
|
-
const clones = [];
|
|
189
|
-
for (const t of targets) {
|
|
190
|
-
if (!t.el || t.layoutIndex < 0)
|
|
191
|
-
continue;
|
|
192
|
-
const clone = t.el.cloneNode(true);
|
|
193
|
-
clone.style.pointerEvents = "none";
|
|
194
|
-
clone.style.overflow = "hidden";
|
|
195
|
-
clone.removeAttribute("data-index");
|
|
196
|
-
clone.removeAttribute("data-id");
|
|
197
|
-
clone.removeAttribute("id");
|
|
198
|
-
clone.removeAttribute("aria-selected");
|
|
199
|
-
clone.classList.remove(`${cfg.classPrefix}-item--selected`);
|
|
200
|
-
clones.push({ clone, offset: t.offset, size: t.size, layoutIndex: t.layoutIndex });
|
|
201
|
-
}
|
|
202
|
-
const oldOffsetById = new Map();
|
|
203
|
-
const children = dom.content.children;
|
|
204
|
-
for (let i = 0; i < children.length; i++) {
|
|
205
|
-
const el = children[i];
|
|
206
|
-
const elId = el.dataset?.id;
|
|
207
|
-
const idx = parseInt(el.dataset?.index ?? "-1", 10);
|
|
208
|
-
if (elId && idx >= 0)
|
|
209
|
-
oldOffsetById.set(elId, sc.getOffset(idx));
|
|
210
|
-
}
|
|
211
|
-
const scrollProp = cfg.horizontal ? "scrollLeft" : "scrollTop";
|
|
212
|
-
const oldScroll = dom.viewport[scrollProp];
|
|
213
|
-
// Remove all items (descending order preserved)
|
|
214
|
-
const removedIds = [];
|
|
215
|
-
for (const t of targets) {
|
|
216
|
-
const result = ctx.removeItemById(t.id);
|
|
217
|
-
if (result >= 0)
|
|
218
|
-
removedIds.push(t.id);
|
|
219
|
-
}
|
|
220
|
-
if (removedIds.length === 0)
|
|
221
|
-
return 0;
|
|
222
|
-
ctx.forceRender();
|
|
223
|
-
commitStyles();
|
|
224
|
-
for (const rid of removedIds) {
|
|
225
|
-
emitter.emit("data:change", { type: "remove", id: rid });
|
|
226
|
-
}
|
|
227
|
-
if (clones.length === 0) {
|
|
228
|
-
if (focIdx >= 0) {
|
|
229
|
-
const t = state.totalItems;
|
|
230
|
-
if (t > 0)
|
|
231
|
-
ctx.getRenderedElement(Math.min(focIdx, t - 1))?.focus();
|
|
232
|
-
}
|
|
233
|
-
for (const rid of removedIds)
|
|
234
|
-
emitter.emit("remove:end", { id: rid });
|
|
235
|
-
return removedIds.length;
|
|
236
|
-
}
|
|
237
|
-
const scrollDelta = oldScroll - dom.viewport[scrollProp];
|
|
238
|
-
const rt = removeTiming;
|
|
239
|
-
const animOptions = { duration: rt.duration, easing: rt.easing };
|
|
240
|
-
const animations = [];
|
|
241
|
-
clones.sort((a, b) => a.layoutIndex - b.layoutIndex);
|
|
242
|
-
let removedSizeAbove = 0;
|
|
243
|
-
for (const c of clones) {
|
|
244
|
-
c.clone.style.zIndex = "1";
|
|
245
|
-
dom.content.appendChild(c.clone);
|
|
246
|
-
const cloneStart = Math.round(c.offset - scrollDelta);
|
|
247
|
-
const shiftedEnd = Math.round(c.offset - removedSizeAbove);
|
|
248
|
-
animations.push(c.clone.animate([
|
|
249
|
-
{ transform: `${prop}(${cloneStart}px) scaleY(1)`, opacity: 1, transformOrigin: origin },
|
|
250
|
-
{ transform: `${prop}(${shiftedEnd}px) scaleY(0)`, opacity: 0, transformOrigin: origin },
|
|
251
|
-
], animOptions));
|
|
252
|
-
removedSizeAbove += c.size;
|
|
253
|
-
}
|
|
254
|
-
const cloneSet = new Set(clones.map(c => c.clone));
|
|
255
|
-
const itemChildren = dom.content.children;
|
|
256
|
-
for (let i = 0; i < itemChildren.length; i++) {
|
|
257
|
-
const el = itemChildren[i];
|
|
258
|
-
if (cloneSet.has(el))
|
|
259
|
-
continue;
|
|
260
|
-
const elId = el.dataset?.id;
|
|
261
|
-
const idx = parseInt(el.dataset?.index ?? "-1", 10);
|
|
262
|
-
if (!elId || idx < 0)
|
|
263
|
-
continue;
|
|
264
|
-
const oldOffset = oldOffsetById.get(elId);
|
|
265
|
-
if (oldOffset === undefined)
|
|
266
|
-
continue;
|
|
267
|
-
const newOffset = sc.getOffset(idx);
|
|
268
|
-
const oldVisual = Math.round(oldOffset - scrollDelta);
|
|
269
|
-
const newVisual = Math.round(newOffset);
|
|
270
|
-
if (oldVisual === newVisual)
|
|
271
|
-
continue;
|
|
272
|
-
animations.push(el.animate([
|
|
273
|
-
{ transform: `${prop}(${oldVisual}px)` },
|
|
274
|
-
{ transform: `${prop}(${newVisual}px)` },
|
|
275
|
-
], animOptions));
|
|
276
|
-
}
|
|
277
|
-
let settled = false;
|
|
278
|
-
const finalize = () => {
|
|
279
|
-
if (settled)
|
|
280
|
-
return;
|
|
281
|
-
settled = true;
|
|
282
|
-
removePending = null;
|
|
283
|
-
for (const { clone } of clones)
|
|
284
|
-
clone.remove();
|
|
285
|
-
for (const a of animations) {
|
|
286
|
-
if (a.playState !== "finished")
|
|
287
|
-
a.cancel();
|
|
288
|
-
}
|
|
289
|
-
if (focIdx >= 0) {
|
|
290
|
-
const t = state.totalItems;
|
|
291
|
-
if (t > 0)
|
|
292
|
-
ctx.getRenderedElement(Math.min(focIdx, t - 1))?.focus();
|
|
293
|
-
}
|
|
294
|
-
for (const rid of removedIds)
|
|
295
|
-
emitter.emit("remove:end", { id: rid });
|
|
296
|
-
};
|
|
297
|
-
removePending = finalize;
|
|
298
|
-
Promise.all(animations.map(a => a.finished)).then(finalize, finalize);
|
|
299
|
-
setTimeout(finalize, rt.duration + 50);
|
|
300
|
-
return removedIds.length;
|
|
301
|
-
};
|
|
302
|
-
ctx.registerMethod("removeItem", removeItem);
|
|
303
|
-
ctx.registerMethod("removeItems", removeItems);
|
|
304
|
-
}
|
|
305
|
-
// ── Animated insertItem ────────────────────────────────────
|
|
306
|
-
if (insertTiming) {
|
|
307
|
-
const insertItem = (item, index) => {
|
|
308
|
-
if (addPending)
|
|
309
|
-
addPending();
|
|
310
|
-
if (removePending)
|
|
311
|
-
removePending();
|
|
312
|
-
const insertDataIndex = index ?? 0;
|
|
313
|
-
const oldOffsetById = new Map();
|
|
314
|
-
const children = dom.content.children;
|
|
315
|
-
for (let i = 0; i < children.length; i++) {
|
|
316
|
-
const el = children[i];
|
|
317
|
-
const id = el.dataset?.id;
|
|
318
|
-
const idx = parseInt(el.dataset?.index ?? "-1", 10);
|
|
319
|
-
if (id && idx >= 0)
|
|
320
|
-
oldOffsetById.set(id, sc.getOffset(idx));
|
|
321
|
-
}
|
|
322
|
-
const scrollProp = cfg.horizontal ? "scrollLeft" : "scrollTop";
|
|
323
|
-
const sizeProp = cfg.horizontal ? "scrollWidth" : "scrollHeight";
|
|
324
|
-
const clientProp = cfg.horizontal ? "clientWidth" : "clientHeight";
|
|
325
|
-
const oldScroll = dom.viewport[scrollProp];
|
|
326
|
-
const oldMaxScroll = dom.viewport[sizeProp] - dom.viewport[clientProp];
|
|
327
|
-
const wasAtEnd = oldScroll >= oldMaxScroll - 1;
|
|
328
|
-
ctx.insertItemAt(item, insertDataIndex);
|
|
329
|
-
ctx.forceRender();
|
|
330
|
-
commitStyles();
|
|
331
|
-
emitter.emit("data:change", { type: "insert", id: item.id });
|
|
332
|
-
let scrollDelta = dom.viewport[scrollProp] - oldScroll;
|
|
333
|
-
const postInsertLayoutIndex = dataToLayout(insertDataIndex);
|
|
334
|
-
if (cfg.reverse && scrollDelta === 0 && wasAtEnd) {
|
|
335
|
-
dom.viewport[scrollProp] = dom.viewport[sizeProp] - dom.viewport[clientProp];
|
|
336
|
-
const bumped = dom.viewport[scrollProp];
|
|
337
|
-
state.prevScrollPosition = state.scrollPosition;
|
|
338
|
-
state.scrollPosition = bumped;
|
|
339
|
-
scrollDelta = bumped - oldScroll;
|
|
340
|
-
if (scrollDelta > 0) {
|
|
341
|
-
ctx.forceRender();
|
|
342
|
-
commitStyles();
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
const newEl = ctx.getRenderedElement(postInsertLayoutIndex);
|
|
346
|
-
const at = insertTiming;
|
|
347
|
-
const animOptions = { duration: at.duration, easing: at.easing };
|
|
348
|
-
const animations = [];
|
|
349
|
-
if (newEl) {
|
|
350
|
-
const newOffset = sc.getOffset(postInsertLayoutIndex);
|
|
351
|
-
animations.push(newEl.animate([
|
|
352
|
-
{ transform: `${prop}(${Math.round(newOffset)}px) scaleY(0)`, opacity: 0, transformOrigin: origin },
|
|
353
|
-
{ transform: `${prop}(${Math.round(newOffset)}px) scaleY(1)`, opacity: 1, transformOrigin: origin },
|
|
354
|
-
], animOptions));
|
|
355
|
-
}
|
|
356
|
-
const postChildren = dom.content.children;
|
|
357
|
-
for (let i = 0; i < postChildren.length; i++) {
|
|
358
|
-
const el = postChildren[i];
|
|
359
|
-
if (el === newEl)
|
|
360
|
-
continue;
|
|
361
|
-
const id = el.dataset?.id;
|
|
362
|
-
const idx = parseInt(el.dataset?.index ?? "-1", 10);
|
|
363
|
-
if (!id || idx < 0)
|
|
364
|
-
continue;
|
|
365
|
-
const oldOffset = oldOffsetById.get(id);
|
|
366
|
-
if (oldOffset === undefined)
|
|
367
|
-
continue;
|
|
368
|
-
const newOffset = sc.getOffset(idx);
|
|
369
|
-
const visualOld = Math.round(oldOffset + scrollDelta);
|
|
370
|
-
const visualNew = Math.round(newOffset);
|
|
371
|
-
if (visualOld === visualNew)
|
|
372
|
-
continue;
|
|
373
|
-
animations.push(el.animate([
|
|
374
|
-
{ transform: `${prop}(${visualOld}px)` },
|
|
375
|
-
{ transform: `${prop}(${visualNew}px)` },
|
|
376
|
-
], animOptions));
|
|
377
|
-
}
|
|
378
|
-
if (animations.length === 0)
|
|
379
|
-
return;
|
|
380
|
-
let settled = false;
|
|
381
|
-
const finalize = () => {
|
|
382
|
-
if (settled)
|
|
383
|
-
return;
|
|
384
|
-
settled = true;
|
|
385
|
-
addPending = null;
|
|
386
|
-
for (const a of animations) {
|
|
387
|
-
if (a.playState !== "finished")
|
|
388
|
-
a.cancel();
|
|
389
|
-
}
|
|
390
|
-
};
|
|
391
|
-
addPending = finalize;
|
|
392
|
-
Promise.all(animations.map(a => a.finished)).then(finalize, finalize);
|
|
393
|
-
setTimeout(finalize, at.duration + 50);
|
|
394
|
-
};
|
|
395
|
-
ctx.registerMethod("insertItem", insertItem);
|
|
396
|
-
}
|
|
397
|
-
},
|
|
398
|
-
destroy() {
|
|
399
|
-
if (removePending)
|
|
400
|
-
removePending();
|
|
401
|
-
if (addPending)
|
|
402
|
-
addPending();
|
|
403
|
-
},
|
|
404
|
-
};
|
|
405
|
-
}
|
package/dist/rendering/aria.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* vlist/rendering -- ARIA position resolvers
|
|
3
|
-
*
|
|
4
|
-
* Lazy-resolves _getTotal and _layoutToDataIndex from the methods map
|
|
5
|
-
* so that aria-setsize uses data total (not layout total including
|
|
6
|
-
* group headers) and aria-posinset uses data-space position.
|
|
7
|
-
*/
|
|
8
|
-
export const createAriaResolvers = (methods, fallbackTotal) => {
|
|
9
|
-
let gt;
|
|
10
|
-
let l2d;
|
|
11
|
-
return {
|
|
12
|
-
getSetSize: () => {
|
|
13
|
-
if (gt === undefined)
|
|
14
|
-
gt = methods.get("_getTotal") ?? null;
|
|
15
|
-
return gt ? gt() : fallbackTotal();
|
|
16
|
-
},
|
|
17
|
-
getPosInSet: (layoutIndex) => {
|
|
18
|
-
if (l2d === undefined)
|
|
19
|
-
l2d = methods.get("_layoutToDataIndex") ?? null;
|
|
20
|
-
return l2d ? l2d(layoutIndex) + 1 : layoutIndex + 1;
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
};
|
package/dist/rendering/index.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* vlist - Rendering Domain
|
|
3
|
-
* Rendering, virtualization, and scaling for large datasets
|
|
4
|
-
*/
|
|
5
|
-
// Size Cache (dimension-agnostic for vertical/horizontal scrolling)
|
|
6
|
-
export { createSizeCache, countVisibleItems, countItemsFittingFromBottom, getOffsetForVirtualIndex, } from "./sizes";
|
|
7
|
-
// Measured Size Cache (auto-measurement for Mode B)
|
|
8
|
-
export { createMeasuredSizeCache, } from "./measured";
|
|
9
|
-
// DOM Sort (accessibility — reorder children on scroll idle)
|
|
10
|
-
export { sortRenderedDOM } from "./sort";
|
|
11
|
-
// Renderer
|
|
12
|
-
export { createRenderer, createDOMStructure, updateContentHeight, updateContentWidth, resolveContainer, getContainerDimensions, } from "./renderer";
|
|
13
|
-
// Viewport Scrolling
|
|
14
|
-
export { createViewportState, updateViewportState, updateViewportSize, updateViewportItems, calculateRenderRange, calculateTotalSize, calculateActualSize, calculateItemOffset, calculateScrollToIndex, clampScrollPosition, getScrollDirection, rangesEqual, isInRange, getRangeCount, rangeToIndices, diffRanges, getSimpleCompressionState, simpleVisibleRange, simpleScrollToIndex, NO_COMPRESSION, } from "./viewport";
|
|
15
|
-
// Scale (large dataset handling - used by withScale feature)
|
|
16
|
-
export { MAX_VIRTUAL_SIZE, getCompressionState, getCompressionState as getCompression, needsCompression, getMaxItemsWithoutCompression, getCompressionInfo, calculateCompressedVisibleRange, calculateCompressedRenderRange, calculateCompressedItemPosition, calculateCompressedScrollToIndex, calculateIndexFromScrollPosition, } from "./scale";
|
|
17
|
-
// Smart Edge Scroll (shared by core baseline and selection feature)
|
|
18
|
-
export { scrollToFocus, scrollToFocusSimple, } from "./scroll";
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
// src/rendering/measured.ts
|
|
2
|
-
/**
|
|
3
|
-
* vlist - Measured Size Cache
|
|
4
|
-
* Auto-measurement support for items with unknown sizes (Mode B)
|
|
5
|
-
*
|
|
6
|
-
* Wraps the existing variable SizeCache with measurement tracking.
|
|
7
|
-
* Once an item is measured, it behaves identically to Mode A (known size).
|
|
8
|
-
* Unmeasured items use the estimated size as a fallback.
|
|
9
|
-
*
|
|
10
|
-
* Fully axis-neutral: works identically for vertical (estimatedHeight)
|
|
11
|
-
* and horizontal (estimatedWidth) orientations. This cache stores plain
|
|
12
|
-
* numbers representing the main-axis dimension — it never knows whether
|
|
13
|
-
* those numbers are heights or widths. The axis-specific translation
|
|
14
|
-
* happens in builder/core.ts at the DOM boundary.
|
|
15
|
-
*
|
|
16
|
-
* Implements the SizeCache interface so all downstream code
|
|
17
|
-
* (viewport, scale, features) works unchanged.
|
|
18
|
-
*/
|
|
19
|
-
import { createSizeCache } from "./sizes";
|
|
20
|
-
// =============================================================================
|
|
21
|
-
// Factory
|
|
22
|
-
// =============================================================================
|
|
23
|
-
/**
|
|
24
|
-
* Create a measured size cache for auto-measurement (Mode B)
|
|
25
|
-
*
|
|
26
|
-
* Works for both orientations:
|
|
27
|
-
* - Vertical: estimatedSize = estimatedHeight, measures block size
|
|
28
|
-
* - Horizontal: estimatedSize = estimatedWidth, measures inline size
|
|
29
|
-
*
|
|
30
|
-
* The cache itself is axis-neutral — it only stores numbers. The caller
|
|
31
|
-
* (builder/core.ts) is responsible for reading the correct axis from
|
|
32
|
-
* the config and from ResizeObserver entries (blockSize vs inlineSize).
|
|
33
|
-
*
|
|
34
|
-
* Internally maintains a Map of measured sizes keyed by item index.
|
|
35
|
-
* Unmeasured items fall back to the estimated size. The underlying
|
|
36
|
-
* prefix-sum array is rebuilt when measurements change.
|
|
37
|
-
*
|
|
38
|
-
* The size function fed into the variable SizeCache becomes:
|
|
39
|
-
* (index) => measuredSizes.has(index) ? measuredSizes.get(index) : estimatedSize
|
|
40
|
-
*
|
|
41
|
-
* This means all existing viewport, compression, and range calculations
|
|
42
|
-
* work unchanged — they only see a SizeCache with variable sizes.
|
|
43
|
-
*/
|
|
44
|
-
export const createMeasuredSizeCache = (estimatedSize, initialTotal) => {
|
|
45
|
-
const measuredSizes = new Map();
|
|
46
|
-
// Size function: return measured size if available, else estimated
|
|
47
|
-
const sizeFn = (index) => measuredSizes.get(index) ?? estimatedSize;
|
|
48
|
-
// Create the underlying variable SizeCache with our size function
|
|
49
|
-
let inner = createSizeCache(sizeFn, initialTotal);
|
|
50
|
-
return {
|
|
51
|
-
// ── SizeCache interface ──────────────────────────────────────
|
|
52
|
-
getOffset(index) {
|
|
53
|
-
return inner.getOffset(index);
|
|
54
|
-
},
|
|
55
|
-
getSize(index) {
|
|
56
|
-
return sizeFn(index);
|
|
57
|
-
},
|
|
58
|
-
indexAtOffset(offset) {
|
|
59
|
-
return inner.indexAtOffset(offset);
|
|
60
|
-
},
|
|
61
|
-
getTotalSize() {
|
|
62
|
-
return inner.getTotalSize();
|
|
63
|
-
},
|
|
64
|
-
getTotal() {
|
|
65
|
-
return inner.getTotal();
|
|
66
|
-
},
|
|
67
|
-
rebuild(totalItems) {
|
|
68
|
-
// Discard measured sizes for indices that no longer exist
|
|
69
|
-
if (totalItems < inner.getTotal()) {
|
|
70
|
-
for (const index of measuredSizes.keys()) {
|
|
71
|
-
if (index >= totalItems)
|
|
72
|
-
measuredSizes.delete(index);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
// Rebuild the underlying variable cache with current size function
|
|
76
|
-
// We must recreate because createSizeCache captures sizeFn at creation,
|
|
77
|
-
// but our sizeFn closes over measuredSizes which is mutable — so a
|
|
78
|
-
// rebuild of the inner cache re-evaluates all prefix sums.
|
|
79
|
-
inner = createSizeCache(sizeFn, totalItems);
|
|
80
|
-
},
|
|
81
|
-
isVariable() {
|
|
82
|
-
return true;
|
|
83
|
-
},
|
|
84
|
-
// ── MeasuredSizeCache extensions ─────────────────────────────
|
|
85
|
-
setMeasuredSize(index, size) {
|
|
86
|
-
measuredSizes.set(index, size);
|
|
87
|
-
},
|
|
88
|
-
isMeasured(index) {
|
|
89
|
-
return measuredSizes.has(index);
|
|
90
|
-
},
|
|
91
|
-
getEstimatedSize() {
|
|
92
|
-
return estimatedSize;
|
|
93
|
-
},
|
|
94
|
-
measuredCount() {
|
|
95
|
-
return measuredSizes.size;
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
};
|