kerfjs 4.2.0-beta.4 → 4.2.0-beta.6
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/CHANGELOG.md +4 -5
- package/dist/attach.d.ts +45 -0
- package/dist/{imperative.js → attach.js} +5 -5
- package/dist/attach.js.map +1 -0
- package/dist/{chunk-4MY2656S.js → chunk-LKWAKC2X.js} +3 -3
- package/dist/{chunk-4MY2656S.js.map → chunk-LKWAKC2X.js.map} +1 -1
- package/dist/{chunk-FSAQR6IU.js → chunk-SUPUPSBE.js} +3 -6
- package/dist/chunk-SUPUPSBE.js.map +1 -0
- package/dist/html.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/jsx-runtime.d.ts +24 -12
- package/dist/jsx-runtime.js +1 -1
- package/dist/list.d.ts +97 -7
- package/dist/list.js +180 -23
- package/dist/list.js.map +1 -1
- package/dist/overlay.d.ts +9 -2
- package/dist/overlay.js +18 -13
- package/dist/overlay.js.map +1 -1
- package/dist/remount.d.ts +3 -3
- package/dist/remount.js +2 -2
- package/dist/remount.js.map +1 -1
- package/dist/scope.js +2 -2
- package/package.json +4 -4
- package/dist/chunk-FSAQR6IU.js.map +0 -1
- package/dist/imperative.d.ts +0 -34
- package/dist/imperative.js.map +0 -1
package/dist/list.js
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import { ARRAY_SIGNAL_BRAND } from './chunk-MRYM3O3V.js';
|
|
2
|
-
import { mount } from './chunk-
|
|
2
|
+
import { mount } from './chunk-LKWAKC2X.js';
|
|
3
3
|
import './chunk-QIP723L4.js';
|
|
4
4
|
import './chunk-YHH7OUFA.js';
|
|
5
|
-
import './chunk-
|
|
5
|
+
import './chunk-SUPUPSBE.js';
|
|
6
6
|
import { effect } from './chunk-3APBEVHF.js';
|
|
7
7
|
import './chunk-GY4XV2UV.js';
|
|
8
8
|
import './chunk-VVDJLWMP.js';
|
|
9
9
|
|
|
10
10
|
// src/list.ts
|
|
11
11
|
function bindList(parent, source, options) {
|
|
12
|
-
const { key, render, tag = "div", virtualize } = options;
|
|
12
|
+
const { key, render, tag = "div", virtualize, before } = options;
|
|
13
13
|
const overscan = virtualize?.overscan ?? 3;
|
|
14
|
+
const minRows = virtualize?.minRows;
|
|
15
|
+
const endAnchor = () => {
|
|
16
|
+
if (virtualize !== void 0 || before === void 0) return null;
|
|
17
|
+
return (typeof before === "function" ? before() : before) ?? null;
|
|
18
|
+
};
|
|
14
19
|
const rows = /* @__PURE__ */ new Map();
|
|
15
20
|
const order = [];
|
|
16
21
|
let items = [];
|
|
@@ -20,7 +25,11 @@ function bindList(parent, source, options) {
|
|
|
20
25
|
const patchSource = source;
|
|
21
26
|
const granularEligible = virtualize === void 0 && patchSource[ARRAY_SIGNAL_BRAND] === true;
|
|
22
27
|
const container = virtualize === void 0 ? parent : document.createElement("div");
|
|
23
|
-
if (virtualize !== void 0)
|
|
28
|
+
if (virtualize !== void 0) {
|
|
29
|
+
if (virtualize.containerClass !== void 0) container.className = virtualize.containerClass;
|
|
30
|
+
if (virtualize.containerId !== void 0) container.id = virtualize.containerId;
|
|
31
|
+
parent.appendChild(container);
|
|
32
|
+
}
|
|
24
33
|
const NOOP = () => {
|
|
25
34
|
};
|
|
26
35
|
const asElementRow = (rendered) => {
|
|
@@ -34,13 +43,11 @@ function bindList(parent, source, options) {
|
|
|
34
43
|
const makeRow = (item) => {
|
|
35
44
|
const elementRow = asElementRow(render(item));
|
|
36
45
|
if (elementRow !== null) {
|
|
37
|
-
if (virtualize !== void 0) elementRow.el.style.height = `${virtualize.rowHeight}px`;
|
|
38
46
|
return { el: elementRow.el, item, dispose: elementRow.dispose, elementMode: true, update: elementRow.update };
|
|
39
47
|
}
|
|
40
48
|
const el = document.createElement(tag);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return { el, item, dispose, elementMode: false };
|
|
49
|
+
const dispose2 = mount(el, () => render(item));
|
|
50
|
+
return { el, item, dispose: dispose2, elementMode: false };
|
|
44
51
|
};
|
|
45
52
|
const reconcileItem = (row, k, item) => {
|
|
46
53
|
if (row.item === item) return row;
|
|
@@ -79,7 +86,7 @@ function bindList(parent, source, options) {
|
|
|
79
86
|
}
|
|
80
87
|
order.push(row);
|
|
81
88
|
}
|
|
82
|
-
let ref =
|
|
89
|
+
let ref = endAnchor();
|
|
83
90
|
for (let i = order.length - 1; i >= 0; i--) {
|
|
84
91
|
const el = order[i].el;
|
|
85
92
|
if (el.parentNode !== container || el.nextSibling !== ref) {
|
|
@@ -94,7 +101,7 @@ function bindList(parent, source, options) {
|
|
|
94
101
|
const row = makeRow(patch.item);
|
|
95
102
|
rows.set(key(patch.item), row);
|
|
96
103
|
order.splice(patch.index, 0, row);
|
|
97
|
-
container.insertBefore(row.el, order[patch.index + 1]?.el ??
|
|
104
|
+
container.insertBefore(row.el, order[patch.index + 1]?.el ?? endAnchor());
|
|
98
105
|
} else if (patch.type === "remove") {
|
|
99
106
|
const [row] = order.splice(patch.index, 1);
|
|
100
107
|
row.dispose();
|
|
@@ -103,7 +110,7 @@ function bindList(parent, source, options) {
|
|
|
103
110
|
} else if (patch.type === "move") {
|
|
104
111
|
const [row] = order.splice(patch.from, 1);
|
|
105
112
|
order.splice(patch.to, 0, row);
|
|
106
|
-
container.insertBefore(row.el, order[patch.to + 1]?.el ??
|
|
113
|
+
container.insertBefore(row.el, order[patch.to + 1]?.el ?? endAnchor());
|
|
107
114
|
} else if (patch.type === "update") {
|
|
108
115
|
const current = order[patch.index];
|
|
109
116
|
if (current.item !== patch.item) {
|
|
@@ -123,12 +130,68 @@ function bindList(parent, source, options) {
|
|
|
123
130
|
const row = makeRow(patch.item);
|
|
124
131
|
rows.set(key(patch.item), row);
|
|
125
132
|
order[patch.index] = row;
|
|
126
|
-
container.insertBefore(row.el, order[patch.index + 1]?.el ??
|
|
133
|
+
container.insertBefore(row.el, order[patch.index + 1]?.el ?? endAnchor());
|
|
127
134
|
}
|
|
128
135
|
}
|
|
129
136
|
}
|
|
130
137
|
}
|
|
131
138
|
};
|
|
139
|
+
const rowHeight = virtualize?.rowHeight;
|
|
140
|
+
const fixedHeight = typeof rowHeight === "number" ? rowHeight : null;
|
|
141
|
+
const measuring = typeof rowHeight === "object" && rowHeight !== null;
|
|
142
|
+
const measured = /* @__PURE__ */ new Map();
|
|
143
|
+
const estimateAt = (index) => {
|
|
144
|
+
const est = rowHeight.estimate;
|
|
145
|
+
return typeof est === "function" ? est(items[index], index) : est;
|
|
146
|
+
};
|
|
147
|
+
const variableHeightAt = fixedHeight !== null ? null : measuring ? (index) => {
|
|
148
|
+
const k = key(items[index]);
|
|
149
|
+
return measured.has(k) ? measured.get(k) : estimateAt(index);
|
|
150
|
+
} : (index) => rowHeight(items[index], index);
|
|
151
|
+
let offsets = [0];
|
|
152
|
+
let heightsDirty = true;
|
|
153
|
+
const indexByKey = /* @__PURE__ */ new Map();
|
|
154
|
+
let pendingAnchorDelta = 0;
|
|
155
|
+
const rebuildOffsets = () => {
|
|
156
|
+
const fn = variableHeightAt;
|
|
157
|
+
const total = items.length;
|
|
158
|
+
offsets = new Array(total + 1);
|
|
159
|
+
offsets[0] = 0;
|
|
160
|
+
if (measuring) indexByKey.clear();
|
|
161
|
+
for (let i = 0; i < total; i++) {
|
|
162
|
+
offsets[i + 1] = offsets[i] + fn(i);
|
|
163
|
+
if (measuring) indexByKey.set(key(items[i]), i);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
const findStart = (target, total) => {
|
|
167
|
+
let lo = 0;
|
|
168
|
+
let hi = total;
|
|
169
|
+
while (lo < hi) {
|
|
170
|
+
const mid = lo + hi + 1 >> 1;
|
|
171
|
+
if (offsets[mid] <= target) lo = mid;
|
|
172
|
+
else hi = mid - 1;
|
|
173
|
+
}
|
|
174
|
+
return lo;
|
|
175
|
+
};
|
|
176
|
+
const findEnd = (target, total) => {
|
|
177
|
+
let lo = 0;
|
|
178
|
+
let hi = total;
|
|
179
|
+
while (lo < hi) {
|
|
180
|
+
const mid = lo + hi >> 1;
|
|
181
|
+
if (offsets[mid] >= target) hi = mid;
|
|
182
|
+
else lo = mid + 1;
|
|
183
|
+
}
|
|
184
|
+
return lo;
|
|
185
|
+
};
|
|
186
|
+
const sizeVisibleRows = (start) => {
|
|
187
|
+
if (measuring) return;
|
|
188
|
+
for (let j = 0; j < order.length; j++) {
|
|
189
|
+
const abs = start + j;
|
|
190
|
+
const h = fixedHeight !== null ? fixedHeight : offsets[abs + 1] - offsets[abs];
|
|
191
|
+
order[j].el.style.height = `${h}px`;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
const renderSubscribers = /* @__PURE__ */ new Set();
|
|
132
195
|
const renderWindow = () => {
|
|
133
196
|
if (virtualize === void 0) {
|
|
134
197
|
if (granularEligible) {
|
|
@@ -142,28 +205,79 @@ function bindList(parent, source, options) {
|
|
|
142
205
|
firstRender = false;
|
|
143
206
|
return;
|
|
144
207
|
}
|
|
145
|
-
const { rowHeight } = virtualize;
|
|
146
208
|
const total = items.length;
|
|
147
|
-
|
|
148
|
-
|
|
209
|
+
let start;
|
|
210
|
+
let end;
|
|
211
|
+
let padTop;
|
|
212
|
+
let padBottom;
|
|
213
|
+
if (minRows !== void 0 && total < minRows) {
|
|
214
|
+
if (fixedHeight === null && heightsDirty) {
|
|
215
|
+
rebuildOffsets();
|
|
216
|
+
heightsDirty = false;
|
|
217
|
+
}
|
|
218
|
+
start = 0;
|
|
219
|
+
end = total;
|
|
220
|
+
padTop = 0;
|
|
221
|
+
padBottom = 0;
|
|
222
|
+
} else {
|
|
223
|
+
const scrollTop = parent.scrollTop;
|
|
224
|
+
const viewportBottom = scrollTop + parent.clientHeight;
|
|
225
|
+
if (fixedHeight !== null) {
|
|
226
|
+
start = Math.max(0, Math.floor(scrollTop / fixedHeight) - overscan);
|
|
227
|
+
end = Math.min(total, Math.ceil(viewportBottom / fixedHeight) + overscan);
|
|
228
|
+
padTop = start * fixedHeight;
|
|
229
|
+
padBottom = Math.max(0, total - end) * fixedHeight;
|
|
230
|
+
} else {
|
|
231
|
+
if (heightsDirty) {
|
|
232
|
+
rebuildOffsets();
|
|
233
|
+
heightsDirty = false;
|
|
234
|
+
}
|
|
235
|
+
start = Math.max(0, findStart(scrollTop, total) - overscan);
|
|
236
|
+
end = Math.min(total, findEnd(viewportBottom, total) + overscan);
|
|
237
|
+
padTop = offsets[start];
|
|
238
|
+
padBottom = offsets[total] - offsets[end];
|
|
239
|
+
}
|
|
240
|
+
}
|
|
149
241
|
syncRows(items.slice(start, end));
|
|
150
|
-
|
|
151
|
-
container.style.
|
|
242
|
+
sizeVisibleRows(start);
|
|
243
|
+
container.style.paddingTop = `${padTop}px`;
|
|
244
|
+
container.style.paddingBottom = `${padBottom}px`;
|
|
245
|
+
for (const cb of renderSubscribers) cb();
|
|
152
246
|
};
|
|
153
247
|
const stopEffect = effect(() => {
|
|
154
248
|
items = source.value;
|
|
249
|
+
heightsDirty = true;
|
|
155
250
|
renderWindow();
|
|
156
251
|
});
|
|
157
|
-
const
|
|
252
|
+
const scheduleRender = () => {
|
|
158
253
|
if (rafPending) return;
|
|
159
254
|
rafPending = true;
|
|
160
255
|
globalThis.requestAnimationFrame(() => {
|
|
161
256
|
rafPending = false;
|
|
162
|
-
if (
|
|
257
|
+
if (disposed) return;
|
|
258
|
+
if (pendingAnchorDelta !== 0) {
|
|
259
|
+
parent.scrollTop += pendingAnchorDelta;
|
|
260
|
+
pendingAnchorDelta = 0;
|
|
261
|
+
}
|
|
262
|
+
renderWindow();
|
|
163
263
|
});
|
|
164
264
|
};
|
|
165
|
-
if (virtualize !== void 0) parent.addEventListener("scroll",
|
|
166
|
-
|
|
265
|
+
if (virtualize !== void 0) parent.addEventListener("scroll", scheduleRender);
|
|
266
|
+
const RO = globalThis.ResizeObserver;
|
|
267
|
+
const parentResize = virtualize !== void 0 && RO !== void 0 ? new RO(scheduleRender) : void 0;
|
|
268
|
+
parentResize?.observe(parent);
|
|
269
|
+
const setHeight = (k, height) => {
|
|
270
|
+
if (!measuring) return;
|
|
271
|
+
const idx = indexByKey.get(k);
|
|
272
|
+
if (idx === void 0) return;
|
|
273
|
+
const oldHeight = measured.has(k) ? measured.get(k) : estimateAt(idx);
|
|
274
|
+
if (height === oldHeight) return;
|
|
275
|
+
measured.set(k, height);
|
|
276
|
+
if (offsets[idx + 1] <= parent.scrollTop) pendingAnchorDelta += height - oldHeight;
|
|
277
|
+
heightsDirty = true;
|
|
278
|
+
scheduleRender();
|
|
279
|
+
};
|
|
280
|
+
const dispose = (() => {
|
|
167
281
|
disposed = true;
|
|
168
282
|
stopEffect();
|
|
169
283
|
for (const row of rows.values()) {
|
|
@@ -171,13 +285,56 @@ function bindList(parent, source, options) {
|
|
|
171
285
|
if (virtualize === void 0) row.el.remove();
|
|
172
286
|
}
|
|
173
287
|
rows.clear();
|
|
288
|
+
renderSubscribers.clear();
|
|
174
289
|
if (virtualize !== void 0) {
|
|
175
|
-
parent.removeEventListener("scroll",
|
|
290
|
+
parent.removeEventListener("scroll", scheduleRender);
|
|
291
|
+
parentResize?.disconnect();
|
|
176
292
|
container.remove();
|
|
293
|
+
VIRTUAL_INTERNALS.delete(handle);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
const handle = dispose;
|
|
297
|
+
handle.setHeight = setHeight;
|
|
298
|
+
if (virtualize !== void 0) {
|
|
299
|
+
handle.container = container;
|
|
300
|
+
VIRTUAL_INTERNALS.set(handle, {
|
|
301
|
+
visibleRows: () => order.map((row) => ({ key: key(row.item), el: row.el })),
|
|
302
|
+
onRender: (cb) => {
|
|
303
|
+
renderSubscribers.add(cb);
|
|
304
|
+
return () => renderSubscribers.delete(cb);
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
return handle;
|
|
309
|
+
}
|
|
310
|
+
var VIRTUAL_INTERNALS = /* @__PURE__ */ new WeakMap();
|
|
311
|
+
function observeRowHeights(handle) {
|
|
312
|
+
const internals = VIRTUAL_INTERNALS.get(handle);
|
|
313
|
+
const RO = globalThis.ResizeObserver;
|
|
314
|
+
if (internals === void 0 || RO === void 0) return () => {
|
|
315
|
+
};
|
|
316
|
+
const keyByEl = /* @__PURE__ */ new WeakMap();
|
|
317
|
+
const observer = new RO((entries) => {
|
|
318
|
+
for (const entry of entries) {
|
|
319
|
+
const k = keyByEl.get(entry.target);
|
|
320
|
+
if (k !== void 0) handle.setHeight(k, entry.target.offsetHeight);
|
|
177
321
|
}
|
|
322
|
+
});
|
|
323
|
+
const resync = () => {
|
|
324
|
+
observer.disconnect();
|
|
325
|
+
for (const { key: k, el } of internals.visibleRows()) {
|
|
326
|
+
keyByEl.set(el, k);
|
|
327
|
+
observer.observe(el);
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
const unsubscribe = internals.onRender(resync);
|
|
331
|
+
resync();
|
|
332
|
+
return () => {
|
|
333
|
+
observer.disconnect();
|
|
334
|
+
unsubscribe();
|
|
178
335
|
};
|
|
179
336
|
}
|
|
180
337
|
|
|
181
|
-
export { bindList };
|
|
338
|
+
export { bindList, observeRowHeights };
|
|
182
339
|
//# sourceMappingURL=list.js.map
|
|
183
340
|
//# sourceMappingURL=list.js.map
|
package/dist/list.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/list.ts"],"names":[],"mappings":"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","file":"list.js","sourcesContent":["/**\n * `kerfjs/list` — `bindList`, a keyed list with a live per-row mount and\n * optional viewport virtualization.\n *\n * This is a DELIBERATE second list API, distinct from `each()`. It does two\n * things `each()` structurally cannot:\n * 1. **Per-row reactivity.** Every row is individually `mount()`ed, so a signal\n * the row's `render` reads updates just that row (fine-grained binding or a\n * one-row morph) without touching its siblings — no full-list pass.\n * 2. **Virtualization.** With `{ virtualize: { rowHeight } }` only the rows in\n * the scroll viewport are rendered; padding on the scroll container keeps\n * `scrollHeight` honest.\n *\n * `each()` stays the choice for item-owned-state lists rendered to HTML strings;\n * reach for `bindList` when you need surgical per-row updates or windowing.\n *\n * import { bindList } from 'kerfjs/list';\n *\n * const dispose = bindList(listEl, itemsSignal, {\n * key: (row) => row.id,\n * render: (row) => <span class={selected} data-id={row.id}>{row.label}</span>,\n * tag: 'li',\n * virtualize: { rowHeight: 32 },\n * });\n *\n * `render` reads signals for reactivity (external state like a `selectedId`, or\n * signals the item carries) — keep the item OBJECTS stable across renders and\n * drive structure (add/remove/move) through `itemsSignal`. A row whose item\n * object identity changes is rebuilt (same rule as `each()`'s memo). `bindList`\n * OWNS `parent`'s children. It reads `itemsSignal.value`, so a plain\n * `signal<T[]>` or an `arraySignal<T>` both work.\n */\nimport { ARRAY_SIGNAL_BRAND, type ArrayPatch } from './array-signal.js';\nimport { mount, type MountResult } from './mount.js';\nimport { effect } from './reactive.js';\n\n/** A row's stable key. */\nexport type ListKey = string | number;\n\n/** Anything with a tracking `.value` array read — a `signal<readonly T[]>` or an `arraySignal<T>`. */\nexport interface ListSource<T> {\n readonly value: readonly T[];\n}\n\n/**\n * A row built imperatively by `render`: return the row **element** itself (kerf\n * keys/moves/reuses it and owns nothing inside it), or `{ el, update?, dispose? }`\n * to also hand back an `update(item)` — called on the SAME element when the row's\n * key persists but its item changes — and a `dispose` that runs only when the row\n * is removed.\n */\nexport type RowElement<T> =\n | HTMLElement\n | { el: HTMLElement; update?: (item: T) => void; dispose?: () => void };\n\n/** Options for {@link bindList}. */\nexport interface BindListOptions<T> {\n /** Stable per-row key. Rows are matched, moved, and reused by this. */\n key: (item: T) => ListKey;\n /**\n * Build a row. Two modes, chosen per call by what you return:\n * - **Content mode** (a `MountResult` — JSX / `SafeHtml`): kerf creates the\n * row element (`tag`) and `mount()`s your content inside it, so signals your\n * content reads drive per-row reactivity.\n * - **Element mode** (an `HTMLElement`, or `{ el, update?, dispose? }`): the\n * element you return IS the row, so you own its tag, class, `data-*`, and\n * listeners. kerf **keys/moves/reuses** it — the SAME element survives an\n * append/remove/reorder or a fresh item object at the same key. Refresh its\n * content by reading signals inside it, or by returning an `update(item)`\n * that kerf calls on the existing element when the item changes. `dispose`\n * runs only when the row is genuinely removed.\n */\n render: (item: T) => MountResult | RowElement<T>;\n /** Row element tag for **content mode**. Default `'div'` (use `'li'` inside a `<ul>`, `'tr'` inside a `<tbody>`, …). Ignored in element mode. */\n tag?: string;\n /**\n * Turn on viewport virtualization. `rowHeight` is the fixed pixel height of\n * every row; `overscan` (default 3) is how many extra rows to render above and\n * below the viewport. `parent` must be a scroll container (your CSS: a fixed\n * height + `overflow: auto`).\n */\n virtualize?: { rowHeight: number; overscan?: number };\n}\n\ninterface Row<T> {\n el: HTMLElement;\n item: T;\n dispose: () => void;\n /** True for element-mode rows (the caller owns the element — reuse it, don't rebuild on item change). */\n elementMode: boolean;\n /** Element mode only: refresh the existing element when the item changes at the same key. */\n update?: (item: T) => void;\n}\n\n/**\n * Bind a keyed, per-row-reactive list to `parent`, driven by `source` (a\n * `signal<readonly T[]>` or an `arraySignal<T>`). Returns a disposer that tears\n * down every row mount, the scroll listener (if virtualized), and the source\n * subscription.\n */\nexport function bindList<T>(\n parent: HTMLElement,\n source: ListSource<T>,\n options: BindListOptions<T>,\n): () => void {\n const { key, render, tag = 'div', virtualize } = options;\n const overscan = virtualize?.overscan ?? 3;\n\n const rows = new Map<ListKey, Row<T>>();\n // The current DOM order of rows, kept in step by both the keyed-diff and the\n // granular patch paths so index-based patches can address rows directly.\n const order: Array<Row<T>> = [];\n let items: readonly T[] = [];\n let disposed = false;\n let rafPending = false;\n let firstRender = true;\n\n // Granular fast path (KF-478): when the source is an `arraySignal` and the\n // list is NOT virtualized, apply its insert/remove/move/update patches\n // directly in O(patches) instead of diffing the whole snapshot. Virtualized\n // lists keep the keyed diff — their visible set is just the window (cheap),\n // and absolute-index patches don't compose with a shifting window. A plain\n // `signal<T[]>` has no patches, so it always uses the keyed diff.\n const patchSource = source as {\n [ARRAY_SIGNAL_BRAND]?: boolean;\n _consumePatches?: () => ArrayPatch<T>[];\n };\n const granularEligible = virtualize === undefined && patchSource[ARRAY_SIGNAL_BRAND] === true;\n\n // Virtualized lists put the windowing padding + rows on an INNER sizer, so the\n // padding never inflates the scroll container's clientHeight (padding counts\n // toward clientHeight). `parent` stays the clean scroll viewport; `container`\n // holds the rows. Non-virtualized lists render straight into `parent`.\n const container: HTMLElement = virtualize === undefined ? parent : document.createElement('div');\n if (virtualize !== undefined) parent.appendChild(container);\n\n const NOOP = (): void => { /* element-mode rows with no caller teardown */ };\n\n // Detect element mode from a render result: a raw `HTMLElement`, or a\n // `{ el, dispose? }` object. Everything else (SafeHtml / string / nullish) is\n // content mode. SafeHtml is an object but has no `el`, so it never matches.\n const asElementRow = (\n rendered: MountResult | RowElement<T>,\n ): { el: HTMLElement; dispose: () => void; update?: (item: T) => void } | null => {\n if (rendered instanceof HTMLElement) return { el: rendered, dispose: NOOP };\n if (\n rendered !== null\n && typeof rendered === 'object'\n && 'el' in rendered\n && (rendered as { el: unknown }).el instanceof HTMLElement\n ) {\n const r = rendered as { el: HTMLElement; update?: (item: T) => void; dispose?: () => void };\n return { el: r.el, dispose: r.dispose ?? NOOP, update: r.update };\n }\n return null;\n };\n\n const makeRow = (item: T): Row<T> => {\n // One call decides the mode per row (so a list may mix element + content rows).\n const elementRow = asElementRow(render(item));\n if (elementRow !== null) {\n // Element mode: the returned element IS the row; the caller owns its\n // content + cleanup. bindList still sizes it for the windowing math.\n if (virtualize !== undefined) elementRow.el.style.height = `${virtualize.rowHeight}px`;\n return { el: elementRow.el, item, dispose: elementRow.dispose, elementMode: true, update: elementRow.update };\n }\n // Content mode: kerf creates the row element and mounts `render` inside it,\n // so the content is per-row reactive. (In content mode `render` runs once\n // more here for the mode probe than the mount itself needs — keep it a pure\n // projection, which bindList already requires.)\n const el = document.createElement(tag);\n if (virtualize !== undefined) el.style.height = `${virtualize.rowHeight}px`;\n // Content mode: `render` returns a MountResult here (element results were\n // handled above), so narrowing it for `mount` is sound.\n const dispose = mount(el, () => render(item) as MountResult);\n return { el, item, dispose, elementMode: false };\n };\n\n // A row whose KEY persists but whose item object changed. Content-mode rows are\n // rebuilt (their mount re-renders the fresh item); element-mode rows are REUSED\n // — the caller owns the element, so we keep it (preserving focus / scroll /\n // listeners) and refresh via the optional `update(item)`. Returns the row to\n // use at that key (a fresh one for content, the same one for element).\n const reconcileItem = (row: Row<T>, k: ListKey, item: T): Row<T> => {\n if (row.item === item) return row;\n if (row.elementMode) {\n row.item = item;\n row.update?.(item);\n return row;\n }\n row.dispose();\n row.el.remove();\n rows.delete(k);\n const fresh = makeRow(item);\n rows.set(k, fresh);\n return fresh;\n };\n\n // Reconcile the live rows to exactly `visible`, in order, keyed.\n const syncRows = (visible: readonly T[]): void => {\n const wanted = new Set<ListKey>();\n for (const item of visible) wanted.add(key(item));\n\n // Remove rows that are gone from the window.\n for (const [k, row] of rows) {\n if (!wanted.has(k)) {\n row.dispose();\n row.el.remove();\n rows.delete(k);\n }\n }\n\n // Create missing rows; reuse existing ones by key (element rows keep their\n // element across item changes; content rows rebuild on identity change).\n order.length = 0;\n for (const item of visible) {\n const k = key(item);\n const existing = rows.get(k);\n let row: Row<T>;\n if (existing !== undefined) {\n row = reconcileItem(existing, k, item);\n } else {\n row = makeRow(item);\n rows.set(k, row);\n }\n order.push(row);\n }\n\n // Reverse pass: move only rows that are out of position.\n let ref: Node | null = null;\n for (let i = order.length - 1; i >= 0; i--) {\n const el = order[i].el;\n if (el.parentNode !== container || el.nextSibling !== ref) {\n container.insertBefore(el, ref);\n }\n ref = el;\n }\n };\n\n // Apply arraySignal structural patches directly to `order` + the DOM, in\n // O(patches). Indices are always valid by construction: `order` reflects the\n // last-rendered state and the patches are exactly the delta from it (bindList\n // drains the queue every render, and `replace` is filtered out by the caller,\n // which snapshots instead). The `splice()`s mirror `arraySignal`'s own\n // `_items` mutations exactly.\n const applyPatches = (patches: readonly ArrayPatch<T>[]): void => {\n for (const patch of patches) {\n if (patch.type === 'insert') {\n const row = makeRow(patch.item);\n rows.set(key(patch.item), row);\n order.splice(patch.index, 0, row);\n container.insertBefore(row.el, order[patch.index + 1]?.el ?? null);\n } else if (patch.type === 'remove') {\n const [row] = order.splice(patch.index, 1);\n row.dispose();\n row.el.remove();\n rows.delete(key(row.item));\n } else if (patch.type === 'move') {\n const [row] = order.splice(patch.from, 1);\n order.splice(patch.to, 0, row);\n container.insertBefore(row.el, order[patch.to + 1]?.el ?? null);\n } else if (patch.type === 'update') {\n // An item whose OBJECT identity changed: content rows rebuild (their mount\n // re-renders the fresh item); element rows are REUSED — keep the caller's\n // element and refresh via update(), re-keying if the key changed. A\n // same-ref update needs nothing (the row's mount reacts to its signals).\n const current = order[patch.index];\n if (current.item !== patch.item) {\n if (current.elementMode) {\n const oldKey = key(current.item);\n const newKey = key(patch.item);\n current.item = patch.item;\n if (newKey !== oldKey) {\n rows.delete(oldKey);\n rows.set(newKey, current);\n }\n current.update?.(patch.item);\n } else {\n current.dispose();\n current.el.remove();\n rows.delete(key(current.item));\n const row = makeRow(patch.item);\n rows.set(key(patch.item), row);\n order[patch.index] = row;\n container.insertBefore(row.el, order[patch.index + 1]?.el ?? null);\n }\n }\n }\n // 'replace' never reaches here — the caller snapshots on it.\n }\n };\n\n const renderWindow = (): void => {\n if (virtualize === undefined) {\n if (granularEligible) {\n // Always drain to keep the single patch queue clean (so patches never\n // double-apply). Take the granular path past the first render, when\n // there are patches, and none is a `replace` (which reshapes the whole\n // array — snapshot instead). Otherwise fall through to a keyed diff.\n const patches = patchSource._consumePatches!();\n if (\n !firstRender\n && patches.length > 0\n && !patches.some((p) => p.type === 'replace')\n ) {\n applyPatches(patches);\n return;\n }\n }\n syncRows(items);\n firstRender = false;\n return;\n }\n const { rowHeight } = virtualize;\n const total = items.length;\n const start = Math.max(0, Math.floor(parent.scrollTop / rowHeight) - overscan);\n const end = Math.min(total, Math.ceil((parent.scrollTop + parent.clientHeight) / rowHeight) + overscan);\n syncRows(items.slice(start, end));\n container.style.paddingTop = `${start * rowHeight}px`;\n container.style.paddingBottom = `${Math.max(0, total - end) * rowHeight}px`;\n };\n\n const stopEffect = effect(() => {\n items = source.value; // tracking read — re-runs on any structural change\n renderWindow();\n });\n\n const onScroll = (): void => {\n if (rafPending) return;\n rafPending = true;\n globalThis.requestAnimationFrame(() => {\n rafPending = false;\n if (!disposed) renderWindow();\n });\n };\n if (virtualize !== undefined) parent.addEventListener('scroll', onScroll);\n\n return () => {\n disposed = true;\n stopEffect();\n for (const row of rows.values()) {\n row.dispose();\n if (virtualize === undefined) row.el.remove();\n }\n rows.clear();\n if (virtualize !== undefined) {\n parent.removeEventListener('scroll', onScroll);\n container.remove(); // removes the inner sizer and its rows in one go\n }\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/list.ts"],"names":["dispose"],"mappings":"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","file":"list.js","sourcesContent":["/**\n * `kerfjs/list` — `bindList`, a keyed list with a live per-row mount and\n * optional viewport virtualization.\n *\n * This is a DELIBERATE second list API, distinct from `each()`. It does two\n * things `each()` structurally cannot:\n * 1. **Per-row reactivity.** Every row is individually `mount()`ed, so a signal\n * the row's `render` reads updates just that row (fine-grained binding or a\n * one-row morph) without touching its siblings — no full-list pass.\n * 2. **Virtualization.** With `{ virtualize: { rowHeight } }` only the rows in\n * the scroll viewport are rendered; padding on the scroll container keeps\n * `scrollHeight` honest. `rowHeight` is a fixed `number` (O(1) windowing), a\n * `(item, index) => number` for **app-declared variable** heights (a prefix\n * sum + binary-search window), or `{ estimate }` for **measured** heights —\n * the app reports real heights via the returned handle's `setHeight` (or the\n * `observeRowHeights` helper) and kerf anchor-corrects `scrollTop`. See\n * `docs/17-list-virtualization.md`.\n *\n * `each()` stays the choice for item-owned-state lists rendered to HTML strings;\n * reach for `bindList` when you need surgical per-row updates or windowing.\n *\n * import { bindList } from 'kerfjs/list';\n *\n * const dispose = bindList(listEl, itemsSignal, {\n * key: (row) => row.id,\n * render: (row) => <span class={selected} data-id={row.id}>{row.label}</span>,\n * tag: 'li',\n * virtualize: { rowHeight: 32 },\n * });\n *\n * `render` reads signals for reactivity (external state like a `selectedId`, or\n * signals the item carries) — keep the item OBJECTS stable across renders and\n * drive structure (add/remove/move) through `itemsSignal`. A row whose item\n * object identity changes is rebuilt (same rule as `each()`'s memo). `bindList`\n * OWNS `parent`'s children by default (append/move to the end) — to share\n * `parent` with fixed trailing siblings (an \"add\" button, an indicator), pass\n * `before` so the rows end just before that node. It reads `itemsSignal.value`,\n * so a plain `signal<T[]>` or an `arraySignal<T>` both work.\n */\nimport { ARRAY_SIGNAL_BRAND, type ArrayPatch } from './array-signal.js';\nimport { mount, type MountResult } from './mount.js';\nimport { effect } from './reactive.js';\n\n/** A row's stable key. */\nexport type ListKey = string | number;\n\n/** Anything with a tracking `.value` array read — a `signal<readonly T[]>` or an `arraySignal<T>`. */\nexport interface ListSource<T> {\n readonly value: readonly T[];\n}\n\n/**\n * A row built imperatively by `render`: return the row **element** itself (kerf\n * keys/moves/reuses it and owns nothing inside it), or `{ el, update?, dispose? }`\n * to also hand back an `update(item)` — called on the SAME element when the row's\n * key persists but its item changes — and a `dispose` that runs only when the row\n * is removed.\n */\nexport type RowElement<T> =\n | HTMLElement\n | { el: HTMLElement; update?: (item: T) => void; dispose?: () => void };\n\n/**\n * The virtualization height model:\n * - **`number`** — every row is this fixed pixel height (O(1) windowing).\n * - **`(item, index) => number`** — app-declared **variable** heights, derived\n * purely from the item and its index.\n * - **`{ estimate }`** — **measured** heights: kerf uses `estimate` for a row\n * until the app reports its real height through {@link BindListHandle.setHeight}\n * (or the `observeRowHeights` helper). See `docs/17-list-virtualization.md`.\n */\nexport type RowHeight<T> =\n | number\n | ((item: T, index: number) => number)\n | { estimate: number | ((item: T, index: number) => number) };\n\n/**\n * The value {@link bindList} returns: a disposer you call to tear the list down,\n * augmented with `setHeight` for the **measured** virtualization mode.\n */\nexport type BindListHandle = (() => void) & {\n /**\n * Report a row's real pixel height (measured after layout) for\n * `virtualize: { rowHeight: { estimate } }` lists. Keyed by the list `key`, so\n * a report survives reorders. kerf recomputes the window and, if the row sits\n * ABOVE the viewport, anchor-corrects `scrollTop` so content doesn't jump.\n * A no-op for fixed / declared-height lists and for unknown keys.\n */\n setHeight: (key: ListKey, height: number) => void;\n /**\n * The inner container element kerf creates to hold the rows in a **virtualized**\n * list (the \"sizer\"). `undefined` for a non-virtualized list (there the rows\n * live directly in `parent`). Use it to style, id, or otherwise reach the row\n * block without guessing at `parent.lastElementChild` — though `containerClass`\n * / `containerId` on `virtualize` set those declaratively.\n */\n container?: HTMLElement;\n};\n\n/** Options for {@link bindList}. */\nexport interface BindListOptions<T> {\n /** Stable per-row key. Rows are matched, moved, and reused by this. */\n key: (item: T) => ListKey;\n /**\n * Build a row. Two modes, chosen per call by what you return:\n * - **Content mode** (a `MountResult` — JSX / `SafeHtml`): kerf creates the\n * row element (`tag`) and `mount()`s your content inside it, so signals your\n * content reads drive per-row reactivity.\n * - **Element mode** (an `HTMLElement`, or `{ el, update?, dispose? }`): the\n * element you return IS the row, so you own its tag, class, `data-*`, and\n * listeners. kerf **keys/moves/reuses** it — the SAME element survives an\n * append/remove/reorder or a fresh item object at the same key. Refresh its\n * content by reading signals inside it, or by returning an `update(item)`\n * that kerf calls on the existing element when the item changes. `dispose`\n * runs only when the row is genuinely removed.\n */\n render: (item: T) => MountResult | RowElement<T>;\n /** Row element tag for **content mode**. Default `'div'` (use `'li'` inside a `<ul>`, `'tr'` inside a `<tbody>`, …). Ignored in element mode. */\n tag?: string;\n /**\n * Keep the rows as a contiguous block that ENDS just before this node, instead\n * of at the very end of `parent`. Use it when `parent` also holds non-row\n * siblings that must stay put — a trailing \"add\" button, a sliding indicator:\n * `before: () => addButton`. The node (a function is re-read each reconcile, or\n * pass the node directly) must be a child of `parent`. Without it, bindList\n * assumes exclusive ownership and appends rows to the end. Ignored when\n * virtualized (the rows own bindList's inner sizer exclusively).\n */\n before?: Node | (() => Node | null);\n /**\n * Turn on viewport virtualization. `parent` must be a scroll container (your\n * CSS: a fixed height + `overflow: auto`). `overscan` (default 3) is how many\n * extra rows to render above and below the viewport.\n *\n * `rowHeight` (a {@link RowHeight}) is the height model:\n * - **`number`** — every row is this fixed pixel height. O(1) windowing, no\n * cumulative model built.\n * - **`(item, index) => number`** — app-declared **variable** heights, derived\n * purely from the item and its index. kerf builds a prefix sum of the\n * heights (rebuilt when the source array changes, not per scroll frame) and\n * binary-searches it to find the visible window. Return a non-negative\n * number of pixels.\n * - **`{ estimate }`** — **measured** heights for rows whose height is only\n * known after layout. kerf sizes an unmeasured row by `estimate` (a number\n * or an `(item, index) => number`), and the app reports each row's real\n * height via {@link BindListHandle.setHeight} (or the `observeRowHeights`\n * helper). kerf anchor-corrects `scrollTop` when an above-viewport row is\n * remeasured, so content doesn't jump.\n *\n * `minRows` renders **every** row (no windowing, zero padding) while the list\n * is shorter than it, and windows only at or above it — the DOM structure (the\n * inner container) is the same either way, so the call site never branches. A\n * fully-rendered short list is friendlier to find-in-page, screen readers, and\n * DOM-count assertions, which only see rows actually in the DOM.\n *\n * `containerClass` / `containerId` are set on the inner container kerf creates\n * to hold the rows, so it's reachable from CSS and tests without guessing at\n * `parent.lastElementChild` (it's also on the handle as `handle.container`).\n *\n * kerf re-windows on `parent`'s `scroll` and, where `ResizeObserver` exists, on\n * `parent` resizing — so a list that mounts before layout (a hidden tab,\n * `clientHeight` 0) fills in once it's sized, and a resized container re-windows.\n */\n virtualize?: {\n rowHeight: RowHeight<T>;\n overscan?: number;\n minRows?: number;\n containerClass?: string;\n containerId?: string;\n };\n}\n\ninterface Row<T> {\n el: HTMLElement;\n item: T;\n dispose: () => void;\n /** True for element-mode rows (the caller owns the element — reuse it, don't rebuild on item change). */\n elementMode: boolean;\n /** Element mode only: refresh the existing element when the item changes at the same key. */\n update?: (item: T) => void;\n}\n\n/**\n * Bind a keyed, per-row-reactive list to `parent`, driven by `source` (a\n * `signal<readonly T[]>` or an `arraySignal<T>`). Returns a disposer that tears\n * down every row mount, the scroll listener (if virtualized), and the source\n * subscription.\n */\nexport function bindList<T>(\n parent: HTMLElement,\n source: ListSource<T>,\n options: BindListOptions<T>,\n): BindListHandle {\n const { key, render, tag = 'div', virtualize, before } = options;\n const overscan = virtualize?.overscan ?? 3;\n const minRows = virtualize?.minRows;\n\n // The node the row block ends before — `before` (KF-496) when the list shares\n // `parent` with trailing siblings, else the end of the container. Never applies\n // when virtualized: the rows own bindList's inner sizer exclusively.\n const endAnchor = (): Node | null => {\n if (virtualize !== undefined || before === undefined) return null;\n return (typeof before === 'function' ? before() : before) ?? null;\n };\n\n const rows = new Map<ListKey, Row<T>>();\n // The current DOM order of rows, kept in step by both the keyed-diff and the\n // granular patch paths so index-based patches can address rows directly.\n const order: Array<Row<T>> = [];\n let items: readonly T[] = [];\n let disposed = false;\n let rafPending = false;\n let firstRender = true;\n\n // Granular fast path (KF-478): when the source is an `arraySignal` and the\n // list is NOT virtualized, apply its insert/remove/move/update patches\n // directly in O(patches) instead of diffing the whole snapshot. Virtualized\n // lists keep the keyed diff — their visible set is just the window (cheap),\n // and absolute-index patches don't compose with a shifting window. A plain\n // `signal<T[]>` has no patches, so it always uses the keyed diff.\n const patchSource = source as {\n [ARRAY_SIGNAL_BRAND]?: boolean;\n _consumePatches?: () => ArrayPatch<T>[];\n };\n const granularEligible = virtualize === undefined && patchSource[ARRAY_SIGNAL_BRAND] === true;\n\n // Virtualized lists put the windowing padding + rows on an INNER sizer, so the\n // padding never inflates the scroll container's clientHeight (padding counts\n // toward clientHeight). `parent` stays the clean scroll viewport; `container`\n // holds the rows. Non-virtualized lists render straight into `parent`.\n const container: HTMLElement = virtualize === undefined ? parent : document.createElement('div');\n if (virtualize !== undefined) {\n if (virtualize.containerClass !== undefined) container.className = virtualize.containerClass;\n if (virtualize.containerId !== undefined) container.id = virtualize.containerId;\n parent.appendChild(container);\n }\n\n const NOOP = (): void => { /* element-mode rows with no caller teardown */ };\n\n // Detect element mode from a render result: a raw `HTMLElement`, or a\n // `{ el, dispose? }` object. Everything else (SafeHtml / string / nullish) is\n // content mode. SafeHtml is an object but has no `el`, so it never matches.\n const asElementRow = (\n rendered: MountResult | RowElement<T>,\n ): { el: HTMLElement; dispose: () => void; update?: (item: T) => void } | null => {\n if (rendered instanceof HTMLElement) return { el: rendered, dispose: NOOP };\n if (\n rendered !== null\n && typeof rendered === 'object'\n && 'el' in rendered\n && (rendered as { el: unknown }).el instanceof HTMLElement\n ) {\n const r = rendered as { el: HTMLElement; update?: (item: T) => void; dispose?: () => void };\n return { el: r.el, dispose: r.dispose ?? NOOP, update: r.update };\n }\n return null;\n };\n\n const makeRow = (item: T): Row<T> => {\n // One call decides the mode per row (so a list may mix element + content rows).\n const elementRow = asElementRow(render(item));\n if (elementRow !== null) {\n // Element mode: the returned element IS the row; the caller owns its\n // content + cleanup. bindList sizes it for the windowing math per render\n // (see `sizeVisibleRows`), not here, since a variable height depends on the\n // row's current index in the full list.\n return { el: elementRow.el, item, dispose: elementRow.dispose, elementMode: true, update: elementRow.update };\n }\n // Content mode: kerf creates the row element and mounts `render` inside it,\n // so the content is per-row reactive. (In content mode `render` runs once\n // more here for the mode probe than the mount itself needs — keep it a pure\n // projection, which bindList already requires.)\n const el = document.createElement(tag);\n // Content mode: `render` returns a MountResult here (element results were\n // handled above), so narrowing it for `mount` is sound.\n const dispose = mount(el, () => render(item) as MountResult);\n return { el, item, dispose, elementMode: false };\n };\n\n // A row whose KEY persists but whose item object changed. Content-mode rows are\n // rebuilt (their mount re-renders the fresh item); element-mode rows are REUSED\n // — the caller owns the element, so we keep it (preserving focus / scroll /\n // listeners) and refresh via the optional `update(item)`. Returns the row to\n // use at that key (a fresh one for content, the same one for element).\n const reconcileItem = (row: Row<T>, k: ListKey, item: T): Row<T> => {\n if (row.item === item) return row;\n if (row.elementMode) {\n row.item = item;\n row.update?.(item);\n return row;\n }\n row.dispose();\n row.el.remove();\n rows.delete(k);\n const fresh = makeRow(item);\n rows.set(k, fresh);\n return fresh;\n };\n\n // Reconcile the live rows to exactly `visible`, in order, keyed.\n const syncRows = (visible: readonly T[]): void => {\n const wanted = new Set<ListKey>();\n for (const item of visible) wanted.add(key(item));\n\n // Remove rows that are gone from the window.\n for (const [k, row] of rows) {\n if (!wanted.has(k)) {\n row.dispose();\n row.el.remove();\n rows.delete(k);\n }\n }\n\n // Create missing rows; reuse existing ones by key (element rows keep their\n // element across item changes; content rows rebuild on identity change).\n order.length = 0;\n for (const item of visible) {\n const k = key(item);\n const existing = rows.get(k);\n let row: Row<T>;\n if (existing !== undefined) {\n row = reconcileItem(existing, k, item);\n } else {\n row = makeRow(item);\n rows.set(k, row);\n }\n order.push(row);\n }\n\n // Reverse pass: move only rows that are out of position.\n let ref: Node | null = endAnchor();\n for (let i = order.length - 1; i >= 0; i--) {\n const el = order[i].el;\n if (el.parentNode !== container || el.nextSibling !== ref) {\n container.insertBefore(el, ref);\n }\n ref = el;\n }\n };\n\n // Apply arraySignal structural patches directly to `order` + the DOM, in\n // O(patches). Indices are always valid by construction: `order` reflects the\n // last-rendered state and the patches are exactly the delta from it (bindList\n // drains the queue every render, and `replace` is filtered out by the caller,\n // which snapshots instead). The `splice()`s mirror `arraySignal`'s own\n // `_items` mutations exactly.\n const applyPatches = (patches: readonly ArrayPatch<T>[]): void => {\n for (const patch of patches) {\n if (patch.type === 'insert') {\n const row = makeRow(patch.item);\n rows.set(key(patch.item), row);\n order.splice(patch.index, 0, row);\n container.insertBefore(row.el, order[patch.index + 1]?.el ?? endAnchor());\n } else if (patch.type === 'remove') {\n const [row] = order.splice(patch.index, 1);\n row.dispose();\n row.el.remove();\n rows.delete(key(row.item));\n } else if (patch.type === 'move') {\n const [row] = order.splice(patch.from, 1);\n order.splice(patch.to, 0, row);\n container.insertBefore(row.el, order[patch.to + 1]?.el ?? endAnchor());\n } else if (patch.type === 'update') {\n // An item whose OBJECT identity changed: content rows rebuild (their mount\n // re-renders the fresh item); element rows are REUSED — keep the caller's\n // element and refresh via update(), re-keying if the key changed. A\n // same-ref update needs nothing (the row's mount reacts to its signals).\n const current = order[patch.index];\n if (current.item !== patch.item) {\n if (current.elementMode) {\n const oldKey = key(current.item);\n const newKey = key(patch.item);\n current.item = patch.item;\n if (newKey !== oldKey) {\n rows.delete(oldKey);\n rows.set(newKey, current);\n }\n current.update?.(patch.item);\n } else {\n current.dispose();\n current.el.remove();\n rows.delete(key(current.item));\n const row = makeRow(patch.item);\n rows.set(key(patch.item), row);\n order[patch.index] = row;\n container.insertBefore(row.el, order[patch.index + 1]?.el ?? endAnchor());\n }\n }\n }\n // 'replace' never reaches here — the caller snapshots on it.\n }\n };\n\n // Virtualization height model, three modes:\n // - `fixedHeight` (a `number`): the O(1) fast path — no cumulative model.\n // - `variableHeightAt` (a function): app-declared per-row heights.\n // - measuring (`{ estimate }`): `variableHeightAt` returns the measured height\n // when the app has reported one (via `setHeight`), else the estimate.\n // In the two variable cases, `offsets[i]` is the total height of rows 0..i-1\n // (a prefix sum, length total+1), so `offsets[i+1] - offsets[i]` is row i's\n // height and `offsets[total]` is the full scroll height. It is rebuilt only\n // when `items` changes or a height is reported (heightsDirty), never per scroll\n // frame — a scroll reuses the prefix sum and pays only the O(log n) searches.\n const rowHeight = virtualize?.rowHeight;\n const fixedHeight = typeof rowHeight === 'number' ? rowHeight : null;\n const measuring = typeof rowHeight === 'object' && rowHeight !== null;\n const measured = new Map<ListKey, number>(); // key → real reported height\n const estimateAt = (index: number): number => {\n const est = (rowHeight as { estimate: number | ((item: T, index: number) => number) }).estimate;\n return typeof est === 'function' ? est(items[index], index) : est;\n };\n const variableHeightAt: ((index: number) => number) | null =\n fixedHeight !== null\n ? null\n : measuring\n ? (index): number => {\n const k = key(items[index]);\n return measured.has(k) ? (measured.get(k) as number) : estimateAt(index);\n }\n : (index): number => (rowHeight as (item: T, index: number) => number)(items[index], index);\n\n let offsets: number[] = [0];\n let heightsDirty = true;\n // Measuring only: key → current absolute index, so `setHeight(key, …)` locates\n // the row in O(1). Rebuilt with the prefix sum when `items` changes.\n const indexByKey = new Map<ListKey, number>();\n // Accumulated scroll-anchor correction: the summed height delta of remeasured\n // rows that sit entirely ABOVE the viewport top, applied to `scrollTop` before\n // the next window render so on-screen content does not jump.\n let pendingAnchorDelta = 0;\n\n const rebuildOffsets = (): void => {\n const fn = variableHeightAt as (index: number) => number;\n const total = items.length;\n offsets = new Array<number>(total + 1);\n offsets[0] = 0;\n if (measuring) indexByKey.clear();\n for (let i = 0; i < total; i++) {\n offsets[i + 1] = offsets[i] + fn(i);\n if (measuring) indexByKey.set(key(items[i]), i);\n }\n };\n\n // Greatest index i in [0, total] with `offsets[i] <= target` — the first row\n // whose top is at or above `target` (the viewport top).\n const findStart = (target: number, total: number): number => {\n let lo = 0;\n let hi = total;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (offsets[mid] <= target) lo = mid;\n else hi = mid - 1;\n }\n return lo;\n };\n\n // Smallest index i in [0, total] with `offsets[i] >= target` — one past the\n // last row that starts before `target` (the viewport bottom). `total` if none.\n const findEnd = (target: number, total: number): number => {\n let lo = 0;\n let hi = total;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (offsets[mid] >= target) hi = mid;\n else lo = mid + 1;\n }\n return lo;\n };\n\n // Size each visible row for the windowing math. `order` holds the visible rows\n // in order, so `order[j]` is the item at absolute index `start + j`.\n // MEASURED mode is the exception: the row must take its NATURAL height so the\n // app (or `observeRowHeights`) can read the real `offsetHeight` — forcing a\n // height here would make the measurement echo the estimate. Its offsets come\n // from `setHeight` reports instead.\n const sizeVisibleRows = (start: number): void => {\n if (measuring) return;\n for (let j = 0; j < order.length; j++) {\n const abs = start + j;\n const h = fixedHeight !== null ? fixedHeight : offsets[abs + 1] - offsets[abs];\n order[j].el.style.height = `${h}px`;\n }\n };\n\n // Called after each virtualized window render (used by `observeRowHeights` to\n // re-observe the current visible rows).\n const renderSubscribers = new Set<() => void>();\n\n const renderWindow = (): void => {\n if (virtualize === undefined) {\n if (granularEligible) {\n // Always drain to keep the single patch queue clean (so patches never\n // double-apply). Take the granular path past the first render, when\n // there are patches, and none is a `replace` (which reshapes the whole\n // array — snapshot instead). Otherwise fall through to a keyed diff.\n const patches = patchSource._consumePatches!();\n if (\n !firstRender\n && patches.length > 0\n && !patches.some((p) => p.type === 'replace')\n ) {\n applyPatches(patches);\n return;\n }\n }\n syncRows(items);\n firstRender = false;\n return;\n }\n const total = items.length;\n let start: number;\n let end: number;\n let padTop: number;\n let padBottom: number;\n if (minRows !== undefined && total < minRows) {\n // Below the threshold: render EVERY row, no windowing, zero padding — one\n // DOM structure (the inner container) shared with the windowed path, so the\n // caller never branches. Rows are still sized (declared/fixed) from the\n // prefix sum, which we still build for the sizing pass.\n if (fixedHeight === null && heightsDirty) {\n rebuildOffsets();\n heightsDirty = false;\n }\n start = 0;\n end = total;\n padTop = 0;\n padBottom = 0;\n } else {\n const scrollTop = parent.scrollTop;\n const viewportBottom = scrollTop + parent.clientHeight;\n if (fixedHeight !== null) {\n start = Math.max(0, Math.floor(scrollTop / fixedHeight) - overscan);\n end = Math.min(total, Math.ceil(viewportBottom / fixedHeight) + overscan);\n padTop = start * fixedHeight;\n padBottom = Math.max(0, total - end) * fixedHeight;\n } else {\n if (heightsDirty) {\n rebuildOffsets();\n heightsDirty = false;\n }\n start = Math.max(0, findStart(scrollTop, total) - overscan);\n end = Math.min(total, findEnd(viewportBottom, total) + overscan);\n padTop = offsets[start];\n padBottom = offsets[total] - offsets[end];\n }\n }\n syncRows(items.slice(start, end));\n sizeVisibleRows(start);\n container.style.paddingTop = `${padTop}px`;\n container.style.paddingBottom = `${padBottom}px`;\n for (const cb of renderSubscribers) cb();\n };\n\n const stopEffect = effect(() => {\n items = source.value; // tracking read — re-runs on any structural change\n heightsDirty = true; // items changed → the prefix sum (if any) is stale\n renderWindow();\n });\n\n // One rAF-coalesced render, shared by scroll and by measurement reports. A\n // pending anchor correction is applied to `scrollTop` first (which itself may\n // fire a scroll, but with the delta already cleared the follow-up is a no-op).\n const scheduleRender = (): void => {\n if (rafPending) return;\n rafPending = true;\n globalThis.requestAnimationFrame(() => {\n rafPending = false;\n if (disposed) return;\n if (pendingAnchorDelta !== 0) {\n parent.scrollTop += pendingAnchorDelta;\n pendingAnchorDelta = 0;\n }\n renderWindow();\n });\n };\n if (virtualize !== undefined) parent.addEventListener('scroll', scheduleRender);\n\n // Re-window when `parent` RESIZES, not just on scroll. This makes two cases\n // robust that the scroll-only model missed: a list mounted before layout\n // (`clientHeight` 0 — a hidden tab, pre-first-paint) fills in once it's sized,\n // and a container resized while open re-windows. ResizeObserver fires an\n // initial callback on observe, so the 0-height case self-heals with no synthetic\n // scroll. Absent (older SSR/runtime) → scroll-only, as before.\n const RO = globalThis.ResizeObserver;\n const parentResize = virtualize !== undefined && RO !== undefined ? new RO(scheduleRender) : undefined;\n parentResize?.observe(parent);\n\n // Measured mode: report a row's real height. No-op for fixed / declared lists\n // and for keys not currently in the list.\n const setHeight = (k: ListKey, height: number): void => {\n if (!measuring) return;\n const idx = indexByKey.get(k);\n if (idx === undefined) return;\n const oldHeight = measured.has(k) ? (measured.get(k) as number) : estimateAt(idx);\n if (height === oldHeight) return;\n measured.set(k, height);\n // A row whose bottom is at/above the viewport top shifts everything below it\n // (the on-screen content) by the height delta — correct `scrollTop` to match.\n // Uses the CURRENT (pre-rebuild) offsets, which reflect the on-screen layout.\n if (offsets[idx + 1] <= parent.scrollTop) pendingAnchorDelta += height - oldHeight;\n heightsDirty = true;\n scheduleRender();\n };\n\n const dispose = ((): void => {\n disposed = true;\n stopEffect();\n for (const row of rows.values()) {\n row.dispose();\n if (virtualize === undefined) row.el.remove();\n }\n rows.clear();\n renderSubscribers.clear();\n if (virtualize !== undefined) {\n parent.removeEventListener('scroll', scheduleRender);\n parentResize?.disconnect();\n container.remove(); // removes the inner sizer and its rows in one go\n VIRTUAL_INTERNALS.delete(handle);\n }\n }) as BindListHandle;\n const handle = dispose;\n handle.setHeight = setHeight;\n\n // Register the coordination surface the `observeRowHeights` helper needs, kept\n // off the public type (a GC-tied WeakMap, so it doesn't count against Design\n // rule 5). Only virtualized lists have a window to observe.\n if (virtualize !== undefined) {\n handle.container = container;\n VIRTUAL_INTERNALS.set(handle, {\n visibleRows: () => order.map((row) => ({ key: key(row.item), el: row.el })),\n onRender: (cb) => {\n renderSubscribers.add(cb);\n return () => renderSubscribers.delete(cb);\n },\n });\n }\n\n return handle;\n}\n\n/** Internal coordination surface between {@link bindList} and {@link observeRowHeights}. */\ninterface VirtualInternals {\n /** The current visible rows, in order, with their keys. */\n visibleRows: () => Array<{ key: ListKey; el: HTMLElement }>;\n /** Subscribe to each window render; returns an unsubscribe. */\n onRender: (cb: () => void) => () => void;\n}\n\n// GC-tied (WeakMap) coordination store — a pure cache, not counted against\n// Design rule 5 (same class as `bindings.ts:insertedTextNodes`).\nconst VIRTUAL_INTERNALS = new WeakMap<object, VirtualInternals>();\n\n/**\n * Drive a **measured** virtualized `bindList` (`virtualize: { rowHeight: {\n * estimate } }`) from real layout: install ONE `ResizeObserver` over the visible\n * rows and forward each row's `offsetHeight` to `handle.setHeight`, re-observing\n * as the window shifts. Returns a disposer.\n *\n * This is the batteries-included measurement path; it is deliberately separate\n * from `bindList` (which never depends on `ResizeObserver`) — you can measure\n * however you like and call `handle.setHeight` yourself instead. A no-op for a\n * non-virtualized handle or where `ResizeObserver` is unavailable (SSR).\n *\n * const list = bindList(scrollEl, source, { key, render, virtualize: { rowHeight: { estimate: 64 } } });\n * const stopMeasuring = observeRowHeights(list);\n */\nexport function observeRowHeights(handle: BindListHandle): () => void {\n const internals = VIRTUAL_INTERNALS.get(handle);\n const RO = globalThis.ResizeObserver;\n if (internals === undefined || RO === undefined) return () => { /* nothing to observe */ };\n\n const keyByEl = new WeakMap<Element, ListKey>();\n const observer = new RO((entries) => {\n for (const entry of entries) {\n const k = keyByEl.get(entry.target);\n if (k !== undefined) handle.setHeight(k, (entry.target as HTMLElement).offsetHeight);\n }\n });\n\n const resync = (): void => {\n observer.disconnect();\n for (const { key: k, el } of internals.visibleRows()) {\n keyByEl.set(el, k);\n observer.observe(el);\n }\n };\n\n const unsubscribe = internals.onRender(resync);\n resync(); // observe the initial window\n\n return () => {\n observer.disconnect();\n unsubscribe();\n };\n}\n"]}
|
package/dist/overlay.d.ts
CHANGED
|
@@ -380,8 +380,15 @@ interface ToastOptions {
|
|
|
380
380
|
interface ToastHandle {
|
|
381
381
|
/** The toast element — inspect it, or run your own entrance/exit transitions. */
|
|
382
382
|
el: HTMLElement;
|
|
383
|
-
/**
|
|
384
|
-
|
|
383
|
+
/**
|
|
384
|
+
* Dismiss it early. Default runs the `exitClass` transition (removed after
|
|
385
|
+
* `exitDuration`); pass `{ instant: true }` to remove it **synchronously** with
|
|
386
|
+
* no exit — for an action button that immediately shows a replacement toast in a
|
|
387
|
+
* single centered slot (no cross-fade). Idempotent.
|
|
388
|
+
*/
|
|
389
|
+
dismiss(options?: {
|
|
390
|
+
instant?: boolean;
|
|
391
|
+
}): void;
|
|
385
392
|
}
|
|
386
393
|
/**
|
|
387
394
|
* Show a non-modal, auto-dismissing notification. Stacks in a shared body-level
|
package/dist/overlay.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { mount } from './chunk-
|
|
1
|
+
import { mount } from './chunk-LKWAKC2X.js';
|
|
2
2
|
import { delegate } from './chunk-KEZTD6H4.js';
|
|
3
3
|
import './chunk-QIP723L4.js';
|
|
4
4
|
import './chunk-YHH7OUFA.js';
|
|
5
|
-
import { jsx } from './chunk-
|
|
5
|
+
import { jsx } from './chunk-SUPUPSBE.js';
|
|
6
6
|
import './chunk-3APBEVHF.js';
|
|
7
7
|
import './chunk-GY4XV2UV.js';
|
|
8
8
|
import './chunk-VVDJLWMP.js';
|
|
@@ -534,39 +534,44 @@ function toast(content, options = {}) {
|
|
|
534
534
|
el.setAttribute("role", role);
|
|
535
535
|
region.appendChild(el);
|
|
536
536
|
const disposeMount = mount(el, typeof content === "function" ? content : () => content);
|
|
537
|
-
const state = { dismissed: false, timer: void 0 };
|
|
537
|
+
const state = { dismissed: false, removed: false, timer: void 0, exitTimer: void 0 };
|
|
538
538
|
if (enterClass !== void 0) {
|
|
539
539
|
globalThis.requestAnimationFrame(() => {
|
|
540
540
|
if (!state.dismissed) el.classList.add(enterClass);
|
|
541
541
|
});
|
|
542
542
|
}
|
|
543
543
|
const remove = () => {
|
|
544
|
+
if (state.removed) return;
|
|
545
|
+
state.removed = true;
|
|
546
|
+
state.dismissed = true;
|
|
547
|
+
if (state.exitTimer !== void 0) clearTimeout(state.exitTimer);
|
|
544
548
|
disposeMount();
|
|
545
549
|
el.remove();
|
|
546
550
|
active.delete(dismiss);
|
|
547
551
|
};
|
|
548
|
-
const
|
|
552
|
+
const removeNow = () => {
|
|
553
|
+
if (state.timer !== void 0) clearTimeout(state.timer);
|
|
554
|
+
remove();
|
|
555
|
+
};
|
|
556
|
+
const fadeOut = () => {
|
|
549
557
|
if (state.dismissed) return;
|
|
550
558
|
state.dismissed = true;
|
|
551
559
|
if (state.timer !== void 0) clearTimeout(state.timer);
|
|
552
|
-
if (instant) {
|
|
553
|
-
remove();
|
|
554
|
-
return;
|
|
555
|
-
}
|
|
556
560
|
if (enterClass !== void 0) el.classList.remove(enterClass);
|
|
557
561
|
if (exitClass !== void 0) el.classList.add(exitClass);
|
|
558
562
|
if (exitClass !== void 0 || exitDuration > 0) {
|
|
559
|
-
setTimeout(remove, exitDuration);
|
|
563
|
+
state.exitTimer = setTimeout(remove, exitDuration);
|
|
560
564
|
} else {
|
|
561
565
|
remove();
|
|
562
566
|
}
|
|
563
567
|
};
|
|
564
|
-
function dismiss() {
|
|
565
|
-
|
|
568
|
+
function dismiss(options2) {
|
|
569
|
+
if (options2?.instant === true) removeNow();
|
|
570
|
+
else fadeOut();
|
|
566
571
|
}
|
|
567
|
-
dismiss.removeNow =
|
|
572
|
+
dismiss.removeNow = removeNow;
|
|
568
573
|
active.add(dismiss);
|
|
569
|
-
if (duration > 0) state.timer = setTimeout(
|
|
574
|
+
if (duration > 0) state.timer = setTimeout(fadeOut, duration);
|
|
570
575
|
return { el, dismiss };
|
|
571
576
|
}
|
|
572
577
|
|