defuss-morph 0.1.1 → 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,48 +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
- observeUnmount: () => observeUnmount,
31
- nsMap: () => nsMap,
32
- morph: () => morph2,
33
- isSVG: () => isSVG,
34
- isMarkup: () => isMarkup,
35
- isHTML: () => isHTML,
36
- htmlStringToVNodes: () => htmlStringToVNodes,
37
- handleLifecycleEventsForOnMount: () => handleLifecycleEventsForOnMount,
38
- getTransitionStyles: () => getTransitionStyles,
39
- getRenderer: () => getRenderer,
40
- getRegisteredEventTypes: () => getRegisteredEventTypes,
41
- getRegisteredEventKeys: () => getRegisteredEventKeys,
42
- getMimeType: () => getMimeType,
43
- domNodeToVNode: () => domNodeToVNode,
44
- clearDelegatedEventsDeep: () => clearDelegatedEventsDeep,
45
- clearDelegatedEvents: () => clearDelegatedEvents,
46
- areDomNodesEqual: () => areDomNodesEqual,
47
- applyStyles: () => applyStyles,
48
- XMLNS_ATTRIBUTE_NAME: () => XMLNS_ATTRIBUTE_NAME,
49
- XLINK_ATTRIBUTE_NAME: () => XLINK_ATTRIBUTE_NAME,
50
- REF_ATTRIBUTE_NAME: () => REF_ATTRIBUTE_NAME,
51
- FROM_DOM_MARKER: () => FROM_DOM_MARKER,
52
- DEFAULT_TRANSITION_CONFIG: () => DEFAULT_TRANSITION_CONFIG,
53
- DANGEROUSLY_SET_INNER_HTML_ATTRIBUTE: () => DANGEROUSLY_SET_INNER_HTML_ATTRIBUTE,
19
+ CAPTURE_ONLY_EVENTS: () => CAPTURE_ONLY_EVENTS,
54
20
  CLASS_ATTRIBUTE_NAME: () => CLASS_ATTRIBUTE_NAME,
55
- 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
56
70
  });
57
-
58
71
  // src/queue.ts
59
72
  var queueCallback = (cb) => (...args) => queueMicrotask(() => cb(...args));
60
-
61
73
  // src/delegated-events.ts
