@firsthandjs/dom 0.8.0 → 0.9.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.
@@ -1,3 +1,18 @@
1
+ import {
2
+ FLAT,
3
+ PENDING,
4
+ applyChild,
5
+ bindPart,
6
+ devComponent,
7
+ devPart,
8
+ devWarn,
9
+ devWarnOnce,
10
+ discard,
11
+ hydration,
12
+ isDynamicChild,
13
+ part
14
+ } from "./dev-chunk-ADHDPKZW.js";
15
+
1
16
  // packages/dom/src/adapter.ts
2
17
  var adapter = null;
3
18
  var adapted = /* @__PURE__ */ new WeakMap();
@@ -27,398 +42,16 @@ function adapt(target) {
27
42
  return component_;
28
43
  }
29
44
 
30
- // packages/dom/src/dev.ts
31
- function devWarn(message) {
32
- console.warn(`[firsthand] ${message}`);
33
- }
34
- var seen = /* @__PURE__ */ new Set();
35
- function devWarnOnce(key2, message) {
36
- if (!seen.has(key2)) {
37
- seen.add(key2);
38
- devWarn(message);
39
- }
40
- }
41
- function devWarnRenderedObject(value) {
42
- const cellish = "peek" in value && typeof value.peek === "function" && "value" in value;
43
- devWarnOnce(
44
- cellish ? "rendered-cell" : "rendered-object",
45
- cellish ? 'A signal or computed was rendered directly, so the page shows "[object Object]". Read it instead: {count.value} rather than {count}.' : `An object was rendered as text, so the page shows "${String(value)}". Render a string, a number or a node.`
46
- );
47
- }
48
- function hook() {
49
- const installed2 = globalThis.__FIRSTHAND_DEVTOOLS__;
50
- return installed2 !== void 0 && installed2.attached ? installed2 : void 0;
51
- }
52
- function devPart(node, property) {
53
- hook()?.part(node, property);
54
- }
55
- function devComponent(owner, name) {
56
- hook()?.component(owner, name);
57
- }
58
- function label(value, kind, name) {
59
- if (typeof value === "object" && value !== null) {
60
- hook()?.label(value, kind, name);
61
- }
62
- return value;
63
- }
64
- var QUIET = 20;
65
- function devRan(store2) {
66
- if (store2.busy === true) {
67
- store2.busy = false;
68
- store2.quiet = 0;
69
- return;
70
- }
71
- const quiet = (store2.quiet ?? 0) + 1;
72
- store2.quiet = quiet;
73
- if (quiet !== QUIET) {
74
- return;
75
- }
76
- devWarnOnce(
77
- `quiet-run:${store2.name}`,
78
- `<${store2.name}> ran ${String(QUIET)} times and wrote nothing.
79
- Something it reads in a statement changes more often than what it shows. Move that read into the markup, where it is a part of its own, or move the derivation into a computed in the setup.`
80
- );
81
- }
82
- function devHandedNewFunction(store2, value) {
83
- if (typeof value !== "function") {
84
- return;
85
- }
86
- devWarnOnce(
87
- `new-function:${store2.name}`,
88
- `<${store2.name}> hands a child a new function on every run, so that child runs again whenever this one does.
89
- If the handler does not depend on the run, define it in the setup.`
90
- );
91
- }
92
-
93
- // packages/dom/src/insert.ts
94
- import {
95
- bind,
96
- createOwner,
97
- signal,
98
- disposeOwner,
99
- getOwner,
100
- onCleanup,
101
- runWithOwner,
102
- setOwner
103
- } from "@firsthandjs/core";
104
- var TEXT_NODE = 3;
105
- function isNode(value) {
106
- return typeof value.nodeType === "number";
107
- }
108
- var PART = /* @__PURE__ */ Symbol("firsthand.part");
109
- function part(thunk) {
110
- return { [PART]: true, anchor: document.createTextNode(""), thunk, owner: getOwner() };
111
- }
112
- function isDynamicChild(value) {
113
- return PART in value;
114
- }
115
- function store(name = "") {
116
- return { slots: [], owner: getOwner(), generation: 0, name };
117
- }
118
- function wrote(store2) {
119
- store2.busy = true;
120
- }
121
- function site(store2, index) {
122
- const existing = store2.slots[index];
123
- if (existing !== void 0) {
124
- existing.seen = store2.generation;
125
- return existing;
126
- }
127
- const slot = { seen: store2.generation };
128
- store2.slots[index] = slot;
129
- return slot;
130
- }
131
- function open(store2, slot) {
132
- const owner = createOwner(store2.owner);
133
- slot.owner = owner;
134
- return setOwner(owner);
135
- }
136
- function close(previous) {
137
- setOwner(previous);
138
- }
139
- function ran(store2, value) {
140
- devRan(store2);
141
- const generation = store2.generation;
142
- const slots = store2.slots;
143
- for (let i = 0; i < slots.length; i++) {
144
- const slot = slots[i];
145
- if (slot === void 0 || slot.seen === generation) {
146
- continue;
147
- }
148
- if (slot.owner !== void 0) {
149
- disposeOwner(slot.owner);
150
- }
151
- slots[i] = void 0;
152
- }
153
- store2.generation = generation + 1;
154
- return value;
155
- }
156
- function cell(store2, index, value) {
157
- const slot = site(store2, index);
158
- const existing = slot.cell;
159
- if (existing === void 0) {
160
- const made = signal(value);
161
- slot.cell = made;
162
- return made;
163
- }
164
- if (existing.peek() !== value) {
165
- devHandedNewFunction(store2, value);
166
- wrote(store2);
167
- existing.value = value;
168
- }
169
- return existing;
170
- }
171
- function writeChild(store2, index, parent, marker, value) {
172
- const slot = site(store2, index);
173
- const type = typeof value;
174
- if (type === "string" || type === "number") {
175
- const text = String(value);
176
- if (text === slot.text) {
177
- return;
178
- }
179
- wrote(store2);
180
- slot.text = text;
181
- slot.node = applyChild(parent, marker, slot.node ?? null, text);
182
- return;
183
- }
184
- slot.text = void 0;
185
- if (value === slot.last) {
186
- return;
187
- }
188
- wrote(store2);
189
- slot.last = value;
190
- slot.node = applyChild(parent, marker, slot.node ?? null, value);
191
- }
192
- function insert(parent, value, marker = null) {
193
- if (typeof value !== "function") {
194
- applyChild(parent, marker, null, value);
195
- return;
196
- }
197
- let current = null;
198
- let written;
199
- bind(() => {
200
- const next = value();
201
- const type = typeof next;
202
- if (type === "string" || type === "number") {
203
- const text = String(next);
204
- if (text !== written) {
205
- written = text;
206
- current = applyChild(parent, marker, current, text);
207
- }
208
- return;
209
- }
210
- written = void 0;
211
- if (type === "function") {
212
- current = clear(current);
213
- insert(parent, next, marker);
214
- return;
215
- }
216
- current = applyChild(parent, marker, current, next);
217
- });
218
- onCleanup(() => {
219
- current = clear(current);
220
- });
221
- }
222
- function applyChild(parent, marker, current, value) {
223
- const type = typeof value;
224
- if (value == null || type === "boolean") {
225
- devPart(parent, "text");
226
- return clear(current);
227
- }
228
- if (type === "string" || type === "number") {
229
- devPart(parent, "text");
230
- const text = String(value);
231
- if (current !== null && !Array.isArray(current) && current.nodeType === TEXT_NODE) {
232
- current.data = text;
233
- return current;
234
- }
235
- return single(parent, marker, current, document.createTextNode(text));
236
- }
237
- if (type === "function") {
238
- return applyChild(parent, marker, current, value());
239
- }
240
- if (Array.isArray(value)) {
241
- const next = [];
242
- const dynamic = [];
243
- flatten(value, next, dynamic);
244
- if (next.length === 0) {
245
- return clear(current);
246
- }
247
- reconcile(
248
- parent,
249
- marker,
250
- current === null ? [] : Array.isArray(current) ? current : [current],
251
- next
252
- );
253
- for (let i = 0; i < dynamic.length; i++) {
254
- const child = dynamic[i];
255
- runWithOwner(child.owner ?? getOwner(), () => {
256
- insert(parent, child.thunk, child.anchor);
257
- });
258
- }
259
- return next;
260
- }
261
- if (isDynamicChild(value)) {
262
- return applyChild(parent, marker, current, [value]);
263
- }
264
- if (isNode(value)) {
265
- return single(parent, marker, current, value);
266
- }
267
- devWarnRenderedObject(value);
268
- return applyChild(parent, marker, current, String(value));
269
- }
270
- function flatten(value, out, dynamic) {
271
- for (let i = 0; i < value.length; i++) {
272
- let item = value[i];
273
- if (typeof item === "object" && item !== null && isDynamicChild(item)) {
274
- out.push(item.anchor);
275
- dynamic.push(item);
276
- continue;
277
- }
278
- while (typeof item === "function") {
279
- item = item();
280
- }
281
- if (item == null || typeof item === "boolean") {
282
- continue;
283
- }
284
- if (Array.isArray(item)) {
285
- flatten(item, out, dynamic);
286
- } else if (typeof item === "object" && isNode(item)) {
287
- out.push(item);
288
- } else {
289
- out.push(document.createTextNode(String(item)));
290
- }
291
- }
292
- }
293
- function detach(node) {
294
- const owner = node.parentNode;
295
- if (owner !== null) {
296
- owner.removeChild(node);
297
- }
298
- }
299
- function clear(current) {
300
- if (current !== null) {
301
- if (Array.isArray(current)) {
302
- for (let i = 0; i < current.length; i++) {
303
- detach(current[i]);
304
- }
305
- } else {
306
- detach(current);
307
- }
308
- }
309
- return null;
310
- }
311
- function single(parent, marker, current, node) {
312
- if (current === node) {
313
- return node;
314
- }
315
- if (current !== null && !Array.isArray(current)) {
316
- parent.replaceChild(node, current);
317
- return node;
318
- }
319
- clear(current);
320
- parent.insertBefore(node, marker);
321
- return node;
322
- }
323
- function reconcile(parent, marker, a, b) {
324
- let aStart = 0;
325
- let bStart = 0;
326
- let aEnd = a.length - 1;
327
- let bEnd = b.length - 1;
328
- while (aStart <= aEnd && bStart <= bEnd && a[aStart] === b[bStart]) {
329
- aStart++;
330
- bStart++;
331
- }
332
- while (aStart <= aEnd && bStart <= bEnd && a[aEnd] === b[bEnd]) {
333
- aEnd--;
334
- bEnd--;
335
- }
336
- const after = bEnd + 1 < b.length ? b[bEnd + 1] : marker;
337
- if (aStart > aEnd) {
338
- for (let i = bStart; i <= bEnd; i++) {
339
- parent.insertBefore(b[i], after);
340
- }
341
- return;
342
- }
343
- if (bStart > bEnd) {
344
- for (let i = aStart; i <= aEnd; i++) {
345
- parent.removeChild(a[i]);
346
- }
347
- return;
348
- }
349
- const oldIndex = /* @__PURE__ */ new Map();
350
- for (let i = aStart; i <= aEnd; i++) {
351
- oldIndex.set(a[i], i);
352
- }
353
- const middle = bEnd - bStart + 1;
354
- const sources = new Int32Array(middle).fill(-1);
355
- for (let j = 0; j < middle; j++) {
356
- const node = b[bStart + j];
357
- const found = oldIndex.get(node);
358
- if (found !== void 0) {
359
- sources[j] = found;
360
- oldIndex.delete(node);
361
- }
362
- }
363
- for (const node of oldIndex.keys()) {
364
- parent.removeChild(node);
365
- }
366
- const keep = longestIncreasing(sources);
367
- let k = keep.length - 1;
368
- let anchor = after;
369
- for (let j = middle - 1; j >= 0; j--) {
370
- const node = b[bStart + j];
371
- if (k >= 0 && keep[k] === j) {
372
- k--;
373
- } else {
374
- parent.insertBefore(node, anchor);
375
- }
376
- anchor = node;
377
- }
378
- }
379
- function longestIncreasing(sources) {
380
- const length = sources.length;
381
- const predecessor = new Int32Array(length).fill(-1);
382
- const tails = [];
383
- for (let i = 0; i < length; i++) {
384
- const value = sources[i];
385
- if (value === -1) {
386
- continue;
387
- }
388
- let low = 0;
389
- let high = tails.length;
390
- while (low < high) {
391
- const mid = low + high >> 1;
392
- if (sources[tails[mid]] < value) {
393
- low = mid + 1;
394
- } else {
395
- high = mid;
396
- }
397
- }
398
- if (low > 0) {
399
- predecessor[i] = tails[low - 1];
400
- }
401
- tails[low] = i;
402
- }
403
- const result = [];
404
- let cursor = tails.length > 0 ? tails[tails.length - 1] : -1;
405
- while (cursor !== -1) {
406
- result.push(cursor);
407
- cursor = predecessor[cursor];
408
- }
409
- return result.reverse();
410
- }
411
-
412
45
  // packages/dom/src/component.ts
