defuss-morph 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/all.js CHANGED
@@ -16,51 +16,60 @@ var __export = (target, all) => {
16
16
  // src/index.ts
17
17
  var exports_src = {};
18
18
  __export(exports_src, {
19
- updateDomWithVdom: () => updateDomWithVdom,
20
- resolveGlobals: () => resolveGlobals,
21
- replaceDomWithVdom: () => replaceDomWithVdom,
22
- renderMarkup: () => renderMarkup,
23
- removeDelegatedEventByKey: () => removeDelegatedEventByKey,
24
- removeDelegatedEvent: () => removeDelegatedEvent,
25
- registerDelegatedEvent: () => registerDelegatedEvent,
26
- queueCallback: () => queueCallback,
27
- performTransition: () => performTransition,
28
- parseEventPropName: () => parseEventPropName,
29
- parseDOM: () => parseDOM,
30
- onClearDelegatedEvents: () => onClearDelegatedEvents,
31
- observeUnmount: () => observeUnmount,
32
- nsMap: () => nsMap,
33
- morph: () => morph2,
34
- isSVG: () => isSVG,
35
- isMarkup: () => isMarkup,
36
- isHTML: () => isHTML,
37
- isCommentVNode: () => isCommentVNode,
38
- htmlStringToVNodes: () => htmlStringToVNodes,
39
- handleLifecycleEventsForOnMount: () => handleLifecycleEventsForOnMount,
40
- getTransitionStyles: () => getTransitionStyles,
41
- getRenderer: () => getRenderer,
42
- getRegisteredEventTypes: () => getRegisteredEventTypes,
43
- getRegisteredEventKeys: () => getRegisteredEventKeys,
44
- getMimeType: () => getMimeType,
45
- domNodeToVNode: () => domNodeToVNode,
46
- clearDelegatedEventsDeep: () => clearDelegatedEventsDeep,
47
- clearDelegatedEvents: () => clearDelegatedEvents,
48
- areDomNodesEqual: () => areDomNodesEqual,
49
- applyStyles: () => applyStyles,
50
- XMLNS_ATTRIBUTE_NAME: () => XMLNS_ATTRIBUTE_NAME,
51
- XLINK_ATTRIBUTE_NAME: () => XLINK_ATTRIBUTE_NAME,
52
- REF_ATTRIBUTE_NAME: () => REF_ATTRIBUTE_NAME,
53
- FROM_DOM_MARKER: () => FROM_DOM_MARKER,
54
- DEFAULT_TRANSITION_CONFIG: () => DEFAULT_TRANSITION_CONFIG,
55
- DANGEROUSLY_SET_INNER_HTML_ATTRIBUTE: () => DANGEROUSLY_SET_INNER_HTML_ATTRIBUTE,
56
- COMMENT_TYPE: () => COMMENT_TYPE,
19
+ CAPTURE_ONLY_EVENTS: () => CAPTURE_ONLY_EVENTS,
57
20
  CLASS_ATTRIBUTE_NAME: () => CLASS_ATTRIBUTE_NAME,
58
- CAPTURE_ONLY_EVENTS: () => CAPTURE_ONLY_EVENTS
21
+ COMMENT_TYPE: () => COMMENT_TYPE,
22
+ DANGEROUSLY_SET_INNER_HTML_ATTRIBUTE: () => DANGEROUSLY_SET_INNER_HTML_ATTRIBUTE,
23
+ DEFAULT_TRANSITION_CONFIG: () => DEFAULT_TRANSITION_CONFIG,
24
+ FROM_DOM_MARKER: () => FROM_DOM_MARKER,
25
+ Fragment: () => Fragment,
26
+ REF_ATTRIBUTE_NAME: () => REF_ATTRIBUTE_NAME,
27
+ Reactive: () => Reactive,
28
+ XLINK_ATTRIBUTE_NAME: () => XLINK_ATTRIBUTE_NAME,
29
+ XMLNS_ATTRIBUTE_NAME: () => XMLNS_ATTRIBUTE_NAME,
30
+ applyStyles: () => applyStyles,
31
+ areDomNodesEqual: () => areDomNodesEqual,
32
+ clearDelegatedEvents: () => clearDelegatedEvents,
33
+ clearDelegatedEventsDeep: () => clearDelegatedEventsDeep,
34
+ createRef: () => createRef,
35
+ domNodeToVNode: () => domNodeToVNode,
36
+ getMimeType: () => getMimeType,
37
+ getRegisteredEventKeys: () => getRegisteredEventKeys,
38
+ getRegisteredEventTypes: () => getRegisteredEventTypes,
39
+ getRenderer: () => getRenderer,
40
+ getTransitionStyles: () => getTransitionStyles,
41
+ h: () => h,
42
+ handleLifecycleEventsForOnMount: () => handleLifecycleEventsForOnMount,
43
+ htmlStringToVNodes: () => htmlStringToVNodes,
44
+ installReactive: () => installReactive,
45
+ isCommentVNode: () => isCommentVNode,
46
+ isHTML: () => isHTML,
47
+ isMarkup: () => isMarkup,
48
+ isRef: () => isRef,
49
+ isSVG: () => isSVG,
50
+ jsx: () => jsx,
51
+ jsxDEV: () => jsxDEV,
52
+ jsxs: () => jsxs,
53
+ morph: () => morph,
54
+ nsMap: () => nsMap,
55
+ observeUnmount: () => observeUnmount,
56
+ onClearDelegatedEvents: () => onClearDelegatedEvents,
57
+ parseDOM: () => parseDOM,
58
+ parseEventPropName: () => parseEventPropName,
59
+ performTransition: () => performTransition,
60
+ queueCallback: () => queueCallback,
61
+ reactive: () => reactive,
62
+ registerDelegatedEvent: () => registerDelegatedEvent,
63
+ removeDelegatedEvent: () => removeDelegatedEvent,
64
+ removeDelegatedEventByKey: () => removeDelegatedEventByKey,
65
+ renderMarkup: () => renderMarkup,
66
+ replaceDomWithVdom: () => replaceDomWithVdom,
67
+ resolveGlobals: () => resolveGlobals,
68
+ templateContent: () => templateContent,
69
+ updateDomWithVdom: () => updateDomWithVdom
59
70
  });
60
-
61
71
  // src/queue.ts
62
72
  var queueCallback = (cb) => (...args) => queueMicrotask(() => cb(...args));
63
-
64
73
  // src/delegated-events.ts
65
74
  var CAPTURE_ONLY_EVENTS = new Set([
66
75
  "focus",
@@ -240,7 +249,6 @@ var removeDelegatedEventByKey = (element, eventType, phase) => {
240
249
  if (isEntryEmpty(entry))
241
250
  byEvent.delete(eventType);
242
251
  };
243
-
244
252
  // src/html.ts
245
253
  var FROM_DOM_MARKER = Symbol("defuss-morph.from-dom");
246
254
  var COMMENT_TYPE = "#comment";
@@ -299,9 +307,10 @@ function getMimeType(input, Parser) {
299
307
  }
300
308
  return "text/html";
301
309
  }
310
+ var templateContent = (element) => element.localName === "template" ? element.content : undefined;
302
311
  function domNodeToVNode(node) {
303
312
  if (node.nodeType === 3) {
304
- return node.textContent || "";
313
+ return node.nodeValue || "";
305
314
  }
306
315
  if (node.nodeType === 1) {
307
316
  const element = node;
@@ -311,31 +320,38 @@ function domNodeToVNode(node) {
311
320
  attributes[attr.name] = domAttributeToVNodeValue(attr);
312
321
  }
313
322
  const children = [];
314
- for (let i = 0;i < element.childNodes.length; i++) {
315
- const childVNode = domNodeToVNode(element.childNodes[i]);
316
- children.push(childVNode);
323
+ for (let child = (templateContent(element) ?? element).firstChild;child; child = child.nextSibling) {
324
+ children.push(domNodeToVNode(child));
317
325
  }
318
- return {
319
- type: element.tagName.toLowerCase(),
320
- attributes: { ...attributes, [FROM_DOM_MARKER]: true },
321
- children
322
- };
326
+ attributes[FROM_DOM_MARKER] = true;
327
+ return { type: element.localName, attributes, children };
323
328
  }
324
329
  if (node.nodeType === 8) {
325
330
  return { type: COMMENT_TYPE, value: node.nodeValue ?? "" };
326
331
  }
327
332
  return "";
328
333
  }
334
+ var inertDocuments = new WeakMap;
329
335
  var DOCUMENT_START = /^\s*(?:<!--[\s\S]*?-->\s*)*<(?:!doctype|html|head|body)[\s>/]/i;
330
336
  function htmlStringToVNodes(html, Parser) {
331
- const parser = new Parser;
332
- const doc = parser.parseFromString(DOCUMENT_START.test(html) ? html : `<body>${html}`, "text/html");
337
+ if (DOCUMENT_START.test(html)) {
338
+ return toVNodes(new Parser().parseFromString(html, "text/html").body.childNodes);
339
+ }
340
+ let inert = inertDocuments.get(Parser);
341
+ if (!inert) {
342
+ inert = new Parser().parseFromString("", "text/html");
343
+ inertDocuments.set(Parser, inert);
344
+ }
345
+ const template = inert.createElement("template");
346
+ template.innerHTML = html;
347
+ return toVNodes(template.content.childNodes);
348
+ }
349
+ function toVNodes(nodes) {
333
350
  const vNodes = [];
334
- for (let i = 0;i < doc.body.childNodes.length; i++) {
335
- const vnode = domNodeToVNode(doc.body.childNodes[i]);
336
- if (vnode !== "") {
351
+ for (let i = 0;i < nodes.length; i++) {
352
+ const vnode = domNodeToVNode(nodes[i]);
353
+ if (vnode !== "")
337
354
  vNodes.push(vnode);
338
- }
339
355
  }
340
356
  return vNodes;
341
357
  }
@@ -388,13 +404,18 @@ var observeUnmount = (domNode, onUnmount) => {
388
404
  });
389
405
  observer.observe(parentNode, { childList: true });
390
406
  };
407
+ var unmountObserved = new WeakSet;
391
408
  var handleLifecycleEventsForOnMount = (newEl) => {
392
409
  if (typeof newEl?.$onMount === "function") {
393
410
  newEl.$onMount(newEl);
394
411
  newEl.$onMount = null;
395
412
  }
396
- if (typeof newEl?.$onUnmount === "function") {
397
- observeUnmount(newEl, newEl.$onUnmount);
413
+ if (typeof newEl?.$onUnmount === "function" && !unmountObserved.has(newEl)) {
414
+ unmountObserved.add(newEl);
415
+ observeUnmount(newEl, () => {
416
+ unmountObserved.delete(newEl);
417
+ newEl.$onUnmount?.(newEl);
418
+ });
398
419
  }
399
420
  };
400
421
  var getRenderer = (document2) => {
@@ -405,6 +426,9 @@ var getRenderer = (document2) => {
405
426
  if (Array.isArray(virtualNode)) {
406
427
  return renderer.createChildElements(virtualNode, parentDomElement);
407
428
  }
429
+ if (typeof virtualNode === "string" || typeof virtualNode === "number") {
430
+ return renderer.createTextNode(String(virtualNode), parentDomElement);
431
+ }
408
432
  if (typeof virtualNode !== "undefined") {
409
433
  return renderer.createElement(virtualNode, parentDomElement);
410
434
  }
@@ -417,40 +441,31 @@ var getRenderer = (document2) => {
417
441
  parentDomElement?.appendChild(comment);
418
442
  return comment;
419
443
  }
420
- try {
421
- if (typeof virtualNode === "function" && virtualNode.constructor.name === "AsyncFunction") {
444
+ if (typeof virtualNode === "function" && virtualNode.constructor.name === "AsyncFunction") {
445
+ newEl = document2.createElement("div");
446
+ } else if (typeof virtualNode === "object" && virtualNode !== null && "type" in virtualNode) {
447
+ const vNode = virtualNode;
448
+ if (typeof vNode.type === "function") {
422
449
  newEl = document2.createElement("div");
423
- } else if (typeof virtualNode === "object" && virtualNode !== null && "type" in virtualNode) {
424
- const vNode = virtualNode;
425
- if (typeof vNode.type === "function") {
426
- newEl = document2.createElement("div");
427
- newEl.innerText = `FATAL ERROR: ${vNode.type._error}`;
428
- } else if (typeof vNode.type === "string" && vNode.type.toUpperCase() === "SVG" || parentDomElement && renderer.hasSvgNamespace(parentDomElement, typeof vNode.type === "string" ? vNode.type.toUpperCase() : "")) {
429
- newEl = document2.createElementNS(nsMap.svg, vNode.type);
430
- } else {
431
- newEl = document2.createElement(vNode.type);
432
- }
433
- if (vNode.attributes) {
434
- renderer.setAttributes(vNode, newEl);
435
- if (vNode.attributes.dangerouslySetInnerHTML) {
436
- newEl.innerHTML = vNode.attributes.dangerouslySetInnerHTML.__html;
437
- }
438
- }
439
- if (vNode.children && !vNode.attributes?.dangerouslySetInnerHTML) {
440
- renderer.createChildElements(vNode.children, newEl);
441
- }
450
+ newEl.innerText = `FATAL ERROR: ${vNode.type._error}`;
451
+ } else if (typeof vNode.type === "string" && vNode.type.toUpperCase() === "SVG" || parentDomElement && renderer.hasSvgNamespace(parentDomElement, typeof vNode.type === "string" ? vNode.type.toUpperCase() : "")) {
452
+ newEl = document2.createElementNS(nsMap.svg, vNode.type);
442
453
  } else {
443
- if (typeof virtualNode === "string" || typeof virtualNode === "number") {
444
- newEl = document2.createElement(String(virtualNode));
454
+ newEl = document2.createElement(vNode.type);
455
+ }
456
+ if (vNode.attributes) {
457
+ renderer.setAttributes(vNode, newEl);
458
+ if (vNode.attributes.dangerouslySetInnerHTML) {
459
+ newEl.innerHTML = vNode.attributes.dangerouslySetInnerHTML.__html;
445
460
  }
446
461
  }
447
- if (newEl && parentDomElement) {
448
- parentDomElement.appendChild(newEl);
449
- handleLifecycleEventsForOnMount(newEl);
462
+ if (vNode.children && !vNode.attributes?.dangerouslySetInnerHTML) {
463
+ renderer.createChildElements(vNode.children, templateContent(newEl) ?? newEl);
450
464
  }
451
- } catch (e) {
452
- console.error("Fatal error! Error happend while rendering the VDOM!", e, virtualNode);
453
- throw e;
465
+ }
466
+ if (newEl && parentDomElement) {
467
+ parentDomElement.appendChild(newEl);
468
+ handleLifecycleEventsForOnMount(newEl);
454
469
  }
455
470
  return newEl;
456
471
  },
@@ -486,19 +501,8 @@ var getRenderer = (document2) => {
486
501
  return;
487
502
  }
488
503
  if (name === REF_ATTRIBUTE_NAME && typeof value !== "function") {
489
- const ref = value;
490
- ref.current = domElement;
504
+ value.current = domElement;
491
505
  domElement._defussRef = value;
492
- domElement.$onUnmount = queueCallback(() => {});
493
- if (domElement.parentNode) {
494
- observeUnmount(domElement, domElement.$onUnmount);
495
- } else {
496
- queueMicrotask(() => {
497
- if (domElement.parentNode) {
498
- observeUnmount(domElement, domElement.$onUnmount);
499
- }
500
- });
501
- }
502
506
  return;
503
507
  }
504
508
  const parsed = parseEventPropName(name);
@@ -509,15 +513,7 @@ var getRenderer = (document2) => {
509
513
  return;
510
514
  }
511
515
  if (eventType === "unmount") {
512
- if (domElement.$onUnmount) {
513
- const existingUnmount = domElement.$onUnmount;
514
- domElement.$onUnmount = () => {
515
- existingUnmount();
516
- value();
517
- };
518
- } else {
519
- domElement.$onUnmount = queueCallback(value);
520
- }
516
+ domElement.$onUnmount = queueCallback(value);
521
517
  return;
522
518
  }
523
519
  registerDelegatedEvent(domElement, eventType, value, { capture });
@@ -571,6 +567,35 @@ var getRenderer = (document2) => {
571
567
  };
572
568
  return renderer;
573
569
  };
570
+ // src/lis.ts
571
+ var longestIncreasingRun = (values) => {
572
+ const n = values.length;
573
+ const marked = new Uint8Array(n);
574
+ const tails = new Int32Array(n);
575
+ const previous = new Int32Array(n);
576
+ let length = 0;
577
+ for (let i = 0;i < n; i++) {
578
+ const value = values[i];
579
+ if (value < 0)
580
+ continue;
581
+ let low = 0;
582
+ let high = length;
583
+ while (low < high) {
584
+ const mid = low + high >> 1;
585
+ if (values[tails[mid]] < value)
586
+ low = mid + 1;
587
+ else
588
+ high = mid;
589
+ }
590
+ previous[i] = low > 0 ? tails[low - 1] : -1;
591
+ tails[low] = i;
592
+ if (low === length)
593
+ length++;
594
+ }
595
+ for (let i = length > 0 ? tails[length - 1] : -1;i >= 0; i = previous[i])
596
+ marked[i] = 1;
597
+ return marked;
598
+ };
574
599
 
575
600
  // src/morph.ts
576
601
  var areDomNodesEqual = (oldNode, newNode) => {
@@ -927,71 +952,140 @@ function morphDiff(targetRoot, patchItems, globals) {
927
952
  }
928
953
  }
929
954
  }
955
+ function moveNode(parent, node, anchor) {
956
+ if (typeof parent.moveBefore === "function") {
957
+ try {
958
+ parent.moveBefore(node, anchor);
959
+ return;
960
+ } catch {}
961
+ }
962
+ const doc = node.ownerDocument;
963
+ const active = doc?.activeElement;
964
+ const holdsFocus = !!active && active !== doc.body && node.contains(active);
965
+ let selection;
966
+ if (holdsFocus) {
967
+ try {
968
+ if (active.selectionStart !== null && active.selectionStart !== undefined) {
969
+ selection = [active.selectionStart, active.selectionEnd ?? active.selectionStart, active.selectionDirection ?? "none"];
970
+ }
971
+ } catch {
972
+ selection = undefined;
973
+ }
974
+ }
975
+ parent.insertBefore(node, anchor);
976
+ if (holdsFocus && active.isConnected && doc.activeElement !== active) {
977
+ active.focus({ preventScroll: true });
978
+ if (selection)
979
+ active.setSelectionRange(...selection);
980
+ }
981
+ }
982
+ function patchByPosition(targetRoot, nextChildren, globals) {
983
+ const live = targetRoot.childNodes;
984
+ if (live.length !== nextChildren.length)
985
+ return false;
986
+ for (let c = 0;c < nextChildren.length; c++) {
987
+ const node = live[c];
988
+ const child = nextChildren[c];
989
+ if (getVNodeMatchKey(child) !== null || !areNodeAndChildMatching(node, child))
990
+ return false;
991
+ if (node.nodeType === 1 && getDomMatchKeys(node).length > 0)
992
+ return false;
993
+ }
994
+ for (let c = 0;c < nextChildren.length; c++)
995
+ morphNode(live[c], nextChildren[c], globals);
996
+ return true;
997
+ }
930
998
  function morphDomDirect(parentElement, newVDOM, globals, mode = "replace") {
931
999
  const el = parentElement;
932
1000
  const isCustomElement = el.tagName.includes("-");
933
- const targetRoot = el.shadowRoot && !isCustomElement ? el.shadowRoot : parentElement;
1001
+ const targetRoot = templateContent(parentElement) ?? (el.shadowRoot && !isCustomElement ? el.shadowRoot : parentElement);
934
1002
  const nextChildren = normalizeChildren(newVDOM, mode === "diff");
935
1003
  if (mode === "diff") {
936
1004
  morphDiff(targetRoot, nextChildren, globals);
937
1005
  return;
938
1006
  }
1007
+ if (patchByPosition(targetRoot, nextChildren, globals))
1008
+ return;
939
1009
  const existing = Array.from(targetRoot.childNodes);
940
1010
  const keyedPool = new Map;
941
- const nodeKeys = new WeakMap;
1011
+ const nodeKeys = [];
942
1012
  const unkeyedPool = [];
943
- for (const node of existing) {
944
- const keys = getDomMatchKeys(node);
1013
+ for (let i = 0;i < existing.length; i++) {
1014
+ const keys = getDomMatchKeys(existing[i]);
945
1015
  if (keys.length > 0) {
946
- nodeKeys.set(node, keys);
1016
+ nodeKeys[i] = keys;
947
1017
  let addedToKeyedPool = false;
948
1018
  for (const k of keys) {
949
1019
  if (!keyedPool.has(k)) {
950
- keyedPool.set(k, node);
1020
+ keyedPool.set(k, i);
951
1021
  addedToKeyedPool = true;
952
1022
  }
953
1023
  }
954
1024
  if (!addedToKeyedPool) {
955
- unkeyedPool.push(node);
1025
+ unkeyedPool.push(i);
956
1026
  }
957
1027
  } else {
958
- unkeyedPool.push(node);
1028
+ unkeyedPool.push(i);
959
1029
  }
960
1030
  }
961
- const consumeKeyedNode = (node) => {
962
- const keys = nodeKeys.get(node) ?? [];
963
- for (const k of keys)
964
- keyedPool.delete(k);
1031
+ const consumeKeyedNode = (i) => {
1032
+ for (const k of nodeKeys[i] ?? []) {
1033
+ if (keyedPool.get(k) === i)
1034
+ keyedPool.delete(k);
1035
+ }
965
1036
  };
966
1037
  const takeUnkeyedMatch = (child) => {
967
- for (let i = 0;i < unkeyedPool.length; i++) {
968
- const candidate = unkeyedPool[i];
969
- if (areNodeAndChildMatching(candidate, child)) {
970
- unkeyedPool.splice(i, 1);
971
- return candidate;
1038
+ for (let j = 0;j < unkeyedPool.length; j++) {
1039
+ const i = unkeyedPool[j];
1040
+ if (areNodeAndChildMatching(existing[i], child)) {
1041
+ unkeyedPool.splice(j, 1);
1042
+ return i;
972
1043
  }
973
1044
  }
974
- return;
1045
+ return -1;
975
1046
  };
976
- let domIndex = 0;
977
- for (const child of nextChildren) {
1047
+ const count = nextChildren.length;
1048
+ const matched = new Int32Array(count);
1049
+ let inOrder = true;
1050
+ let previous = -1;
1051
+ for (let c = 0;c < count; c++) {
1052
+ const child = nextChildren[c];
978
1053
  const key = getVNodeMatchKey(child);
979
- let match;
1054
+ let i = -1;
980
1055
  if (key) {
981
- match = keyedPool.get(key);
982
- if (match)
983
- consumeKeyedNode(match);
1056
+ const hit = keyedPool.get(key);
1057
+ if (hit !== undefined) {
1058
+ i = hit;
1059
+ consumeKeyedNode(hit);
1060
+ }
984
1061
  } else {
985
- match = takeUnkeyedMatch(child);
1062
+ i = takeUnkeyedMatch(child);
986
1063
  }
987
- const anchor = targetRoot.childNodes[domIndex] ?? null;
988
- if (match) {
989
- if (match !== anchor) {
990
- targetRoot.insertBefore(match, anchor);
991
- }
992
- const morphed = morphNode(match, child, globals);
993
- if (morphed && morphed !== match) {}
994
- domIndex++;
1064
+ matched[c] = i;
1065
+ if (i >= 0) {
1066
+ if (i < previous)
1067
+ inOrder = false;
1068
+ previous = i;
1069
+ }
1070
+ }
1071
+ const stays = inOrder ? null : longestIncreasingRun(matched);
1072
+ const staying = (c) => matched[c] >= 0 && (stays === null || stays[c] === 1);
1073
+ let anchorAt = 0;
1074
+ while (anchorAt < count && !staying(anchorAt))
1075
+ anchorAt++;
1076
+ for (let c = 0;c < count; c++) {
1077
+ if (anchorAt <= c) {
1078
+ anchorAt = c + 1;
1079
+ while (anchorAt < count && !staying(anchorAt))
1080
+ anchorAt++;
1081
+ }
1082
+ const anchor = anchorAt < count ? existing[matched[anchorAt]] : null;
1083
+ const child = nextChildren[c];
1084
+ if (matched[c] >= 0) {
1085
+ const match = existing[matched[c]];
1086
+ if (!staying(c))
1087
+ moveNode(targetRoot, match, anchor);
1088
+ morphNode(match, child, globals);
995
1089
  continue;
996
1090
  }
997
1091
  const created = createDomFromChild(child, globals);
@@ -1001,14 +1095,13 @@ function morphDomDirect(parentElement, newVDOM, globals, mode = "replace") {
1001
1095
  for (const node of nodes) {
1002
1096
  targetRoot.insertBefore(node, anchor);
1003
1097
  handleLifecycleEventsForOnMount(node);
1004
- domIndex++;
1005
1098
  }
1006
1099
  }
1007
1100
  const remaining = new Set;
1008
- for (const node of unkeyedPool)
1009
- remaining.add(node);
1010
- for (const node of keyedPool.values())
1011
- remaining.add(node);
1101
+ for (const i of unkeyedPool)
1102
+ remaining.add(existing[i]);
1103
+ for (const i of keyedPool.values())
1104
+ remaining.add(existing[i]);
1012
1105
  for (const node of remaining) {
1013
1106
  if (node.parentNode === targetRoot) {
1014
1107
  if (node.nodeType === 1) {
@@ -1021,7 +1114,10 @@ function morphDomDirect(parentElement, newVDOM, globals, mode = "replace") {
1021
1114
  function replaceDomWithVdom(parentElement, newVDOM, globals) {
1022
1115
  const resolvedGlobals = resolveGlobals(parentElement, globals);
1023
1116
  while (parentElement.firstChild) {
1024
- parentElement.removeChild(parentElement.firstChild);
1117
+ const child = parentElement.firstChild;
1118
+ if (child.nodeType === 1)
1119
+ clearDelegatedEventsDeep(child);
1120
+ parentElement.removeChild(child);
1025
1121
  }
1026
1122
  const renderer = getRenderer(resolvedGlobals.window.document);
1027
1123
  const newDom = renderer.createElementOrElements(newVDOM);
@@ -1037,7 +1133,6 @@ function replaceDomWithVdom(parentElement, newVDOM, globals) {
1037
1133
  handleLifecycleEventsForOnMount(newDom);
1038
1134
  }
1039
1135
  }
1040
-
1041
1136
  // src/transitions.ts
1042
1137
  var injectShakeKeyframes = (doc) => {
1043
1138
  if (!doc)
@@ -1121,7 +1216,9 @@ var performCrossfade = async (element, updateCallback, duration, easing) => {
1121
1216
  const doc = element.ownerDocument;
1122
1217
  try {
1123
1218
  const rect = element.getBoundingClientRect();
1124
- snapshot.style.cssText = `position:absolute;top:${rect.top}px;left:${rect.left}px;width:${rect.width}px;height:${rect.height}px;opacity:1;transition:opacity ${duration}ms ${easing};z-index:1000;`;
1219
+ const top = rect.top + (doc.defaultView?.scrollY ?? 0);
1220
+ const left = rect.left + (doc.defaultView?.scrollX ?? 0);
1221
+ snapshot.style.cssText = `position:absolute;top:${top}px;left:${left}px;width:${rect.width}px;height:${rect.height}px;opacity:1;transition:opacity ${duration}ms ${easing};z-index:1000;`;
1125
1222
  element.style.opacity = "0";
1126
1223
  element.style.transition = `opacity ${duration}ms ${easing}`;
1127
1224
  doc.body.appendChild(snapshot);
@@ -1181,10 +1278,9 @@ var performTransition = async (element, updateCallback, config = {}) => {
1181
1278
  throw error;
1182
1279
  }
1183
1280
  };
1184
-
1185
- // src/index.ts
1281
+ // src/api.ts
1186
1282
  var inflightTransitions = new WeakMap;
1187
- var morph2 = (el, newContent, options = {}) => {
1283
+ var morph = (el, newContent, options = {}) => {
1188
1284
  const globals = resolveGlobals(el);
1189
1285
  const win = globals.window;
1190
1286
  const mode = options.diff ? "diff" : "replace";
@@ -1195,15 +1291,15 @@ var morph2 = (el, newContent, options = {}) => {
1195
1291
  const transitionTarget = config.target === "self" ? el : el.parentElement;
1196
1292
  if (!transitionTarget) {
1197
1293
  apply(newContent);
1198
- return;
1294
+ return Promise.resolve();
1199
1295
  }
1200
- const slot2 = { content: newContent };
1201
- inflightTransitions.set(el, slot2);
1296
+ const slot = { content: newContent };
1297
+ inflightTransitions.set(el, slot);
1202
1298
  return performTransition(transitionTarget, async () => {
1203
- if (inflightTransitions.get(el) === slot2)
1204
- apply(slot2.content);
1299
+ if (inflightTransitions.get(el) === slot)
1300
+ apply(slot.content);
1205
1301
  }, config).finally(() => {
1206
- if (inflightTransitions.get(el) === slot2)
1302
+ if (inflightTransitions.get(el) === slot)
1207
1303
  inflightTransitions.delete(el);
1208
1304
  });
1209
1305
  }
@@ -1212,7 +1308,151 @@ var morph2 = (el, newContent, options = {}) => {
1212
1308
  slot.content = newContent;
1213
1309
  apply(newContent);
1214
1310
  };
1215
-
1311
+ // src/jsx.ts
1312
+ var toChildren = (children) => children === undefined ? [] : Array.isArray(children) ? children.flat(Infinity) : [children];
1313
+ var isVNode2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && ("type" in value);
1314
+ var Fragment = (props) => toChildren(props?.children);
1315
+ function jsx(type, props, key) {
1316
+ const { children, ...attributes } = props ?? {};
1317
+ const resolvedKey = key ?? attributes.key;
1318
+ if (resolvedKey !== undefined)
1319
+ attributes.key = resolvedKey;
1320
+ if (typeof type === "function") {
1321
+ const result = type(children === undefined ? attributes : { ...attributes, children });
1322
+ return resolvedKey !== undefined && isVNode2(result) && result.attributes?.key === undefined ? { ...result, attributes: { ...result.attributes, key: resolvedKey } } : result;
1323
+ }
1324
+ return { type, attributes, children: toChildren(children) };
1325
+ }
1326
+ var jsxs = jsx;
1327
+ var jsxDEV = (type, props, key) => jsx(type, props, key);
1328
+ function h(type, props, ...children) {
1329
+ return jsx(type, children.length === 0 ? props : { ...props, children: children.length === 1 ? children[0] : children });
1330
+ }
1331
+ // src/ref.ts
1332
+ var createRef = (state) => ({
1333
+ current: null,
1334
+ state
1335
+ });
1336
+ var isRef = (value) => typeof value === "object" && value !== null && ("current" in value);
1337
+ // src/reactive.ts
1338
+ var isElement = (value) => typeof value === "object" && value !== null && value.nodeType === 1;
1339
+ var toStores = (store, caller) => {
1340
+ const stores = Array.isArray(store) ? store : [store];
1341
+ stores.forEach((candidate, index) => {
1342
+ if (typeof candidate?.subscribe !== "function") {
1343
+ throw new TypeError(`${caller}: store${Array.isArray(store) ? ` ${index}` : ""} has no subscribe() method`);
1344
+ }
1345
+ });
1346
+ return stores;
1347
+ };
1348
+ var listen = (stores, update) => {
1349
+ const unsubscribes = stores.map((store) => {
1350
+ const result = store.subscribe(() => update());
1351
+ if (typeof result === "function")
1352
+ return result;
1353
+ return () => result?.unsubscribe?.();
1354
+ });
1355
+ return () => {
1356
+ for (const unsubscribe of unsubscribes)
1357
+ unsubscribe();
1358
+ };
1359
+ };
1360
+ var resolveTarget = (target) => {
1361
+ if (isElement(target))
1362
+ return () => [target];
1363
+ if (isRef(target)) {
1364
+ if (!isElement(target.current)) {
1365
+ throw new TypeError("reactive: the ref has no element yet; call reactive() after the element is rendered (in onMount or later)");
1366
+ }
1367
+ return () => isElement(target.current) ? [target.current] : [];
1368
+ }
1369
+ if (target && typeof target.length === "number") {
1370
+ const elements = Array.from(target).filter(isElement);
1371
+ return () => elements;
1372
+ }
1373
+ throw new TypeError("reactive: the target must be an element, a ref or a list of elements");
1374
+ };
1375
+ function reactive(config, target) {
1376
+ const stores = toStores(config?.store, "reactive");
1377
+ if (typeof config.render !== "function")
1378
+ throw new TypeError("reactive: config.render must be a function");
1379
+ const elements = resolveTarget(target);
1380
+ if (config.signal?.aborted || elements().length === 0)
1381
+ return () => {};
1382
+ const update = () => {
1383
+ for (const el of elements())
1384
+ morph(el, config.render());
1385
+ };
1386
+ update();
1387
+ const unsubscribe = listen(stores, update);
1388
+ let disposed = false;
1389
+ const dispose = () => {
1390
+ if (disposed)
1391
+ return;
1392
+ disposed = true;
1393
+ unsubscribe();
1394
+ config.signal?.removeEventListener("abort", dispose);
1395
+ config.cleanup?.();
1396
+ };
1397
+ config.signal?.addEventListener("abort", dispose, { once: true });
1398
+ return dispose;
1399
+ }
1400
+ var mounted = new WeakMap;
1401
+ var toChildren2 = (content) => {
1402
+ if (typeof content === "string") {
1403
+ if (!/[<&]/.test(content))
1404
+ return content === "" ? [] : [content];
1405
+ const Parser = globalThis.DOMParser;
1406
+ if (!Parser) {
1407
+ throw new TypeError("Reactive: rendering an HTML string needs a global DOMParser; return JSX/VNodes, or call reactive()");
1408
+ }
1409
+ return htmlStringToVNodes(content, Parser);
1410
+ }
1411
+ return Array.isArray(content) ? content.flat(Infinity) : [content];
1412
+ };
1413
+ function Reactive(props) {
1414
+ const { store, render, cleanup, tag = "div", onMount, onUnmount, children: _children, ...attributes } = props;
1415
+ const stores = toStores(store, "Reactive");
1416
+ if (typeof render !== "function")
1417
+ throw new TypeError("Reactive: the render prop must be a function");
1418
+ return {
1419
+ type: tag,
1420
+ attributes: {
1421
+ ...attributes,
1422
+ onMount: (el) => {
1423
+ mounted.get(el)?.unsubscribe();
1424
+ mounted.set(el, { unsubscribe: listen(stores, () => morph(el, render())), cleanup });
1425
+ morph(el, render());
1426
+ if (typeof onMount === "function")
1427
+ onMount(el);
1428
+ },
1429
+ onUnmount: (el) => {
1430
+ const entry = mounted.get(el);
1431
+ mounted.delete(el);
1432
+ entry?.unsubscribe();
1433
+ entry?.cleanup?.();
1434
+ if (typeof onUnmount === "function")
1435
+ onUnmount(el);
1436
+ }
1437
+ },
1438
+ children: toChildren2(render())
1439
+ };
1440
+ }
1441
+ var installReactive = (df$) => {
1442
+ const fn = df$?.fn;
1443
+ if (typeof fn !== "object" || fn === null) {
1444
+ throw new TypeError("installReactive: expected defuss-query's df$ (a df$ with a df$.fn prototype)");
1445
+ }
1446
+ Object.defineProperty(fn, "reactive", {
1447
+ configurable: true,
1448
+ writable: true,
1449
+ value: function(config) {
1450
+ reactive(config, this);
1451
+ return this;
1452
+ }
1453
+ });
1454
+ return df$;
1455
+ };
1216
1456
  // src/global.ts
1217
1457
  if (typeof globalThis !== "undefined") {
1218
1458
  const g = globalThis;
@@ -1221,49 +1461,63 @@ if (typeof globalThis !== "undefined") {
1221
1461
  g.df$ = {};
1222
1462
  }
1223
1463
  Object.assign(g.df$, exports_src);
1464
+ const fn = g.df$.fn;
1465
+ if (typeof fn === "object" && fn !== null)
1466
+ installReactive(g.df$);
1224
1467
  }
1225
1468
  export {
1226
- updateDomWithVdom,
1227
- resolveGlobals,
1228
- replaceDomWithVdom,
1229
- renderMarkup,
1230
- removeDelegatedEventByKey,
1231
- removeDelegatedEvent,
1232
- registerDelegatedEvent,
1233
- queueCallback,
1234
- performTransition,
1235
- parseEventPropName,
1236
- parseDOM,
1237
- onClearDelegatedEvents,
1238
- observeUnmount,
1239
- nsMap,
1240
- morph2 as morph,
1241
- isSVG,
1242
- isMarkup,
1243
- isHTML,
1244
- isCommentVNode,
1245
- htmlStringToVNodes,
1246
- handleLifecycleEventsForOnMount,
1247
- getTransitionStyles,
1248
- getRenderer,
1249
- getRegisteredEventTypes,
1250
- getRegisteredEventKeys,
1251
- getMimeType,
1252
- domNodeToVNode,
1253
- clearDelegatedEventsDeep,
1254
- clearDelegatedEvents,
1255
- areDomNodesEqual,
1256
- applyStyles,
1257
- XMLNS_ATTRIBUTE_NAME,
1258
- XLINK_ATTRIBUTE_NAME,
1259
- REF_ATTRIBUTE_NAME,
1260
- FROM_DOM_MARKER,
1261
- DEFAULT_TRANSITION_CONFIG,
1262
- DANGEROUSLY_SET_INNER_HTML_ATTRIBUTE,
1263
- COMMENT_TYPE,
1469
+ CAPTURE_ONLY_EVENTS,
1264
1470
  CLASS_ATTRIBUTE_NAME,
1265
- CAPTURE_ONLY_EVENTS
1471
+ COMMENT_TYPE,
1472
+ DANGEROUSLY_SET_INNER_HTML_ATTRIBUTE,
1473
+ DEFAULT_TRANSITION_CONFIG,
1474
+ FROM_DOM_MARKER,
1475
+ Fragment,
1476
+ REF_ATTRIBUTE_NAME,
1477
+ Reactive,
1478
+ XLINK_ATTRIBUTE_NAME,
1479
+ XMLNS_ATTRIBUTE_NAME,
1480
+ applyStyles,
1481
+ areDomNodesEqual,
1482
+ clearDelegatedEvents,
1483
+ clearDelegatedEventsDeep,
1484
+ createRef,
1485
+ domNodeToVNode,
1486
+ getMimeType,
1487
+ getRegisteredEventKeys,
1488
+ getRegisteredEventTypes,
1489
+ getRenderer,
1490
+ getTransitionStyles,
1491
+ h,
1492
+ handleLifecycleEventsForOnMount,
1493
+ htmlStringToVNodes,
1494
+ installReactive,
1495
+ isCommentVNode,
1496
+ isHTML,
1497
+ isMarkup,
1498
+ isRef,
1499
+ isSVG,
1500
+ jsx,
1501
+ jsxDEV,
1502
+ jsxs,
1503
+ morph,
1504
+ nsMap,
1505
+ observeUnmount,
1506
+ onClearDelegatedEvents,
1507
+ parseDOM,
1508
+ parseEventPropName,
1509
+ performTransition,
1510
+ queueCallback,
1511
+ reactive,
1512
+ registerDelegatedEvent,
1513
+ removeDelegatedEvent,
1514
+ removeDelegatedEventByKey,
1515
+ renderMarkup,
1516
+ replaceDomWithVdom,
1517
+ resolveGlobals,
1518
+ templateContent,
1519
+ updateDomWithVdom
1266
1520
  };
1267
1521
 
1268
- //# debugId=2B212A8922F1193364756E2164756E21
1522
+ //# debugId=18D1BB0152DB217664756E2164756E21
1269
1523
  //# sourceMappingURL=all.js.map