62
74
  var CAPTURE_ONLY_EVENTS = new Set([
63
75
  "focus",
@@ -67,8 +79,7 @@ var CAPTURE_ONLY_EVENTS = new Set([
67
79
  "mouseleave"
68
80
  ]);
69
81
  var elementHandlerMap = new WeakMap;
70
- var bubbleDispatched = new WeakMap;
71
- var captureDispatched = new WeakMap;
82
+ var slotListeners = new WeakMap;
72
83
  var activeDispatches = new WeakMap;
73
84
  var parseEventPropName = (propName) => {
74
85
  if (!propName.startsWith("on"))
@@ -91,146 +102,66 @@ var getOrCreateElementHandlers = (el) => {
91
102
  elementHandlerMap.set(el, created);
92
103
  return created;
93
104
  };
94
- var getEventPath = (event) => {
95
- const composedPath = event.composedPath?.();
96
- if (composedPath && composedPath.length > 0)
97
- return composedPath;
98
- const path = [];
99
- let node = event.target;
100
- while (node) {
101
- path.push(node);
102
- const maybeNode = node;
103
- if (typeof maybeNode === "object" && maybeNode && "parentNode" in maybeNode) {
104
- node = maybeNode.parentNode;
105
- continue;
106
- }
107
- break;
108
- }
109
- const doc = event.target?.ownerDocument;
110
- if (doc && path[path.length - 1] !== doc)
111
- path.push(doc);
112
- const win = doc?.defaultView;
113
- if (win && path[path.length - 1] !== win)
114
- path.push(win);
115
- return path;
105
+ var runSlot = (target, eventType, phase, event) => {
106
+ const handler = elementHandlerMap.get(target)?.get(eventType)?.[phase];
107
+ if (!handler)
108
+ return;
109
+ const dispatchKey = `${eventType}:${phase}`;
110
+ let active = activeDispatches.get(target);
111
+ if (active?.has(dispatchKey))
112
+ return;
113
+ if (!active) {
114
+ active = new Set;
115
+ activeDispatches.set(target, active);
116
+ }
117
+ active.add(dispatchKey);
118
+ try {
119
+ handler.call(target, event);
120
+ } finally {
121
+ active.delete(dispatchKey);
122
+ }
116
123
  };
117
- var createPhaseHandler = (eventType, phase) => {
118
- const dispatched = phase === "capture" ? captureDispatched : bubbleDispatched;
119
- return (event) => {
120
- const path = getEventPath(event).filter((t) => typeof t === "object" && t !== null && t.nodeType === 1);
121
- const ordered = phase === "capture" ? [...path].reverse() : path;
122
- for (const target of ordered) {
123
- const handlersByEvent = elementHandlerMap.get(target);
124
- if (!handlersByEvent)
125
- continue;
126
- const entry = handlersByEvent.get(eventType);
127
- if (!entry)
128
- continue;
129
- let targets = dispatched.get(event);
130
- if (targets?.has(target))
131
- continue;
132
- if (!targets) {
133
- targets = new WeakSet;
134
- dispatched.set(event, targets);
135
- }
136
- targets.add(target);
137
- const dispatchKey = `${eventType}:${phase}`;
138
- let activeSet = activeDispatches.get(target);
139
- if (activeSet?.has(dispatchKey))
140
- continue;
141
- if (!activeSet) {
142
- activeSet = new Set;
143
- activeDispatches.set(target, activeSet);
144
- }
145
- activeSet.add(dispatchKey);
146
- try {
147
- if (phase === "capture") {
148
- if (entry.capture) {
149
- entry.capture.call(target, event);
150
- if (event.cancelBubble)
151
- return;
152
- }
153
- if (entry.captureSet) {
154
- for (const handler of entry.captureSet) {
155
- handler.call(target, event);
156
- if (event.cancelBubble)
157
- return;
158
- }
159
- }
160
- } else {
161
- if (entry.bubble) {
162
- entry.bubble.call(target, event);
163
- if (event.cancelBubble)
164
- return;
165
- }
166
- if (entry.bubbleSet) {
167
- for (const handler of entry.bubbleSet) {
168
- handler.call(target, event);
169
- if (event.cancelBubble)
170
- return;
171
- }
172
- }
173
- }
174
- } finally {
175
- activeSet.delete(dispatchKey);
176
- }
124
+ var setSlot = (target, eventType, phase, entry, handler) => {
125
+ entry[phase] = handler;
126
+ const key = `${eventType}:${phase}`;
127
+ const capture = phase === "capture";
128
+ let byKey = slotListeners.get(target);
129
+ const installed = byKey?.get(key);
130
+ if (handler && !installed) {
131
+ if (!byKey) {
132
+ byKey = new Map;
133
+ slotListeners.set(target, byKey);
177
134
  }
178
- };
179
- };
180
- var installedRootListeners = new WeakMap;
181
- var ensureRootListener = (root, eventType) => {
182
- const installed = installedRootListeners.get(root) ?? new Set;
183
- installedRootListeners.set(root, installed);
184
- const captureKey = `${eventType}:capture`;
185
- if (!installed.has(captureKey)) {
186
- root.addEventListener(eventType, createPhaseHandler(eventType, "capture"), true);
187
- installed.add(captureKey);
188
- }
189
- const bubbleKey = `${eventType}:bubble`;
190
- if (!installed.has(bubbleKey)) {
191
- root.addEventListener(eventType, createPhaseHandler(eventType, "bubble"), false);
192
- installed.add(bubbleKey);
135
+ const trampoline = (event) => runSlot(target, eventType, phase, event);
136
+ byKey.set(key, trampoline);
137
+ target.addEventListener(eventType, trampoline, capture);
138
+ } else if (!handler && installed) {
139
+ byKey.delete(key);
140
+ target.removeEventListener(eventType, installed, capture);
193
141
  }
194
142
  };
195
- var getEventRoot = (element) => {
196
- const root = element.getRootNode();
197
- if (root && root.nodeType === 9) {
198
- return root;
199
- }
200
- if (root && root.nodeType === 11 && "host" in root) {
201
- return root;
202
- }
203
- return null;
143
+ var clearPhase = (target, eventType, entry, phase) => {
144
+ setSlot(target, eventType, phase, entry, undefined);
145
+ const setKey = phase === "capture" ? "captureSet" : "bubbleSet";
146
+ for (const handler of entry[setKey] ?? [])
147
+ target.removeEventListener(eventType, handler, phase === "capture");
148
+ entry[setKey] = undefined;
204
149
  };
205
150
  var registerDelegatedEvent = (element, eventType, handler, options = {}) => {
206
- const root = getEventRoot(element);
207
151
  const capture = options.capture || CAPTURE_ONLY_EVENTS.has(eventType);
208
- if (root) {
209
- ensureRootListener(root, eventType);
210
- } else if (element.ownerDocument) {
211
- ensureRootListener(element.ownerDocument, eventType);
212
- } else {
213
- element.addEventListener(eventType, handler, capture);
214
- }
152
+ const phase = capture ? "capture" : "bubble";
215
153
  const byEvent = getOrCreateElementHandlers(element);
216
154
  const entry = byEvent.get(eventType) ?? {};
217
155
  byEvent.set(eventType, entry);
218
156
  if (options.multi) {
219
- if (capture) {
220
- if (!entry.captureSet)
221
- entry.captureSet = new Set;
222
- entry.captureSet.add(handler);
223
- } else {
224
- if (!entry.bubbleSet)
225
- entry.bubbleSet = new Set;
226
- entry.bubbleSet.add(handler);
157
+ const setKey = capture ? "captureSet" : "bubbleSet";
158
+ const set = entry[setKey] ??= new Set;
159
+ if (!set.has(handler)) {
160
+ set.add(handler);
161
+ element.addEventListener(eventType, handler, capture);
227
162
  }
228
163
  } else {
229
- if (capture) {
230
- entry.capture = handler;
231
- } else {
232
- entry.bubble = handler;
233
- }
164
+ setSlot(element, eventType, phase, entry, handler);
234
165
  }
235
166
  };
236
167
  var isEntryEmpty = (entry) => !entry.capture && !entry.bubble && (!entry.captureSet || entry.captureSet.size === 0) && (!entry.bubbleSet || entry.bubbleSet.size === 0);
@@ -242,35 +173,39 @@ var removeDelegatedEvent = (target, eventType, handler, _options = {}) => {
242
173
  if (!entry)
243
174
  return;
244
175
  if (handler) {
245
- if (entry.captureSet) {
246
- entry.captureSet.delete(handler);
247
- }
248
- if (entry.bubbleSet) {
249
- entry.bubbleSet.delete(handler);
250
- }
251
- if (entry.capture === handler) {
252
- entry.capture = undefined;
176
+ for (const phase of ["capture", "bubble"]) {
177
+ const set = phase === "capture" ? entry.captureSet : entry.bubbleSet;
178
+ if (set?.delete(handler))
179
+ target.removeEventListener(eventType, handler, phase === "capture");
180
+ if (entry[phase] === handler)
181
+ setSlot(target, eventType, phase, entry, undefined);
253
182
  }
254
- if (entry.bubble === handler) {
255
- entry.bubble = undefined;
256
- }
257
- target.removeEventListener(eventType, handler, true);
258
- target.removeEventListener(eventType, handler, false);
259
183
  } else {
260
- entry.capture = undefined;
261
- entry.bubble = undefined;
262
- entry.captureSet = undefined;
263
- entry.bubbleSet = undefined;
184
+ clearPhase(target, eventType, entry, "capture");
185
+ clearPhase(target, eventType, entry, "bubble");
264
186
  }
265
187
  if (isEntryEmpty(entry)) {
266
188
  byEvent.delete(eventType);
267
189
  }
268
190
  };
191
+ var clearHooks = new Set;
192
+ var onClearDelegatedEvents = (hook) => {
193
+ clearHooks.add(hook);
194
+ return () => {
195
+ clearHooks.delete(hook);
196
+ };
197
+ };
269
198
  var clearDelegatedEvents = (target) => {
270
199
  const byEvent = elementHandlerMap.get(target);
271
- if (!byEvent)
272
- return;
273
- byEvent.clear();
200
+ if (byEvent) {
201
+ for (const [eventType, entry] of byEvent) {
202
+ clearPhase(target, eventType, entry, "capture");
203
+ clearPhase(target, eventType, entry, "bubble");
204
+ }
205
+ byEvent.clear();
206
+ }
207
+ for (const hook of clearHooks)
208
+ hook(target);
274
209
  };
275
210
  var clearDelegatedEventsDeep = (root) => {
276
211
  clearDelegatedEvents(root);
@@ -310,16 +245,116 @@ var removeDelegatedEventByKey = (element, eventType, phase) => {
310
245
  const entry = byEvent.get(eventType);
311
246
  if (!entry)
312
247
  return;
313
- if (phase === "capture") {
314
- entry.capture = undefined;
315
- entry.captureSet = undefined;
316
- } else {
317
- entry.bubble = undefined;
318
- entry.bubbleSet = undefined;
319
- }
248
+ clearPhase(element, eventType, entry, phase);
320
249
  if (isEntryEmpty(entry))
321
250
  byEvent.delete(eventType);
322
251
  };
252
+ // src/html.ts
253
+ var FROM_DOM_MARKER = Symbol("defuss-morph.from-dom");
254
+ var COMMENT_TYPE = "#comment";
255
+ var isCommentVNode = (value) => !!value && typeof value === "object" && value.type === COMMENT_TYPE;
256
+ var HTML_BOOLEAN_ATTRIBUTES = new Set([
257
+ "allowfullscreen",
258
+ "async",
259
+ "autofocus",
260
+ "autoplay",
261
+ "checked",
262
+ "controls",
263
+ "default",
264
+ "defer",
265
+ "disabled",
266
+ "formnovalidate",
267
+ "hidden",
268
+ "inert",
269
+ "ismap",
270
+ "itemscope",
271
+ "loop",
272
+ "multiple",
273
+ "muted",
274
+ "nomodule",
275
+ "novalidate",
276
+ "open",
277
+ "playsinline",
278
+ "readonly",
279
+ "required",
280
+ "reversed",
281
+ "selected"
282
+ ]);
283
+ var domAttributeToVNodeValue = (attr) => HTML_BOOLEAN_ATTRIBUTES.has(attr.name.toLowerCase()) ? true : attr.value;
284
+ function parseDOM(input, type, Parser) {
285
+ return new Parser().parseFromString(input, type);
286
+ }
287
+ function isSVG(input, Parser) {
288
+ const doc = parseDOM(input, "image/svg+xml", Parser);
289
+ if (!doc.documentElement)
290
+ return false;
291
+ return doc.documentElement.nodeName.toLowerCase() === "svg";
292
+ }
293
+ function isHTML(input, Parser) {
294
+ const doc = parseDOM(input, "text/html", Parser);
295
+ return doc.documentElement.querySelectorAll("*").length > 2;
296
+ }
297
+ var isMarkup = (input, Parser) => input.indexOf("<") > -1 && input.indexOf(">") > -1 && (isHTML(input, Parser) || isSVG(input, Parser));
298
+ function renderMarkup(markup, Parser, doc) {
299
+ const parsed = doc ? doc : parseDOM(markup, getMimeType(markup, Parser), Parser);
300
+ if (parsed.body)
301
+ return Array.from(parsed.body.childNodes);
302
+ return parsed.documentElement ? [parsed.documentElement] : [];
303
+ }
304
+ function getMimeType(input, Parser) {
305
+ if (isSVG(input, Parser)) {
306
+ return "image/svg+xml";
307
+ }
308
+ return "text/html";
309
+ }
310
+ var templateContent = (element) => element.localName === "template" ? element.content : undefined;
311
+ function domNodeToVNode(node) {
312
+ if (node.nodeType === 3) {
313
+ return node.nodeValue || "";
314
+ }
315
+ if (node.nodeType === 1) {
316
+ const element = node;
317
+ const attributes = {};
318
+ for (let i = 0;i < element.attributes.length; i++) {
319
+ const attr = element.attributes[i];
320
+ attributes[attr.name] = domAttributeToVNodeValue(attr);
321
+ }
322
+ const children = [];
323
+ for (let child = (templateContent(element) ?? element).firstChild;child; child = child.nextSibling) {
324
+ children.push(domNodeToVNode(child));
325
+ }
326
+ attributes[FROM_DOM_MARKER] = true;
327
+ return { type: element.localName, attributes, children };
328
+ }
329
+ if (node.nodeType === 8) {
330
+ return { type: COMMENT_TYPE, value: node.nodeValue ?? "" };
331
+ }
332
+ return "";
333
+ }
334
+ var inertDocuments = new WeakMap;
335
+ var DOCUMENT_START = /^\s*(?:<!--[\s\S]*?-->\s*)*<(?:!doctype|html|head|body)[\s>/]/i;
336
+ function htmlStringToVNodes(html, Parser) {
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) {
350
+ const vNodes = [];
351
+ for (let i = 0;i < nodes.length; i++) {
352
+ const vnode = domNodeToVNode(nodes[i]);
353
+ if (vnode !== "")
354
+ vNodes.push(vnode);
355
+ }
356
+ return vNodes;
357
+ }
323
358
 
324
359
  // src/renderer.ts
325
360
  var CLASS_ATTRIBUTE_NAME = "class";
@@ -369,13 +404,18 @@ var observeUnmount = (domNode, onUnmount) => {
369
404
  });
370
405
  observer.observe(parentNode, { childList: true });
371
406
  };
407
+ var unmountObserved = new WeakSet;
372
408
  var handleLifecycleEventsForOnMount = (newEl) => {
373
409
  if (typeof newEl?.$onMount === "function") {
374
410
  newEl.$onMount(newEl);
375
411
  newEl.$onMount = null;
376
412
  }
377
- if (typeof newEl?.$onUnmount === "function") {
378
- 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
+ });
379
419
  }
380
420
  };
381
421
  var getRenderer = (document2) => {
@@ -386,6 +426,9 @@ var getRenderer = (document2) => {
386
426
  if (Array.isArray(virtualNode)) {
387
427
  return renderer.createChildElements(virtualNode, parentDomElement);
388
428
  }
429
+ if (typeof virtualNode === "string" || typeof virtualNode === "number") {
430
+ return renderer.createTextNode(String(virtualNode), parentDomElement);
431
+ }
389
432
  if (typeof virtualNode !== "undefined") {
390
433
  return renderer.createElement(virtualNode, parentDomElement);
391
434
  }
@@ -393,40 +436,36 @@ var getRenderer = (document2) => {
393
436
  },
394
437
  createElement: (virtualNode, parentDomElement) => {
395
438
  let newEl;
396
- try {
397
- if (typeof virtualNode === "function" && virtualNode.constructor.name === "AsyncFunction") {
439
+ if (isCommentVNode(virtualNode)) {
440
+ const comment = document2.createComment(virtualNode.value ?? "");
441
+ parentDomElement?.appendChild(comment);
442
+ return comment;
443
+ }
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") {
398
449
  newEl = document2.createElement("div");
399
- } else if (typeof virtualNode === "object" && virtualNode !== null && "type" in virtualNode) {
400
- const vNode = virtualNode;
401
- if (typeof vNode.type === "function") {
402
- newEl = document2.createElement("div");
403
- newEl.innerText = `FATAL ERROR: ${vNode.type._error}`;
404
- } else if (typeof vNode.type === "string" && vNode.type.toUpperCase() === "SVG" || parentDomElement && renderer.hasSvgNamespace(parentDomElement, typeof vNode.type === "string" ? vNode.type.toUpperCase() : "")) {
405
- newEl = document2.createElementNS(nsMap.svg, vNode.type);
406
- } else {
407
- newEl = document2.createElement(vNode.type);
408
- }
409
- if (vNode.attributes) {
410
- renderer.setAttributes(vNode, newEl);
411
- if (vNode.attributes.dangerouslySetInnerHTML) {
412
- newEl.innerHTML = vNode.attributes.dangerouslySetInnerHTML.__html;
413
- }
414
- }
415
- if (vNode.children && !vNode.attributes?.dangerouslySetInnerHTML) {
416
- renderer.createChildElements(vNode.children, newEl);
417
- }
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);
418
453
  } else {
419
- if (typeof virtualNode === "string" || typeof virtualNode === "number") {
420
- 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;
421
460
  }
422
461
  }
423
- if (newEl && parentDomElement) {
424
- parentDomElement.appendChild(newEl);
425
- handleLifecycleEventsForOnMount(newEl);
462
+ if (vNode.children && !vNode.attributes?.dangerouslySetInnerHTML) {
463
+ renderer.createChildElements(vNode.children, templateContent(newEl) ?? newEl);
426
464
  }
427
- } catch (e) {
428
- console.error("Fatal error! Error happend while rendering the VDOM!", e, virtualNode);
429
- throw e;
465
+ }
466
+ if (newEl && parentDomElement) {
467
+ parentDomElement.appendChild(newEl);
468
+ handleLifecycleEventsForOnMount(newEl);
430
469
  }
431
470
  return newEl;
432
471
  },
@@ -462,19 +501,8 @@ var getRenderer = (document2) => {
462
501
  return;
463
502
  }
464
503
  if (name === REF_ATTRIBUTE_NAME && typeof value !== "function") {
465
- const ref = value;
466
- ref.current = domElement;
504
+ value.current = domElement;
467
505
  domElement._defussRef = value;
468
- domElement.$onUnmount = queueCallback(() => {});
469
- if (domElement.parentNode) {
470
- observeUnmount(domElement, domElement.$onUnmount);
471
- } else {
472
- queueMicrotask(() => {
473
- if (domElement.parentNode) {
474
- observeUnmount(domElement, domElement.$onUnmount);
475
- }
476
- });
477
- }
478
506
  return;
479
507
  }
480
508
  const parsed = parseEventPropName(name);
@@ -485,15 +513,7 @@ var getRenderer = (document2) => {
485
513
  return;
486
514
  }
487
515
  if (eventType === "unmount") {
488
- if (domElement.$onUnmount) {
489
- const existingUnmount = domElement.$onUnmount;
490
- domElement.$onUnmount = () => {
491
- existingUnmount();
492
- value();
493
- };
494
- } else {
495
- domElement.$onUnmount = queueCallback(value);
496
- }
516
+ domElement.$onUnmount = queueCallback(value);
497
517
  return;
498
518
  }
499
519
  registerDelegatedEvent(domElement, eventType, value, { capture });
@@ -547,99 +567,35 @@ var getRenderer = (document2) => {
547
567
  };
548
568
  return renderer;
549
569
  };
550
-
551
- // src/html.ts
552
- var FROM_DOM_MARKER = Symbol("defuss-morph.from-dom");
553
- var HTML_BOOLEAN_ATTRIBUTES = new Set([
554
- "allowfullscreen",
555
- "async",
556
- "autofocus",
557
- "autoplay",
558
- "checked",
559
- "controls",
560
- "default",
561
- "defer",
562
- "disabled",
563
- "formnovalidate",
564
- "hidden",
565
- "inert",
566
- "ismap",
567
- "itemscope",
568
- "loop",
569
- "multiple",
570
- "muted",
571
- "nomodule",
572
- "novalidate",
573
- "open",
574
- "playsinline",
575
- "readonly",
576
- "required",
577
- "reversed",
578
- "selected"
579
- ]);
580
- var domAttributeToVNodeValue = (attr) => HTML_BOOLEAN_ATTRIBUTES.has(attr.name.toLowerCase()) ? true : attr.value;
581
- function parseDOM(input, type, Parser) {
582
- return new Parser().parseFromString(input, type);
583
- }
584
- function isSVG(input, Parser) {
585
- const doc = parseDOM(input, "image/svg+xml", Parser);
586
- if (!doc.documentElement)
587
- return false;
588
- return doc.documentElement.nodeName.toLowerCase() === "svg";
589
- }
590
- function isHTML(input, Parser) {
591
- const doc = parseDOM(input, "text/html", Parser);
592
- return doc.documentElement.querySelectorAll("*").length > 2;
593
- }
594
- var isMarkup = (input, Parser) => input.indexOf("<") > -1 && input.indexOf(">") > -1 && (isHTML(input, Parser) || isSVG(input, Parser));
595
- function renderMarkup(markup, Parser, doc) {
596
- const parsed = doc ? doc : parseDOM(markup, getMimeType(markup, Parser), Parser);
597
- if (parsed.body)
598
- return Array.from(parsed.body.childNodes);
599
- return parsed.documentElement ? [parsed.documentElement] : [];
600
- }
601
- function getMimeType(input, Parser) {
602
- if (isSVG(input, Parser)) {
603
- return "image/svg+xml";
604
- }
605
- return "text/html";
606
- }
607
- function domNodeToVNode(node) {
608
- if (node.nodeType === 3) {
609
- return node.textContent || "";
610
- }
611
- if (node.nodeType === 1) {
612
- const element = node;
613
- const attributes = {};
614
- for (let i = 0;i < element.attributes.length; i++) {
615
- const attr = element.attributes[i];
616
- attributes[attr.name] = domAttributeToVNodeValue(attr);
617
- }
618
- const children = [];
619
- for (let i = 0;i < element.childNodes.length; i++) {
620
- const childVNode = domNodeToVNode(element.childNodes[i]);
621
- children.push(childVNode);
622
- }
623
- return {
624
- type: element.tagName.toLowerCase(),
625
- attributes: { ...attributes, [FROM_DOM_MARKER]: true },
626
- children
627
- };
628
- }
629
- return "";
630
- }
631
- function htmlStringToVNodes(html, Parser) {
632
- const parser = new Parser;
633
- const doc = parser.parseFromString(html, "text/html");
634
- const vNodes = [];
635
- for (let i = 0;i < doc.body.childNodes.length; i++) {
636
- const vnode = domNodeToVNode(doc.body.childNodes[i]);
637
- if (vnode !== "") {
638
- vNodes.push(vnode);
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;
639
589
  }
590
+ previous[i] = low > 0 ? tails[low - 1] : -1;
591
+ tails[low] = i;
592
+ if (low === length)
593
+ length++;
640
594
  }
641
- return vNodes;
642
- }
595
+ for (let i = length > 0 ? tails[length - 1] : -1;i >= 0; i = previous[i])
596
+ marked[i] = 1;
597
+ return marked;
598
+ };
643
599
 
644
600
  // src/morph.ts
645
601
  var areDomNodesEqual = (oldNode, newNode) => {
@@ -663,8 +619,8 @@ var areDomNodesEqual = (oldNode, newNode) => {
663
619
  return false;
664
620
  }
665
621
  }
666
- if (oldNode.nodeType === 3) {
667
- if (oldNode.textContent !== newNode.textContent)
622
+ if (oldNode.nodeType === 3 || oldNode.nodeType === 8) {
623
+ if (oldNode.nodeValue !== newNode.nodeValue)
668
624
  return false;
669
625
  }
670
626
  return true;
@@ -761,6 +717,8 @@ function areNodeAndChildMatching(domNode, child) {
761
717
  if (typeof child === "string" || typeof child === "number" || typeof child === "boolean") {
762
718
  return domNode.nodeType === 3;
763
719
  }
720
+ if (isCommentVNode(child))
721
+ return domNode.nodeType === 8;
764
722
  if (child && typeof child === "object") {
765
723
  if (domNode.nodeType !== 1)
766
724
  return false;
@@ -854,6 +812,11 @@ function patchElementInPlace(el, vnode, globals, mergeAttributes = false) {
854
812
  return;
855
813
  morphDomDirect(el, vnode.children ?? [], globals);
856
814
  }
815
+ function replaceNode(old, next) {
816
+ if (old.nodeType === 1)
817
+ clearDelegatedEventsDeep(old);
818
+ old.parentNode?.replaceChild(next, old);
819
+ }
857
820
  function morphNode(domNode, child, globals, mergeAttributes = false) {
858
821
  if (typeof child === "string" || typeof child === "number" || typeof child === "boolean") {
859
822
  const text = String(child);
@@ -863,7 +826,18 @@ function morphNode(domNode, child, globals, mergeAttributes = false) {
863
826
  return domNode;
864
827
  }
865
828
  const next = globals.window.document.createTextNode(text);
866
- domNode.parentNode?.replaceChild(next, domNode);
829
+ replaceNode(domNode, next);
830
+ return next;
831
+ }
832
+ if (isCommentVNode(child)) {
833
+ const data = child.value ?? "";
834
+ if (domNode.nodeType === 8) {
835
+ if (domNode.nodeValue !== data)
836
+ domNode.nodeValue = data;
837
+ return domNode;
838
+ }
839
+ const next = globals.window.document.createComment(data);
840
+ replaceNode(domNode, next);
867
841
  return next;
868
842
  }
869
843
  if (child && typeof child === "object") {
@@ -887,13 +861,15 @@ function morphNode(domNode, child, globals, mergeAttributes = false) {
887
861
  const first = Array.isArray(created) ? created[0] : created;
888
862
  if (!first)
889
863
  return null;
890
- el.parentNode?.replaceChild(first, el);
864
+ replaceNode(el, first);
891
865
  handleLifecycleEventsForOnMount(first);
892
866
  return first;
893
867
  }
894
868
  patchElementInPlace(el, child, globals, mergeAttributes);
895
869
  return el;
896
870
  }
871
+ if (domNode.nodeType === 1)
872
+ clearDelegatedEventsDeep(domNode);
897
873
  domNode.parentNode?.removeChild(domNode);
898
874
  return null;
899
875
  }
@@ -954,6 +930,8 @@ function morphDiff(targetRoot, patchItems, globals) {
954
930
  for (const item of patchItems) {
955
931
  if (typeof item === "string" && item.trim() === "")
956
932
  continue;
933
+ if (isCommentVNode(item))
934
+ continue;
957
935
  const key = getVNodeMatchKey(item);
958
936
  if (!key) {
959
937
  throw new Error(`morph diff: patch items must be elements with a key or id attribute (got ${describePatchItem(item)})`);
@@ -974,71 +952,140 @@ function morphDiff(targetRoot, patchItems, globals) {
974
952
  }
975
953
  }
976
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
+ }
977
998
  function morphDomDirect(parentElement, newVDOM, globals, mode = "replace") {
978
999
  const el = parentElement;
979
1000
  const isCustomElement = el.tagName.includes("-");
980
- const targetRoot = el.shadowRoot && !isCustomElement ? el.shadowRoot : parentElement;
1001
+ const targetRoot = templateContent(parentElement) ?? (el.shadowRoot && !isCustomElement ? el.shadowRoot : parentElement);
981
1002
  const nextChildren = normalizeChildren(newVDOM, mode === "diff");
982
1003
  if (mode === "diff") {
983
1004
  morphDiff(targetRoot, nextChildren, globals);
984
1005
  return;
985
1006
  }
1007
+ if (patchByPosition(targetRoot, nextChildren, globals))
1008
+ return;
986
1009
  const existing = Array.from(targetRoot.childNodes);
987
1010
  const keyedPool = new Map;
988
- const nodeKeys = new WeakMap;
1011
+ const nodeKeys = [];
989
1012
  const unkeyedPool = [];
990
- for (const node of existing) {
991
- const keys = getDomMatchKeys(node);
1013
+ for (let i = 0;i < existing.length; i++) {
1014
+ const keys = getDomMatchKeys(existing[i]);
992
1015
  if (keys.length > 0) {
993
- nodeKeys.set(node, keys);
1016
+ nodeKeys[i] = keys;
994
1017
  let addedToKeyedPool = false;
995
1018
  for (const k of keys) {
996
1019
  if (!keyedPool.has(k)) {
997
- keyedPool.set(k, node);
1020
+ keyedPool.set(k, i);
998
1021
  addedToKeyedPool = true;
999
1022
  }
1000
1023
  }
1001
1024
  if (!addedToKeyedPool) {
1002
- unkeyedPool.push(node);
1025
+ unkeyedPool.push(i);
1003
1026
  }
1004
1027
  } else {
1005
- unkeyedPool.push(node);
1028
+ unkeyedPool.push(i);
1006
1029
  }
1007
1030
  }
1008
- const consumeKeyedNode = (node) => {
1009
- const keys = nodeKeys.get(node) ?? [];
1010
- for (const k of keys)
1011
- 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
+ }
1012
1036
  };
1013
1037
  const takeUnkeyedMatch = (child) => {
1014
- for (let i = 0;i < unkeyedPool.length; i++) {
1015
- const candidate = unkeyedPool[i];
1016
- if (areNodeAndChildMatching(candidate, child)) {
1017
- unkeyedPool.splice(i, 1);
1018
- 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;
1019
1043
  }
1020
1044
  }
1021
- return;
1045
+ return -1;
1022
1046
  };
1023
- let domIndex = 0;
1024
- 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];
1025
1053
  const key = getVNodeMatchKey(child);
1026
- let match;
1054
+ let i = -1;
1027
1055
  if (key) {
1028
- match = keyedPool.get(key);
1029
- if (match)
1030
- consumeKeyedNode(match);
1056
+ const hit = keyedPool.get(key);
1057
+ if (hit !== undefined) {
1058
+ i = hit;
1059
+ consumeKeyedNode(hit);
1060
+ }
1031
1061
  } else {
1032
- match = takeUnkeyedMatch(child);
1062
+ i = takeUnkeyedMatch(child);
1033
1063
  }
1034
- const anchor = targetRoot.childNodes[domIndex] ?? null;
1035
- if (match) {
1036
- if (match !== anchor) {
1037
- targetRoot.insertBefore(match, anchor);
1038
- }
1039
- const morphed = morphNode(match, child, globals);
1040
- if (morphed && morphed !== match) {}
1041
- 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);
1042
1089
  continue;
1043
1090
  }
1044
1091
  const created = createDomFromChild(child, globals);
@@ -1048,14 +1095,13 @@ function morphDomDirect(parentElement, newVDOM, globals, mode = "replace") {
1048
1095
  for (const node of nodes) {
1049
1096
  targetRoot.insertBefore(node, anchor);
1050
1097
  handleLifecycleEventsForOnMount(node);
1051
- domIndex++;
1052
1098
  }
1053
1099
  }
1054
1100
  const remaining = new Set;
1055
- for (const node of unkeyedPool)
1056
- remaining.add(node);
1057
- for (const node of keyedPool.values())
1058
- 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]);
1059
1105
  for (const node of remaining) {
1060
1106
  if (node.parentNode === targetRoot) {
1061
1107
  if (node.nodeType === 1) {
@@ -1068,7 +1114,10 @@ function morphDomDirect(parentElement, newVDOM, globals, mode = "replace") {
1068
1114
  function replaceDomWithVdom(parentElement, newVDOM, globals) {
1069
1115
  const resolvedGlobals = resolveGlobals(parentElement, globals);
1070
1116
  while (parentElement.firstChild) {
1071
- parentElement.removeChild(parentElement.firstChild);
1117
+ const child = parentElement.firstChild;
1118
+ if (child.nodeType === 1)
1119
+ clearDelegatedEventsDeep(child);
1120
+ parentElement.removeChild(child);
1072
1121
  }
1073
1122
  const renderer = getRenderer(resolvedGlobals.window.document);
1074
1123
  const newDom = renderer.createElementOrElements(newVDOM);
@@ -1084,7 +1133,6 @@ function replaceDomWithVdom(parentElement, newVDOM, globals) {
1084
1133
  handleLifecycleEventsForOnMount(newDom);
1085
1134
  }
1086
1135
  }
1087
-
1088
1136
  // src/transitions.ts
1089
1137
  var injectShakeKeyframes = (doc) => {
1090
1138
  if (!doc)
@@ -1168,7 +1216,9 @@ var performCrossfade = async (element, updateCallback, duration, easing) => {
1168
1216
  const doc = element.ownerDocument;
1169
1217
  try {
1170
1218
  const rect = element.getBoundingClientRect();
1171
- 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;`;
1172
1222
  element.style.opacity = "0";
1173
1223
  element.style.transition = `opacity ${duration}ms ${easing}`;
1174
1224
  doc.body.appendChild(snapshot);
@@ -1228,10 +1278,9 @@ var performTransition = async (element, updateCallback, config = {}) => {
1228
1278
  throw error;
1229
1279
  }
1230
1280
  };
1231
-
1232
- // src/index.ts
1281
+ // src/api.ts
1233
1282
  var inflightTransitions = new WeakMap;
1234
- var morph2 = (el, newContent, options = {}) => {
1283
+ var morph = (el, newContent, options = {}) => {
1235
1284
  const globals = resolveGlobals(el);
1236
1285
  const win = globals.window;
1237
1286
  const mode = options.diff ? "diff" : "replace";
@@ -1242,15 +1291,15 @@ var morph2 = (el, newContent, options = {}) => {
1242
1291
  const transitionTarget = config.target === "self" ? el : el.parentElement;
1243
1292
  if (!transitionTarget) {
1244
1293
  apply(newContent);
1245
- return;
1294
+ return Promise.resolve();
1246
1295
  }
1247
- const slot2 = { content: newContent };
1248
- inflightTransitions.set(el, slot2);
1296
+ const slot = { content: newContent };
1297
+ inflightTransitions.set(el, slot);
1249
1298
  return performTransition(transitionTarget, async () => {
1250
- if (inflightTransitions.get(el) === slot2)
1251
- apply(slot2.content);
1299
+ if (inflightTransitions.get(el) === slot)
1300
+ apply(slot.content);
1252
1301
  }, config).finally(() => {
1253
- if (inflightTransitions.get(el) === slot2)
1302
+ if (inflightTransitions.get(el) === slot)
1254
1303
  inflightTransitions.delete(el);
1255
1304
  });
1256
1305
  }
@@ -1259,7 +1308,151 @@ var morph2 = (el, newContent, options = {}) => {
1259
1308
  slot.content = newContent;
1260
1309
  apply(newContent);
1261
1310
  };
1262
-
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
+ };
1263
1456
  // src/global.ts
1264
1457
  if (typeof globalThis !== "undefined") {
1265
1458
  const g = globalThis;
@@ -1268,46 +1461,63 @@ if (typeof globalThis !== "undefined") {
1268
1461
  g.df$ = {};
1269
1462
  }
1270
1463
  Object.assign(g.df$, exports_src);
1464
+ const fn = g.df$.fn;
1465
+ if (typeof fn === "object" && fn !== null)
1466
+ installReactive(g.df$);
1271
1467
  }
1272
1468
  export {
1273
- updateDomWithVdom,
1274
- resolveGlobals,
1275
- replaceDomWithVdom,
1276
- renderMarkup,
1277
- removeDelegatedEventByKey,
1278
- removeDelegatedEvent,
1279
- registerDelegatedEvent,
1280
- queueCallback,
1281
- performTransition,
1282
- parseEventPropName,
1283
- parseDOM,
1284
- observeUnmount,
1285
- nsMap,
1286
- morph2 as morph,
1287
- isSVG,
1288
- isMarkup,
1289
- isHTML,
1290
- htmlStringToVNodes,
1291
- handleLifecycleEventsForOnMount,
1292
- getTransitionStyles,
1293
- getRenderer,
1294
- getRegisteredEventTypes,
1295
- getRegisteredEventKeys,
1296
- getMimeType,
1297
- domNodeToVNode,
1298
- clearDelegatedEventsDeep,
1299
- clearDelegatedEvents,
1300
- areDomNodesEqual,
1301
- applyStyles,
1302
- XMLNS_ATTRIBUTE_NAME,
1303
- XLINK_ATTRIBUTE_NAME,
1304
- REF_ATTRIBUTE_NAME,
1305
- FROM_DOM_MARKER,
1306
- DEFAULT_TRANSITION_CONFIG,
1307
- DANGEROUSLY_SET_INNER_HTML_ATTRIBUTE,
1469
+ CAPTURE_ONLY_EVENTS,
1308
1470
  CLASS_ATTRIBUTE_NAME,
1309
- 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
1310
1520
  };
1311
1521
 
1312
- //# debugId=BA5C1E84E9A829C164756E2164756E21
1522
+ //# debugId=18D1BB0152DB217664756E2164756E21
1313
1523
  //# sourceMappingURL=all.js.map