413
46
  import {
414
- signal as signal2,
415
- createOwner as createOwner2,
47
+ signal,
48
+ createOwner,
416
49
  devEnterSetup,
417
50
  devExitSetup,
418
- disposeOwner as disposeOwner2,
419
- getOwner as getOwner2,
51
+ disposeOwner,
52
+ getOwner,
420
53
  handleError,
421
- setOwner as setOwner2,
54
+ setOwner,
422
55
  untrack
423
56
  } from "@firsthandjs/core";
424
57
  var COMPONENT = /* @__PURE__ */ Symbol.for("firsthand.component");
@@ -474,18 +107,18 @@ function createComponent(target, props) {
474
107
  if (target.tag !== void 0) {
475
108
  return createHost(target, props);
476
109
  }
477
- const owner = createOwner2(getOwner2());
110
+ const owner = createOwner(getOwner());
478
111
  devComponent(owner, target.name);
479
- const previous = setOwner2(owner);
112
+ const previous = setOwner(owner);
480
113
  try {
481
114
  devEnterSetup(target.id);
482
115
  const result = untrack(() => target.setup(props));
483
116
  devExitSetup();
484
- setOwner2(previous);
117
+ setOwner(previous);
485
118
  return result;
486
119
  } catch (error) {
487
120
  devExitSetup();
488
- setOwner2(previous);
121
+ setOwner(previous);
489
122
  handleError(error, owner);
490
123
  return null;
491
124
  }
@@ -533,13 +166,13 @@ function elementClass(target) {
533
166
  if (!this.$standalone || this.$owner === null) {
534
167
  return;
535
168
  }
536
- disposeOwner2(this.$owner);
169
+ disposeOwner(this.$owner);
537
170
  this.$owner = null;
538
171
  }
539
172
  attributeChangedCallback(name, _old, value) {
540
- const cell2 = this.$attrs?.[name];
541
- if (cell2 !== void 0) {
542
- cell2.value = codecs[name]?.(value);
173
+ const cell = this.$attrs?.[name];
174
+ if (cell !== void 0) {
175
+ cell.value = codecs[name]?.(value);
543
176
  }
544
177
  }
545
178
  };
@@ -549,21 +182,21 @@ function attributeProps(element, codecs, cells) {
549
182
  const props = {};
550
183
  if (codecs !== void 0) {
551
184
  for (const name in codecs) {
552
- const cell2 = signal2(codecs[name](element.getAttribute(name)));
553
- cells[name] = cell2;
185
+ const cell = signal(codecs[name](element.getAttribute(name)));
186
+ cells[name] = cell;
554
187
  Object.defineProperty(props, name, {
555
188
  enumerable: true,
556
- get: () => cell2.value
189
+ get: () => cell.value
557
190
  });
558
191
  }
559
192
  }
560
193
  return Object.freeze(props);
561
194
  }
562
195
  function mountHost(element, target, shadow) {
563
- const owner = createOwner2(getOwner2());
196
+ const owner = createOwner(getOwner());
564
197
  devComponent(owner, target.name);
565
198
  element.$owner = owner;
566
- const previous = setOwner2(owner);
199
+ const previous = setOwner(owner);
567
200
  try {
568
201
  devEnterSetup(target.id);
569
202
  const root = shadow ? element.attachShadow({ mode: "open" }) : element;
@@ -574,7 +207,7 @@ function mountHost(element, target, shadow) {
574
207
  devExitSetup();
575
208
  handleError(error, owner);
576
209
  } finally {
577
- setOwner2(previous);
210
+ setOwner(previous);
578
211
  }
579
212
  }
580
213
  function createHost(target, props) {
@@ -587,24 +220,52 @@ function createHost(target, props) {
587
220
  // packages/dom/src/list.ts
588
221
  import {
589
222
  batch,
590
- createOwner as createOwner3,
591
- disposeOwner as disposeOwner3,
592
- getOwner as getOwner3,
593
- setOwner as setOwner3,
594
- signal as signal3,
223
+ createOwner as createOwner2,
224
+ disposeOwner as disposeOwner2,
225
+ getOwner as getOwner2,
226
+ setOwner as setOwner2,
227
+ signal as signal2,
595
228
  untrack as untrack2
596
229
  } from "@firsthandjs/core";
597
230
  function list(each, keyOf, render) {
598
- const host = createOwner3(getOwner3());
231
+ const host = createOwner2(getOwner2());
599
232
  let rows = /* @__PURE__ */ new Map();
233
+ let placed;
234
+ let views = false;
600
235
  return () => {
601
236
  const items = each();
237
+ if (views && placed !== void 0) {
238
+ placed.length = 0;
239
+ for (const row of rows.values()) {
240
+ for (const node of row.pieces === void 0 ? row.nodes : nodesOf(row)) {
241
+ placed.push(node);
242
+ }
243
+ }
244
+ }
602
245
  const next = /* @__PURE__ */ new Map();
603
246
  const nodes = [];
604
- batch(() => {
247
+ nodes[FLAT] = true;
248
+ let pending;
249
+ untrack2(() => {
250
+ batch(() => {
251
+ buildRows();
252
+ });
253
+ });
254
+ rows = next;
255
+ placed = nodes;
256
+ if (pending !== void 0) {
257
+ const made = pending;
258
+ nodes[PENDING] = (parent) => {
259
+ for (const row of made) {
260
+ bindRow(row, parent);
261
+ }
262
+ };
263
+ }
264
+ return nodes;
265
+ function buildRows() {
605
266
  for (let i = 0; i < items.length; i++) {
606
267
  const item = items[i];
607
- const key2 = untrack2(() => keyOf(item, i));
268
+ const key2 = keyOf(item, i);
608
269
  if (next.has(key2)) {
609
270
  devWarn(
610
271
  `Duplicate list key ${String(key2)}. Keys must be unique, or rows will be dropped.`
@@ -614,56 +275,108 @@ function list(each, keyOf, render) {
614
275
  let row = rows.get(key2);
615
276
  if (row === void 0) {
616
277
  row = createRow(host, item, i, render);
278
+ if (row.pieces !== void 0) {
279
+ views = true;
280
+ if (!bindRow(row, null)) {
281
+ pending = pending === void 0 ? [row] : [...pending, row];
282
+ }
283
+ }
617
284
  } else {
618
285
  rows.delete(key2);
619
286
  row.item.value = item;
620
287
  row.index.value = i;
621
288
  }
622
289
  next.set(key2, row);
623
- for (let n = 0; n < row.nodes.length; n++) {
624
- nodes.push(row.nodes[n]);
290
+ const found = row.pieces === void 0 ? row.nodes : nodesOf(row);
291
+ for (let n = 0; n < found.length; n++) {
292
+ nodes.push(found[n]);
625
293
  }
626
294
  }
627
- for (const row of rows.values()) {
628
- disposeOwner3(row.owner);
629
- }
630
- });
631
- rows = next;
632
- return nodes;
295
+ discard(() => {
296
+ for (const row of rows.values()) {
297
+ disposeOwner2(row.owner);
298
+ }
299
+ });
300
+ }
633
301
  };
634
302
  }
303
+ function isNode(piece) {
304
+ return typeof piece.nodeType === "number";
305
+ }
306
+ function bindRow(row, into) {
307
+ const pieces = row.pieces;
308
+ for (const piece of pieces) {
309
+ if (isNode(piece)) {
310
+ continue;
311
+ }
312
+ const parent = into ?? hydration.current?.place(piece.child.anchor) ?? null;
313
+ if (parent === null) {
314
+ return false;
315
+ }
316
+ bindPart(piece.child, parent, (found) => {
317
+ piece.nodes = found;
318
+ });
319
+ }
320
+ return true;
321
+ }
322
+ function nodesOf(row) {
323
+ const nodes = [];
324
+ for (const piece of row.pieces) {
325
+ if (isNode(piece)) {
326
+ nodes.push(piece);
327
+ } else {
328
+ for (const node of piece.nodes) {
329
+ nodes.push(node);
330
+ }
331
+ }
332
+ }
333
+ return nodes;
334
+ }
635
335
  function createRow(host, item, index, render) {
636
- const owner = createOwner3(host);
637
- const itemCell = signal3(item);
638
- const indexCell = signal3(index);
639
- const previous = setOwner3(owner);
640
- let result;
336
+ const owner = createOwner2(host);
337
+ const itemCell = signal2(item);
338
+ const indexCell = signal2(index);
339
+ const previous = setOwner2(owner);
340
+ const pieces = [];
341
+ let parts;
641
342
  try {
642
- result = untrack2(() => render(itemCell, indexCell));
643
- setOwner3(previous);
644
- } catch (error) {
645
- setOwner3(previous);
646
- throw error;
343
+ parts = collect(
344
+ untrack2(() => render(itemCell, indexCell)),
345
+ pieces
346
+ );
347
+ } finally {
348
+ setOwner2(previous);
647
349
  }
648
- const nodes = [];
649
- collect(result, nodes);
650
- return { owner, item: itemCell, index: indexCell, nodes };
350
+ return parts ? { owner, item: itemCell, index: indexCell, nodes: [], pieces } : { owner, item: itemCell, index: indexCell, nodes: pieces };
651
351
  }
652
352
  function collect(value, out) {
653
353
  if (value == null || typeof value === "boolean") {
654
- return;
354
+ return false;
655
355
  }
656
356
  if (Array.isArray(value)) {
357
+ let parts = false;
657
358
  for (let i = 0; i < value.length; i++) {
658
- collect(value[i], out);
359
+ parts = collect(value[i], out) || parts;
659
360
  }
660
- return;
361
+ return parts;
661
362
  }
662
- if (typeof value === "object" && typeof value.nodeType === "number") {
663
- out.push(value);
664
- return;
363
+ if (typeof value === "function") {
364
+ const child = part(value);
365
+ out.push({ child, nodes: [child.anchor] });
366
+ return true;
367
+ }
368
+ if (typeof value === "object") {
369
+ if (isDynamicChild(value)) {
370
+ out.push({ child: value, nodes: [value.anchor] });
371
+ return true;
372
+ }
373
+ if (typeof value.nodeType === "number") {
374
+ out.push(value);
375
+ return false;
376
+ }
665
377
  }
666
378
  out.push(document.createTextNode(String(value)));
379
+ return false;
667
380
  }
668
381
 
669
382
  // packages/dom/src/events.ts
@@ -835,7 +548,9 @@ function hyphenate(name) {
835
548
  // packages/dom/src/props.ts
836
549
  function applyProp(node, name, value) {
837
550
  if (name === "class" || name === "className") {
838
- if (value !== null && typeof value === "object") {
551
+ if (Array.isArray(value)) {
552
+ setClass(node, value.filter(Boolean).join(" "));
553
+ } else if (value !== null && typeof value === "object") {
839
554
  setClassList(node, value, remember(node, "class", value));
840
555
  } else {
841
556
  setClass(node, value);
@@ -937,19 +652,6 @@ function remember(node, slot, value) {
937
652
  export {
938
653
  setComponentAdapter,
939
654
  FirsthandComponentError,
940
- label,
941
- part,
942
- store,
943
- wrote,
944
- site,
945
- open,
946
- close,
947
- ran,
948
- cell,
949
- writeChild,
950
- insert,
951
- applyChild,
952
- reconcile,
953
655
  COMPONENT,
954
656
  VIEW,
955
657
  setElementPrefix,