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/CHANGELOG.md +34 -0
- package/README.md +281 -212
- package/dist/all.js +639 -429
- package/dist/all.js.map +15 -11
- package/dist/all.min.js +1 -1
- package/dist/all.min.js.map +1 -1
- package/dist/index.cjs +520 -347
- package/dist/index.d.ts +338 -28
- package/dist/index.mjs +507 -348
- package/dist/stats.json +9 -9
- package/package.json +46 -13
- package/skills/defuss-morph/SKILL.md +101 -0
- package/skills/defuss-morph/references/api.md +86 -0
- package/skills/defuss-morph/references/integrations.md +116 -0
- package/skills/defuss-morph/references/patterns.md +190 -0
- package/skills/defuss-morph/references/pitfalls.md +29 -0
package/dist/index.cjs
CHANGED
|
@@ -11,8 +11,7 @@ const CAPTURE_ONLY_EVENTS = /* @__PURE__ */ new Set([
|
|
|
11
11
|
// Note: focusin/focusout DO bubble, so they're not included here
|
|
12
12
|
]);
|
|
13
13
|
const elementHandlerMap = /* @__PURE__ */ new WeakMap();
|
|
14
|
-
const
|
|
15
|
-
const captureDispatched = /* @__PURE__ */ new WeakMap();
|
|
14
|
+
const slotListeners = /* @__PURE__ */ new WeakMap();
|
|
16
15
|
const activeDispatches = /* @__PURE__ */ new WeakMap();
|
|
17
16
|
const parseEventPropName = (propName) => {
|
|
18
17
|
if (!propName.startsWith("on")) return null;
|
|
@@ -31,147 +30,64 @@ const getOrCreateElementHandlers = (el) => {
|
|
|
31
30
|
elementHandlerMap.set(el, created);
|
|
32
31
|
return created;
|
|
33
32
|
};
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
if (
|
|
37
|
-
const
|
|
38
|
-
let
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
33
|
+
const runSlot = (target, eventType, phase, event) => {
|
|
34
|
+
const handler = elementHandlerMap.get(target)?.get(eventType)?.[phase];
|
|
35
|
+
if (!handler) return;
|
|
36
|
+
const dispatchKey = `${eventType}:${phase}`;
|
|
37
|
+
let active = activeDispatches.get(target);
|
|
38
|
+
if (active?.has(dispatchKey)) return;
|
|
39
|
+
if (!active) {
|
|
40
|
+
active = /* @__PURE__ */ new Set();
|
|
41
|
+
activeDispatches.set(target, active);
|
|
42
|
+
}
|
|
43
|
+
active.add(dispatchKey);
|
|
44
|
+
try {
|
|
45
|
+
handler.call(target, event);
|
|
46
|
+
} finally {
|
|
47
|
+
active.delete(dispatchKey);
|
|
47
48
|
}
|
|
48
|
-
const doc = event.target?.ownerDocument;
|
|
49
|
-
if (doc && path[path.length - 1] !== doc) path.push(doc);
|
|
50
|
-
const win = doc?.defaultView;
|
|
51
|
-
if (win && path[path.length - 1] !== win) path.push(win);
|
|
52
|
-
return path;
|
|
53
49
|
};
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const entry = handlersByEvent.get(eventType);
|
|
65
|
-
if (!entry) continue;
|
|
66
|
-
let targets = dispatched.get(event);
|
|
67
|
-
if (targets?.has(target)) continue;
|
|
68
|
-
if (!targets) {
|
|
69
|
-
targets = /* @__PURE__ */ new WeakSet();
|
|
70
|
-
dispatched.set(event, targets);
|
|
71
|
-
}
|
|
72
|
-
targets.add(target);
|
|
73
|
-
const dispatchKey = `${eventType}:${phase}`;
|
|
74
|
-
let activeSet = activeDispatches.get(target);
|
|
75
|
-
if (activeSet?.has(dispatchKey)) continue;
|
|
76
|
-
if (!activeSet) {
|
|
77
|
-
activeSet = /* @__PURE__ */ new Set();
|
|
78
|
-
activeDispatches.set(target, activeSet);
|
|
79
|
-
}
|
|
80
|
-
activeSet.add(dispatchKey);
|
|
81
|
-
try {
|
|
82
|
-
if (phase === "capture") {
|
|
83
|
-
if (entry.capture) {
|
|
84
|
-
entry.capture.call(target, event);
|
|
85
|
-
if (event.cancelBubble)
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
if (entry.captureSet) {
|
|
89
|
-
for (const handler of entry.captureSet) {
|
|
90
|
-
handler.call(target, event);
|
|
91
|
-
if (event.cancelBubble)
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
} else {
|
|
96
|
-
if (entry.bubble) {
|
|
97
|
-
entry.bubble.call(target, event);
|
|
98
|
-
if (event.cancelBubble)
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
if (entry.bubbleSet) {
|
|
102
|
-
for (const handler of entry.bubbleSet) {
|
|
103
|
-
handler.call(target, event);
|
|
104
|
-
if (event.cancelBubble)
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
} finally {
|
|
110
|
-
activeSet.delete(dispatchKey);
|
|
111
|
-
}
|
|
50
|
+
const setSlot = (target, eventType, phase, entry, handler) => {
|
|
51
|
+
entry[phase] = handler;
|
|
52
|
+
const key = `${eventType}:${phase}`;
|
|
53
|
+
const capture = phase === "capture";
|
|
54
|
+
let byKey = slotListeners.get(target);
|
|
55
|
+
const installed = byKey?.get(key);
|
|
56
|
+
if (handler && !installed) {
|
|
57
|
+
if (!byKey) {
|
|
58
|
+
byKey = /* @__PURE__ */ new Map();
|
|
59
|
+
slotListeners.set(target, byKey);
|
|
112
60
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const captureKey = `${eventType}:capture`;
|
|
120
|
-
if (!installed.has(captureKey)) {
|
|
121
|
-
root.addEventListener(
|
|
122
|
-
eventType,
|
|
123
|
-
createPhaseHandler(eventType, "capture"),
|
|
124
|
-
true
|
|
125
|
-
);
|
|
126
|
-
installed.add(captureKey);
|
|
127
|
-
}
|
|
128
|
-
const bubbleKey = `${eventType}:bubble`;
|
|
129
|
-
if (!installed.has(bubbleKey)) {
|
|
130
|
-
root.addEventListener(
|
|
131
|
-
eventType,
|
|
132
|
-
createPhaseHandler(eventType, "bubble"),
|
|
133
|
-
false
|
|
134
|
-
);
|
|
135
|
-
installed.add(bubbleKey);
|
|
61
|
+
const trampoline = (event) => runSlot(target, eventType, phase, event);
|
|
62
|
+
byKey.set(key, trampoline);
|
|
63
|
+
target.addEventListener(eventType, trampoline, capture);
|
|
64
|
+
} else if (!handler && installed) {
|
|
65
|
+
byKey.delete(key);
|
|
66
|
+
target.removeEventListener(eventType, installed, capture);
|
|
136
67
|
}
|
|
137
68
|
};
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
return root;
|
|
145
|
-
}
|
|
146
|
-
return null;
|
|
69
|
+
const clearPhase = (target, eventType, entry, phase) => {
|
|
70
|
+
setSlot(target, eventType, phase, entry, void 0);
|
|
71
|
+
const setKey = phase === "capture" ? "captureSet" : "bubbleSet";
|
|
72
|
+
for (const handler of entry[setKey] ?? [])
|
|
73
|
+
target.removeEventListener(eventType, handler, phase === "capture");
|
|
74
|
+
entry[setKey] = void 0;
|
|
147
75
|
};
|
|
148
76
|
const registerDelegatedEvent = (element, eventType, handler, options = {}) => {
|
|
149
|
-
const root = getEventRoot(element);
|
|
150
77
|
const capture = options.capture || CAPTURE_ONLY_EVENTS.has(eventType);
|
|
151
|
-
|
|
152
|
-
ensureRootListener(root, eventType);
|
|
153
|
-
} else if (element.ownerDocument) {
|
|
154
|
-
ensureRootListener(element.ownerDocument, eventType);
|
|
155
|
-
} else {
|
|
156
|
-
element.addEventListener(eventType, handler, capture);
|
|
157
|
-
}
|
|
78
|
+
const phase = capture ? "capture" : "bubble";
|
|
158
79
|
const byEvent = getOrCreateElementHandlers(element);
|
|
159
80
|
const entry = byEvent.get(eventType) ?? {};
|
|
160
81
|
byEvent.set(eventType, entry);
|
|
161
82
|
if (options.multi) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
entry.bubbleSet.add(handler);
|
|
83
|
+
const setKey = capture ? "captureSet" : "bubbleSet";
|
|
84
|
+
const set = entry[setKey] ??= /* @__PURE__ */ new Set();
|
|
85
|
+
if (!set.has(handler)) {
|
|
86
|
+
set.add(handler);
|
|
87
|
+
element.addEventListener(eventType, handler, capture);
|
|
168
88
|
}
|
|
169
89
|
} else {
|
|
170
|
-
|
|
171
|
-
entry.capture = handler;
|
|
172
|
-
} else {
|
|
173
|
-
entry.bubble = handler;
|
|
174
|
-
}
|
|
90
|
+
setSlot(element, eventType, phase, entry, handler);
|
|
175
91
|
}
|
|
176
92
|
};
|
|
177
93
|
const isEntryEmpty = (entry) => !entry.capture && !entry.bubble && (!entry.captureSet || entry.captureSet.size === 0) && (!entry.bubbleSet || entry.bubbleSet.size === 0);
|
|
@@ -181,34 +97,38 @@ const removeDelegatedEvent = (target, eventType, handler, _options = {}) => {
|
|
|
181
97
|
const entry = byEvent.get(eventType);
|
|
182
98
|
if (!entry) return;
|
|
183
99
|
if (handler) {
|
|
184
|
-
|
|
185
|
-
entry.captureSet.
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
entry
|
|
189
|
-
|
|
190
|
-
if (entry.capture === handler) {
|
|
191
|
-
entry.capture = void 0;
|
|
192
|
-
}
|
|
193
|
-
if (entry.bubble === handler) {
|
|
194
|
-
entry.bubble = void 0;
|
|
100
|
+
for (const phase of ["capture", "bubble"]) {
|
|
101
|
+
const set = phase === "capture" ? entry.captureSet : entry.bubbleSet;
|
|
102
|
+
if (set?.delete(handler))
|
|
103
|
+
target.removeEventListener(eventType, handler, phase === "capture");
|
|
104
|
+
if (entry[phase] === handler)
|
|
105
|
+
setSlot(target, eventType, phase, entry, void 0);
|
|
195
106
|
}
|
|
196
|
-
target.removeEventListener(eventType, handler, true);
|
|
197
|
-
target.removeEventListener(eventType, handler, false);
|
|
198
107
|
} else {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
entry.captureSet = void 0;
|
|
202
|
-
entry.bubbleSet = void 0;
|
|
108
|
+
clearPhase(target, eventType, entry, "capture");
|
|
109
|
+
clearPhase(target, eventType, entry, "bubble");
|
|
203
110
|
}
|
|
204
111
|
if (isEntryEmpty(entry)) {
|
|
205
112
|
byEvent.delete(eventType);
|
|
206
113
|
}
|
|
207
114
|
};
|
|
115
|
+
const clearHooks = /* @__PURE__ */ new Set();
|
|
116
|
+
const onClearDelegatedEvents = (hook) => {
|
|
117
|
+
clearHooks.add(hook);
|
|
118
|
+
return () => {
|
|
119
|
+
clearHooks.delete(hook);
|
|
120
|
+
};
|
|
121
|
+
};
|
|
208
122
|
const clearDelegatedEvents = (target) => {
|
|
209
123
|
const byEvent = elementHandlerMap.get(target);
|
|
210
|
-
if (
|
|
211
|
-
|
|
124
|
+
if (byEvent) {
|
|
125
|
+
for (const [eventType, entry] of byEvent) {
|
|
126
|
+
clearPhase(target, eventType, entry, "capture");
|
|
127
|
+
clearPhase(target, eventType, entry, "bubble");
|
|
128
|
+
}
|
|
129
|
+
byEvent.clear();
|
|
130
|
+
}
|
|
131
|
+
for (const hook of clearHooks) hook(target);
|
|
212
132
|
};
|
|
213
133
|
const clearDelegatedEventsDeep = (root) => {
|
|
214
134
|
clearDelegatedEvents(root);
|
|
@@ -246,16 +166,113 @@ const removeDelegatedEventByKey = (element, eventType, phase) => {
|
|
|
246
166
|
if (!byEvent) return;
|
|
247
167
|
const entry = byEvent.get(eventType);
|
|
248
168
|
if (!entry) return;
|
|
249
|
-
|
|
250
|
-
entry.capture = void 0;
|
|
251
|
-
entry.captureSet = void 0;
|
|
252
|
-
} else {
|
|
253
|
-
entry.bubble = void 0;
|
|
254
|
-
entry.bubbleSet = void 0;
|
|
255
|
-
}
|
|
169
|
+
clearPhase(element, eventType, entry, phase);
|
|
256
170
|
if (isEntryEmpty(entry)) byEvent.delete(eventType);
|
|
257
171
|
};
|
|
258
172
|
|
|
173
|
+
const FROM_DOM_MARKER = /* @__PURE__ */ Symbol("defuss-morph.from-dom");
|
|
174
|
+
const COMMENT_TYPE = "#comment";
|
|
175
|
+
const isCommentVNode = (value) => !!value && typeof value === "object" && value.type === COMMENT_TYPE;
|
|
176
|
+
const HTML_BOOLEAN_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
177
|
+
"allowfullscreen",
|
|
178
|
+
"async",
|
|
179
|
+
"autofocus",
|
|
180
|
+
"autoplay",
|
|
181
|
+
"checked",
|
|
182
|
+
"controls",
|
|
183
|
+
"default",
|
|
184
|
+
"defer",
|
|
185
|
+
"disabled",
|
|
186
|
+
"formnovalidate",
|
|
187
|
+
"hidden",
|
|
188
|
+
"inert",
|
|
189
|
+
"ismap",
|
|
190
|
+
"itemscope",
|
|
191
|
+
"loop",
|
|
192
|
+
"multiple",
|
|
193
|
+
"muted",
|
|
194
|
+
"nomodule",
|
|
195
|
+
"novalidate",
|
|
196
|
+
"open",
|
|
197
|
+
"playsinline",
|
|
198
|
+
"readonly",
|
|
199
|
+
"required",
|
|
200
|
+
"reversed",
|
|
201
|
+
"selected"
|
|
202
|
+
]);
|
|
203
|
+
const domAttributeToVNodeValue = (attr) => HTML_BOOLEAN_ATTRIBUTES.has(attr.name.toLowerCase()) ? true : attr.value;
|
|
204
|
+
function parseDOM(input, type, Parser) {
|
|
205
|
+
return new Parser().parseFromString(input, type);
|
|
206
|
+
}
|
|
207
|
+
function isSVG(input, Parser) {
|
|
208
|
+
const doc = parseDOM(input, "image/svg+xml", Parser);
|
|
209
|
+
if (!doc.documentElement) return false;
|
|
210
|
+
return doc.documentElement.nodeName.toLowerCase() === "svg";
|
|
211
|
+
}
|
|
212
|
+
function isHTML(input, Parser) {
|
|
213
|
+
const doc = parseDOM(input, "text/html", Parser);
|
|
214
|
+
return doc.documentElement.querySelectorAll("*").length > 2;
|
|
215
|
+
}
|
|
216
|
+
const isMarkup = (input, Parser) => input.indexOf("<") > -1 && input.indexOf(">") > -1 && (isHTML(input, Parser) || isSVG(input, Parser));
|
|
217
|
+
function renderMarkup(markup, Parser, doc) {
|
|
218
|
+
const parsed = doc ? doc : parseDOM(markup, getMimeType(markup, Parser), Parser);
|
|
219
|
+
if (parsed.body) return Array.from(parsed.body.childNodes);
|
|
220
|
+
return parsed.documentElement ? [parsed.documentElement] : [];
|
|
221
|
+
}
|
|
222
|
+
function getMimeType(input, Parser) {
|
|
223
|
+
if (isSVG(input, Parser)) {
|
|
224
|
+
return "image/svg+xml";
|
|
225
|
+
}
|
|
226
|
+
return "text/html";
|
|
227
|
+
}
|
|
228
|
+
const templateContent = (element) => element.localName === "template" ? element.content : void 0;
|
|
229
|
+
function domNodeToVNode(node) {
|
|
230
|
+
if (node.nodeType === 3) {
|
|
231
|
+
return node.nodeValue || "";
|
|
232
|
+
}
|
|
233
|
+
if (node.nodeType === 1) {
|
|
234
|
+
const element = node;
|
|
235
|
+
const attributes = {};
|
|
236
|
+
for (let i = 0; i < element.attributes.length; i++) {
|
|
237
|
+
const attr = element.attributes[i];
|
|
238
|
+
attributes[attr.name] = domAttributeToVNodeValue(attr);
|
|
239
|
+
}
|
|
240
|
+
const children = [];
|
|
241
|
+
for (let child = (templateContent(element) ?? element).firstChild; child; child = child.nextSibling) {
|
|
242
|
+
children.push(domNodeToVNode(child));
|
|
243
|
+
}
|
|
244
|
+
attributes[FROM_DOM_MARKER] = true;
|
|
245
|
+
return { type: element.localName, attributes, children };
|
|
246
|
+
}
|
|
247
|
+
if (node.nodeType === 8) {
|
|
248
|
+
return { type: COMMENT_TYPE, value: node.nodeValue ?? "" };
|
|
249
|
+
}
|
|
250
|
+
return "";
|
|
251
|
+
}
|
|
252
|
+
const inertDocuments = /* @__PURE__ */ new WeakMap();
|
|
253
|
+
const DOCUMENT_START = /^\s*(?:<!--[\s\S]*?-->\s*)*<(?:!doctype|html|head|body)[\s>/]/i;
|
|
254
|
+
function htmlStringToVNodes(html, Parser) {
|
|
255
|
+
if (DOCUMENT_START.test(html)) {
|
|
256
|
+
return toVNodes(new Parser().parseFromString(html, "text/html").body.childNodes);
|
|
257
|
+
}
|
|
258
|
+
let inert = inertDocuments.get(Parser);
|
|
259
|
+
if (!inert) {
|
|
260
|
+
inert = new Parser().parseFromString("", "text/html");
|
|
261
|
+
inertDocuments.set(Parser, inert);
|
|
262
|
+
}
|
|
263
|
+
const template = inert.createElement("template");
|
|
264
|
+
template.innerHTML = html;
|
|
265
|
+
return toVNodes(template.content.childNodes);
|
|
266
|
+
}
|
|
267
|
+
function toVNodes(nodes) {
|
|
268
|
+
const vNodes = [];
|
|
269
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
270
|
+
const vnode = domNodeToVNode(nodes[i]);
|
|
271
|
+
if (vnode !== "") vNodes.push(vnode);
|
|
272
|
+
}
|
|
273
|
+
return vNodes;
|
|
274
|
+
}
|
|
275
|
+
|
|
259
276
|
const CLASS_ATTRIBUTE_NAME = "class";
|
|
260
277
|
const XLINK_ATTRIBUTE_NAME = "xlink";
|
|
261
278
|
const XMLNS_ATTRIBUTE_NAME = "xmlns";
|
|
@@ -305,13 +322,18 @@ const observeUnmount = (domNode, onUnmount) => {
|
|
|
305
322
|
});
|
|
306
323
|
observer.observe(parentNode, { childList: true });
|
|
307
324
|
};
|
|
325
|
+
const unmountObserved = /* @__PURE__ */ new WeakSet();
|
|
308
326
|
const handleLifecycleEventsForOnMount = (newEl) => {
|
|
309
327
|
if (typeof newEl?.$onMount === "function") {
|
|
310
328
|
newEl.$onMount(newEl);
|
|
311
329
|
newEl.$onMount = null;
|
|
312
330
|
}
|
|
313
|
-
if (typeof newEl?.$onUnmount === "function") {
|
|
314
|
-
|
|
331
|
+
if (typeof newEl?.$onUnmount === "function" && !unmountObserved.has(newEl)) {
|
|
332
|
+
unmountObserved.add(newEl);
|
|
333
|
+
observeUnmount(newEl, () => {
|
|
334
|
+
unmountObserved.delete(newEl);
|
|
335
|
+
newEl.$onUnmount?.(newEl);
|
|
336
|
+
});
|
|
315
337
|
}
|
|
316
338
|
};
|
|
317
339
|
const getRenderer = (document) => {
|
|
@@ -322,6 +344,9 @@ const getRenderer = (document) => {
|
|
|
322
344
|
if (Array.isArray(virtualNode)) {
|
|
323
345
|
return renderer.createChildElements(virtualNode, parentDomElement);
|
|
324
346
|
}
|
|
347
|
+
if (typeof virtualNode === "string" || typeof virtualNode === "number") {
|
|
348
|
+
return renderer.createTextNode(String(virtualNode), parentDomElement);
|
|
349
|
+
}
|
|
325
350
|
if (typeof virtualNode !== "undefined") {
|
|
326
351
|
return renderer.createElement(virtualNode, parentDomElement);
|
|
327
352
|
}
|
|
@@ -329,50 +354,45 @@ const getRenderer = (document) => {
|
|
|
329
354
|
},
|
|
330
355
|
createElement: (virtualNode, parentDomElement) => {
|
|
331
356
|
let newEl;
|
|
332
|
-
|
|
333
|
-
|
|
357
|
+
if (isCommentVNode(virtualNode)) {
|
|
358
|
+
const comment = document.createComment(virtualNode.value ?? "");
|
|
359
|
+
parentDomElement?.appendChild(comment);
|
|
360
|
+
return comment;
|
|
361
|
+
}
|
|
362
|
+
if (typeof virtualNode === "function" && virtualNode.constructor.name === "AsyncFunction") {
|
|
363
|
+
newEl = document.createElement("div");
|
|
364
|
+
} else if (typeof virtualNode === "object" && virtualNode !== null && "type" in virtualNode) {
|
|
365
|
+
const vNode = virtualNode;
|
|
366
|
+
if (typeof vNode.type === "function") {
|
|
334
367
|
newEl = document.createElement("div");
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
typeof vNode.type === "string" ? vNode.type.toUpperCase() : ""
|
|
345
|
-
)
|
|
346
|
-
) {
|
|
347
|
-
newEl = document.createElementNS(nsMap.svg, vNode.type);
|
|
348
|
-
} else {
|
|
349
|
-
newEl = document.createElement(vNode.type);
|
|
350
|
-
}
|
|
351
|
-
if (vNode.attributes) {
|
|
352
|
-
renderer.setAttributes(vNode, newEl);
|
|
353
|
-
if (vNode.attributes.dangerouslySetInnerHTML) {
|
|
354
|
-
newEl.innerHTML = vNode.attributes.dangerouslySetInnerHTML.__html;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
if (vNode.children && !vNode.attributes?.dangerouslySetInnerHTML) {
|
|
358
|
-
renderer.createChildElements(vNode.children, newEl);
|
|
359
|
-
}
|
|
368
|
+
newEl.innerText = `FATAL ERROR: ${vNode.type._error}`;
|
|
369
|
+
} else if (
|
|
370
|
+
// SVG support
|
|
371
|
+
typeof vNode.type === "string" && vNode.type.toUpperCase() === "SVG" || parentDomElement && renderer.hasSvgNamespace(
|
|
372
|
+
parentDomElement,
|
|
373
|
+
typeof vNode.type === "string" ? vNode.type.toUpperCase() : ""
|
|
374
|
+
)
|
|
375
|
+
) {
|
|
376
|
+
newEl = document.createElementNS(nsMap.svg, vNode.type);
|
|
360
377
|
} else {
|
|
361
|
-
|
|
362
|
-
|
|
378
|
+
newEl = document.createElement(vNode.type);
|
|
379
|
+
}
|
|
380
|
+
if (vNode.attributes) {
|
|
381
|
+
renderer.setAttributes(vNode, newEl);
|
|
382
|
+
if (vNode.attributes.dangerouslySetInnerHTML) {
|
|
383
|
+
newEl.innerHTML = vNode.attributes.dangerouslySetInnerHTML.__html;
|
|
363
384
|
}
|
|
364
385
|
}
|
|
365
|
-
if (
|
|
366
|
-
|
|
367
|
-
|
|
386
|
+
if (vNode.children && !vNode.attributes?.dangerouslySetInnerHTML) {
|
|
387
|
+
renderer.createChildElements(
|
|
388
|
+
vNode.children,
|
|
389
|
+
templateContent(newEl) ?? newEl
|
|
390
|
+
);
|
|
368
391
|
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
virtualNode
|
|
374
|
-
);
|
|
375
|
-
throw e;
|
|
392
|
+
}
|
|
393
|
+
if (newEl && parentDomElement) {
|
|
394
|
+
parentDomElement.appendChild(newEl);
|
|
395
|
+
handleLifecycleEventsForOnMount(newEl);
|
|
376
396
|
}
|
|
377
397
|
return newEl;
|
|
378
398
|
},
|
|
@@ -413,20 +433,8 @@ const getRenderer = (document) => {
|
|
|
413
433
|
return;
|
|
414
434
|
}
|
|
415
435
|
if (name === REF_ATTRIBUTE_NAME && typeof value !== "function") {
|
|
416
|
-
|
|
417
|
-
ref.current = domElement;
|
|
436
|
+
value.current = domElement;
|
|
418
437
|
domElement._defussRef = value;
|
|
419
|
-
domElement.$onUnmount = queueCallback(() => {
|
|
420
|
-
});
|
|
421
|
-
if (domElement.parentNode) {
|
|
422
|
-
observeUnmount(domElement, domElement.$onUnmount);
|
|
423
|
-
} else {
|
|
424
|
-
queueMicrotask(() => {
|
|
425
|
-
if (domElement.parentNode) {
|
|
426
|
-
observeUnmount(domElement, domElement.$onUnmount);
|
|
427
|
-
}
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
438
|
return;
|
|
431
439
|
}
|
|
432
440
|
const parsed = parseEventPropName(name);
|
|
@@ -437,15 +445,7 @@ const getRenderer = (document) => {
|
|
|
437
445
|
return;
|
|
438
446
|
}
|
|
439
447
|
if (eventType === "unmount") {
|
|
440
|
-
|
|
441
|
-
const existingUnmount = domElement.$onUnmount;
|
|
442
|
-
domElement.$onUnmount = () => {
|
|
443
|
-
existingUnmount();
|
|
444
|
-
value();
|
|
445
|
-
};
|
|
446
|
-
} else {
|
|
447
|
-
domElement.$onUnmount = queueCallback(value);
|
|
448
|
-
}
|
|
448
|
+
domElement.$onUnmount = queueCallback(value);
|
|
449
449
|
return;
|
|
450
450
|
}
|
|
451
451
|
registerDelegatedEvent(
|
|
@@ -519,95 +519,29 @@ const getRenderer = (document) => {
|
|
|
519
519
|
return renderer;
|
|
520
520
|
};
|
|
521
521
|
|
|
522
|
-
const
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
"itemscope",
|
|
538
|
-
"loop",
|
|
539
|
-
"multiple",
|
|
540
|
-
"muted",
|
|
541
|
-
"nomodule",
|
|
542
|
-
"novalidate",
|
|
543
|
-
"open",
|
|
544
|
-
"playsinline",
|
|
545
|
-
"readonly",
|
|
546
|
-
"required",
|
|
547
|
-
"reversed",
|
|
548
|
-
"selected"
|
|
549
|
-
]);
|
|
550
|
-
const domAttributeToVNodeValue = (attr) => HTML_BOOLEAN_ATTRIBUTES.has(attr.name.toLowerCase()) ? true : attr.value;
|
|
551
|
-
function parseDOM(input, type, Parser) {
|
|
552
|
-
return new Parser().parseFromString(input, type);
|
|
553
|
-
}
|
|
554
|
-
function isSVG(input, Parser) {
|
|
555
|
-
const doc = parseDOM(input, "image/svg+xml", Parser);
|
|
556
|
-
if (!doc.documentElement) return false;
|
|
557
|
-
return doc.documentElement.nodeName.toLowerCase() === "svg";
|
|
558
|
-
}
|
|
559
|
-
function isHTML(input, Parser) {
|
|
560
|
-
const doc = parseDOM(input, "text/html", Parser);
|
|
561
|
-
return doc.documentElement.querySelectorAll("*").length > 2;
|
|
562
|
-
}
|
|
563
|
-
const isMarkup = (input, Parser) => input.indexOf("<") > -1 && input.indexOf(">") > -1 && (isHTML(input, Parser) || isSVG(input, Parser));
|
|
564
|
-
function renderMarkup(markup, Parser, doc) {
|
|
565
|
-
const parsed = doc ? doc : parseDOM(markup, getMimeType(markup, Parser), Parser);
|
|
566
|
-
if (parsed.body) return Array.from(parsed.body.childNodes);
|
|
567
|
-
return parsed.documentElement ? [parsed.documentElement] : [];
|
|
568
|
-
}
|
|
569
|
-
function getMimeType(input, Parser) {
|
|
570
|
-
if (isSVG(input, Parser)) {
|
|
571
|
-
return "image/svg+xml";
|
|
572
|
-
}
|
|
573
|
-
return "text/html";
|
|
574
|
-
}
|
|
575
|
-
function domNodeToVNode(node) {
|
|
576
|
-
if (node.nodeType === 3) {
|
|
577
|
-
return node.textContent || "";
|
|
578
|
-
}
|
|
579
|
-
if (node.nodeType === 1) {
|
|
580
|
-
const element = node;
|
|
581
|
-
const attributes = {};
|
|
582
|
-
for (let i = 0; i < element.attributes.length; i++) {
|
|
583
|
-
const attr = element.attributes[i];
|
|
584
|
-
attributes[attr.name] = domAttributeToVNodeValue(attr);
|
|
585
|
-
}
|
|
586
|
-
const children = [];
|
|
587
|
-
for (let i = 0; i < element.childNodes.length; i++) {
|
|
588
|
-
const childVNode = domNodeToVNode(element.childNodes[i]);
|
|
589
|
-
children.push(childVNode);
|
|
522
|
+
const longestIncreasingRun = (values) => {
|
|
523
|
+
const n = values.length;
|
|
524
|
+
const marked = new Uint8Array(n);
|
|
525
|
+
const tails = new Int32Array(n);
|
|
526
|
+
const previous = new Int32Array(n);
|
|
527
|
+
let length = 0;
|
|
528
|
+
for (let i = 0; i < n; i++) {
|
|
529
|
+
const value = values[i];
|
|
530
|
+
if (value < 0) continue;
|
|
531
|
+
let low = 0;
|
|
532
|
+
let high = length;
|
|
533
|
+
while (low < high) {
|
|
534
|
+
const mid = low + high >> 1;
|
|
535
|
+
if (values[tails[mid]] < value) low = mid + 1;
|
|
536
|
+
else high = mid;
|
|
590
537
|
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
children
|
|
595
|
-
};
|
|
538
|
+
previous[i] = low > 0 ? tails[low - 1] : -1;
|
|
539
|
+
tails[low] = i;
|
|
540
|
+
if (low === length) length++;
|
|
596
541
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
const parser = new Parser();
|
|
601
|
-
const doc = parser.parseFromString(html, "text/html");
|
|
602
|
-
const vNodes = [];
|
|
603
|
-
for (let i = 0; i < doc.body.childNodes.length; i++) {
|
|
604
|
-
const vnode = domNodeToVNode(doc.body.childNodes[i]);
|
|
605
|
-
if (vnode !== "") {
|
|
606
|
-
vNodes.push(vnode);
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
return vNodes;
|
|
610
|
-
}
|
|
542
|
+
for (let i = length > 0 ? tails[length - 1] : -1; i >= 0; i = previous[i]) marked[i] = 1;
|
|
543
|
+
return marked;
|
|
544
|
+
};
|
|
611
545
|
|
|
612
546
|
const areDomNodesEqual = (oldNode, newNode) => {
|
|
613
547
|
if (oldNode === newNode) return true;
|
|
@@ -625,15 +559,15 @@ const areDomNodesEqual = (oldNode, newNode) => {
|
|
|
625
559
|
if (oldAttr.value !== newAttrValue) return false;
|
|
626
560
|
}
|
|
627
561
|
}
|
|
628
|
-
if (oldNode.nodeType === 3) {
|
|
629
|
-
if (oldNode.
|
|
562
|
+
if (oldNode.nodeType === 3 || oldNode.nodeType === 8) {
|
|
563
|
+
if (oldNode.nodeValue !== newNode.nodeValue) return false;
|
|
630
564
|
}
|
|
631
565
|
return true;
|
|
632
566
|
};
|
|
633
567
|
function isTextLike(value) {
|
|
634
568
|
return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
635
569
|
}
|
|
636
|
-
function isVNode(value) {
|
|
570
|
+
function isVNode$1(value) {
|
|
637
571
|
return Boolean(
|
|
638
572
|
value && typeof value === "object" && "type" in value
|
|
639
573
|
);
|
|
@@ -641,7 +575,7 @@ function isVNode(value) {
|
|
|
641
575
|
function toValidChild(child, lenient = false) {
|
|
642
576
|
if (child == null) return child;
|
|
643
577
|
if (isTextLike(child)) return child;
|
|
644
|
-
if (isVNode(child)) return child;
|
|
578
|
+
if (isVNode$1(child)) return child;
|
|
645
579
|
if (lenient && child && typeof child === "object" && "attributes" in child)
|
|
646
580
|
return child;
|
|
647
581
|
return void 0;
|
|
@@ -655,7 +589,7 @@ function normalizeChildren(input, lenient = false) {
|
|
|
655
589
|
}
|
|
656
590
|
const valid = toValidChild(child, lenient);
|
|
657
591
|
if (typeof valid === "undefined") return;
|
|
658
|
-
if (isVNode(valid) && (valid.type === "fragment" || valid.type === "Fragment")) {
|
|
592
|
+
if (isVNode$1(valid) && (valid.type === "fragment" || valid.type === "Fragment")) {
|
|
659
593
|
const nested = Array.isArray(valid.children) ? valid.children : [];
|
|
660
594
|
nested.forEach(pushChild);
|
|
661
595
|
return;
|
|
@@ -713,6 +647,7 @@ function areNodeAndChildMatching(domNode, child) {
|
|
|
713
647
|
if (typeof child === "string" || typeof child === "number" || typeof child === "boolean") {
|
|
714
648
|
return domNode.nodeType === 3;
|
|
715
649
|
}
|
|
650
|
+
if (isCommentVNode(child)) return domNode.nodeType === 8;
|
|
716
651
|
if (child && typeof child === "object") {
|
|
717
652
|
if (domNode.nodeType !== 1) return false;
|
|
718
653
|
const el = domNode;
|
|
@@ -798,6 +733,11 @@ function patchElementInPlace(el, vnode, globals, mergeAttributes = false) {
|
|
|
798
733
|
return;
|
|
799
734
|
morphDomDirect(el, vnode.children ?? [], globals);
|
|
800
735
|
}
|
|
736
|
+
function replaceNode(old, next) {
|
|
737
|
+
if (old.nodeType === 1)
|
|
738
|
+
clearDelegatedEventsDeep(old);
|
|
739
|
+
old.parentNode?.replaceChild(next, old);
|
|
740
|
+
}
|
|
801
741
|
function morphNode(domNode, child, globals, mergeAttributes = false) {
|
|
802
742
|
if (typeof child === "string" || typeof child === "number" || typeof child === "boolean") {
|
|
803
743
|
const text = String(child);
|
|
@@ -806,7 +746,17 @@ function morphNode(domNode, child, globals, mergeAttributes = false) {
|
|
|
806
746
|
return domNode;
|
|
807
747
|
}
|
|
808
748
|
const next = globals.window.document.createTextNode(text);
|
|
809
|
-
domNode
|
|
749
|
+
replaceNode(domNode, next);
|
|
750
|
+
return next;
|
|
751
|
+
}
|
|
752
|
+
if (isCommentVNode(child)) {
|
|
753
|
+
const data = child.value ?? "";
|
|
754
|
+
if (domNode.nodeType === 8) {
|
|
755
|
+
if (domNode.nodeValue !== data) domNode.nodeValue = data;
|
|
756
|
+
return domNode;
|
|
757
|
+
}
|
|
758
|
+
const next = globals.window.document.createComment(data);
|
|
759
|
+
replaceNode(domNode, next);
|
|
810
760
|
return next;
|
|
811
761
|
}
|
|
812
762
|
if (child && typeof child === "object") {
|
|
@@ -827,13 +777,15 @@ function morphNode(domNode, child, globals, mergeAttributes = false) {
|
|
|
827
777
|
const created = createDomFromChild(child, globals);
|
|
828
778
|
const first = Array.isArray(created) ? created[0] : created;
|
|
829
779
|
if (!first) return null;
|
|
830
|
-
el
|
|
780
|
+
replaceNode(el, first);
|
|
831
781
|
handleLifecycleEventsForOnMount(first);
|
|
832
782
|
return first;
|
|
833
783
|
}
|
|
834
784
|
patchElementInPlace(el, child, globals, mergeAttributes);
|
|
835
785
|
return el;
|
|
836
786
|
}
|
|
787
|
+
if (domNode.nodeType === 1)
|
|
788
|
+
clearDelegatedEventsDeep(domNode);
|
|
837
789
|
domNode.parentNode?.removeChild(domNode);
|
|
838
790
|
return null;
|
|
839
791
|
}
|
|
@@ -888,6 +840,7 @@ function morphDiff(targetRoot, patchItems, globals) {
|
|
|
888
840
|
}
|
|
889
841
|
for (const item of patchItems) {
|
|
890
842
|
if (typeof item === "string" && item.trim() === "") continue;
|
|
843
|
+
if (isCommentVNode(item)) continue;
|
|
891
844
|
const key = getVNodeMatchKey(item);
|
|
892
845
|
if (!key) {
|
|
893
846
|
throw new Error(
|
|
@@ -912,68 +865,130 @@ function morphDiff(targetRoot, patchItems, globals) {
|
|
|
912
865
|
}
|
|
913
866
|
}
|
|
914
867
|
}
|
|
868
|
+
function moveNode(parent, node, anchor) {
|
|
869
|
+
if (typeof parent.moveBefore === "function") {
|
|
870
|
+
try {
|
|
871
|
+
parent.moveBefore(node, anchor);
|
|
872
|
+
return;
|
|
873
|
+
} catch {
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
const doc = node.ownerDocument;
|
|
877
|
+
const active = doc?.activeElement;
|
|
878
|
+
const holdsFocus = !!active && active !== doc.body && node.contains(active);
|
|
879
|
+
let selection;
|
|
880
|
+
if (holdsFocus) {
|
|
881
|
+
try {
|
|
882
|
+
if (active.selectionStart !== null && active.selectionStart !== void 0) {
|
|
883
|
+
selection = [active.selectionStart, active.selectionEnd ?? active.selectionStart, active.selectionDirection ?? "none"];
|
|
884
|
+
}
|
|
885
|
+
} catch {
|
|
886
|
+
selection = void 0;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
parent.insertBefore(node, anchor);
|
|
890
|
+
if (holdsFocus && active.isConnected && doc.activeElement !== active) {
|
|
891
|
+
active.focus({ preventScroll: true });
|
|
892
|
+
if (selection) active.setSelectionRange(...selection);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
function patchByPosition(targetRoot, nextChildren, globals) {
|
|
896
|
+
const live = targetRoot.childNodes;
|
|
897
|
+
if (live.length !== nextChildren.length) return false;
|
|
898
|
+
for (let c = 0; c < nextChildren.length; c++) {
|
|
899
|
+
const node = live[c];
|
|
900
|
+
const child = nextChildren[c];
|
|
901
|
+
if (getVNodeMatchKey(child) !== null || !areNodeAndChildMatching(node, child)) return false;
|
|
902
|
+
if (node.nodeType === 1 && getDomMatchKeys(node).length > 0) return false;
|
|
903
|
+
}
|
|
904
|
+
for (let c = 0; c < nextChildren.length; c++) morphNode(live[c], nextChildren[c], globals);
|
|
905
|
+
return true;
|
|
906
|
+
}
|
|
915
907
|
function morphDomDirect(parentElement, newVDOM, globals, mode = "replace") {
|
|
916
908
|
const el = parentElement;
|
|
917
909
|
const isCustomElement = el.tagName.includes("-");
|
|
918
|
-
const targetRoot = el.shadowRoot && !isCustomElement ? el.shadowRoot : parentElement;
|
|
910
|
+
const targetRoot = templateContent(parentElement) ?? (el.shadowRoot && !isCustomElement ? el.shadowRoot : parentElement);
|
|
919
911
|
const nextChildren = normalizeChildren(newVDOM, mode === "diff");
|
|
920
912
|
if (mode === "diff") {
|
|
921
913
|
morphDiff(targetRoot, nextChildren, globals);
|
|
922
914
|
return;
|
|
923
915
|
}
|
|
916
|
+
if (patchByPosition(targetRoot, nextChildren, globals)) return;
|
|
924
917
|
const existing = Array.from(targetRoot.childNodes);
|
|
925
918
|
const keyedPool = /* @__PURE__ */ new Map();
|
|
926
|
-
const nodeKeys =
|
|
919
|
+
const nodeKeys = [];
|
|
927
920
|
const unkeyedPool = [];
|
|
928
|
-
for (
|
|
929
|
-
const keys = getDomMatchKeys(
|
|
921
|
+
for (let i = 0; i < existing.length; i++) {
|
|
922
|
+
const keys = getDomMatchKeys(existing[i]);
|
|
930
923
|
if (keys.length > 0) {
|
|
931
|
-
nodeKeys
|
|
924
|
+
nodeKeys[i] = keys;
|
|
932
925
|
let addedToKeyedPool = false;
|
|
933
926
|
for (const k of keys) {
|
|
934
927
|
if (!keyedPool.has(k)) {
|
|
935
|
-
keyedPool.set(k,
|
|
928
|
+
keyedPool.set(k, i);
|
|
936
929
|
addedToKeyedPool = true;
|
|
937
930
|
}
|
|
938
931
|
}
|
|
939
932
|
if (!addedToKeyedPool) {
|
|
940
|
-
unkeyedPool.push(
|
|
933
|
+
unkeyedPool.push(i);
|
|
941
934
|
}
|
|
942
935
|
} else {
|
|
943
|
-
unkeyedPool.push(
|
|
936
|
+
unkeyedPool.push(i);
|
|
944
937
|
}
|
|
945
938
|
}
|
|
946
|
-
const consumeKeyedNode = (
|
|
947
|
-
const
|
|
948
|
-
|
|
939
|
+
const consumeKeyedNode = (i) => {
|
|
940
|
+
for (const k of nodeKeys[i] ?? []) {
|
|
941
|
+
if (keyedPool.get(k) === i) keyedPool.delete(k);
|
|
942
|
+
}
|
|
949
943
|
};
|
|
950
944
|
const takeUnkeyedMatch = (child) => {
|
|
951
|
-
for (let
|
|
952
|
-
const
|
|
953
|
-
if (areNodeAndChildMatching(
|
|
954
|
-
unkeyedPool.splice(
|
|
955
|
-
return
|
|
945
|
+
for (let j = 0; j < unkeyedPool.length; j++) {
|
|
946
|
+
const i = unkeyedPool[j];
|
|
947
|
+
if (areNodeAndChildMatching(existing[i], child)) {
|
|
948
|
+
unkeyedPool.splice(j, 1);
|
|
949
|
+
return i;
|
|
956
950
|
}
|
|
957
951
|
}
|
|
958
|
-
return
|
|
952
|
+
return -1;
|
|
959
953
|
};
|
|
960
|
-
|
|
961
|
-
|
|
954
|
+
const count = nextChildren.length;
|
|
955
|
+
const matched = new Int32Array(count);
|
|
956
|
+
let inOrder = true;
|
|
957
|
+
let previous = -1;
|
|
958
|
+
for (let c = 0; c < count; c++) {
|
|
959
|
+
const child = nextChildren[c];
|
|
962
960
|
const key = getVNodeMatchKey(child);
|
|
963
|
-
let
|
|
961
|
+
let i = -1;
|
|
964
962
|
if (key) {
|
|
965
|
-
|
|
966
|
-
if (
|
|
963
|
+
const hit = keyedPool.get(key);
|
|
964
|
+
if (hit !== void 0) {
|
|
965
|
+
i = hit;
|
|
966
|
+
consumeKeyedNode(hit);
|
|
967
|
+
}
|
|
967
968
|
} else {
|
|
968
|
-
|
|
969
|
+
i = takeUnkeyedMatch(child);
|
|
969
970
|
}
|
|
970
|
-
|
|
971
|
-
if (
|
|
972
|
-
if (
|
|
973
|
-
|
|
974
|
-
|
|
971
|
+
matched[c] = i;
|
|
972
|
+
if (i >= 0) {
|
|
973
|
+
if (i < previous) inOrder = false;
|
|
974
|
+
previous = i;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
const stays = inOrder ? null : longestIncreasingRun(matched);
|
|
978
|
+
const staying = (c) => matched[c] >= 0 && (stays === null || stays[c] === 1);
|
|
979
|
+
let anchorAt = 0;
|
|
980
|
+
while (anchorAt < count && !staying(anchorAt)) anchorAt++;
|
|
981
|
+
for (let c = 0; c < count; c++) {
|
|
982
|
+
if (anchorAt <= c) {
|
|
983
|
+
anchorAt = c + 1;
|
|
984
|
+
while (anchorAt < count && !staying(anchorAt)) anchorAt++;
|
|
985
|
+
}
|
|
986
|
+
const anchor = anchorAt < count ? existing[matched[anchorAt]] : null;
|
|
987
|
+
const child = nextChildren[c];
|
|
988
|
+
if (matched[c] >= 0) {
|
|
989
|
+
const match = existing[matched[c]];
|
|
990
|
+
if (!staying(c)) moveNode(targetRoot, match, anchor);
|
|
975
991
|
morphNode(match, child, globals);
|
|
976
|
-
domIndex++;
|
|
977
992
|
continue;
|
|
978
993
|
}
|
|
979
994
|
const created = createDomFromChild(child, globals);
|
|
@@ -982,12 +997,11 @@ function morphDomDirect(parentElement, newVDOM, globals, mode = "replace") {
|
|
|
982
997
|
for (const node of nodes) {
|
|
983
998
|
targetRoot.insertBefore(node, anchor);
|
|
984
999
|
handleLifecycleEventsForOnMount(node);
|
|
985
|
-
domIndex++;
|
|
986
1000
|
}
|
|
987
1001
|
}
|
|
988
1002
|
const remaining = /* @__PURE__ */ new Set();
|
|
989
|
-
for (const
|
|
990
|
-
for (const
|
|
1003
|
+
for (const i of unkeyedPool) remaining.add(existing[i]);
|
|
1004
|
+
for (const i of keyedPool.values()) remaining.add(existing[i]);
|
|
991
1005
|
for (const node of remaining) {
|
|
992
1006
|
if (node.parentNode === targetRoot) {
|
|
993
1007
|
if (node.nodeType === 1) {
|
|
@@ -1000,7 +1014,10 @@ function morphDomDirect(parentElement, newVDOM, globals, mode = "replace") {
|
|
|
1000
1014
|
function replaceDomWithVdom(parentElement, newVDOM, globals) {
|
|
1001
1015
|
const resolvedGlobals = resolveGlobals(parentElement, globals);
|
|
1002
1016
|
while (parentElement.firstChild) {
|
|
1003
|
-
parentElement.
|
|
1017
|
+
const child = parentElement.firstChild;
|
|
1018
|
+
if (child.nodeType === 1)
|
|
1019
|
+
clearDelegatedEventsDeep(child);
|
|
1020
|
+
parentElement.removeChild(child);
|
|
1004
1021
|
}
|
|
1005
1022
|
const renderer = getRenderer(resolvedGlobals.window.document);
|
|
1006
1023
|
const newDom = renderer.createElementOrElements(
|
|
@@ -1104,7 +1121,9 @@ const performCrossfade = async (element, updateCallback, duration, easing) => {
|
|
|
1104
1121
|
const doc = element.ownerDocument;
|
|
1105
1122
|
try {
|
|
1106
1123
|
const rect = element.getBoundingClientRect();
|
|
1107
|
-
|
|
1124
|
+
const top = rect.top + (doc.defaultView?.scrollY ?? 0);
|
|
1125
|
+
const left = rect.left + (doc.defaultView?.scrollX ?? 0);
|
|
1126
|
+
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;`;
|
|
1108
1127
|
element.style.opacity = "0";
|
|
1109
1128
|
element.style.transition = `opacity ${duration}ms ${easing}`;
|
|
1110
1129
|
doc.body.appendChild(snapshot);
|
|
@@ -1180,7 +1199,7 @@ const morph = (el, newContent, options = {}) => {
|
|
|
1180
1199
|
const transitionTarget = config.target === "self" ? el : el.parentElement;
|
|
1181
1200
|
if (!transitionTarget) {
|
|
1182
1201
|
apply(newContent);
|
|
1183
|
-
return;
|
|
1202
|
+
return Promise.resolve();
|
|
1184
1203
|
}
|
|
1185
1204
|
const slot2 = { content: newContent };
|
|
1186
1205
|
inflightTransitions.set(el, slot2);
|
|
@@ -1203,40 +1222,194 @@ const morph = (el, newContent, options = {}) => {
|
|
|
1203
1222
|
apply(newContent);
|
|
1204
1223
|
};
|
|
1205
1224
|
|
|
1225
|
+
const toChildren$1 = (children) => children === void 0 ? [] : Array.isArray(children) ? children.flat(Infinity) : [children];
|
|
1226
|
+
const isVNode = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && "type" in value;
|
|
1227
|
+
const Fragment = (props) => toChildren$1(props?.children);
|
|
1228
|
+
function jsx(type, props, key) {
|
|
1229
|
+
const { children, ...attributes } = props ?? {};
|
|
1230
|
+
const resolvedKey = key ?? attributes.key;
|
|
1231
|
+
if (resolvedKey !== void 0) attributes.key = resolvedKey;
|
|
1232
|
+
if (typeof type === "function") {
|
|
1233
|
+
const result = type(children === void 0 ? attributes : { ...attributes, children });
|
|
1234
|
+
return resolvedKey !== void 0 && isVNode(result) && result.attributes?.key === void 0 ? { ...result, attributes: { ...result.attributes, key: resolvedKey } } : result;
|
|
1235
|
+
}
|
|
1236
|
+
return { type, attributes, children: toChildren$1(children) };
|
|
1237
|
+
}
|
|
1238
|
+
const jsxs = jsx;
|
|
1239
|
+
const jsxDEV = (type, props, key) => jsx(type, props, key);
|
|
1240
|
+
function h(type, props, ...children) {
|
|
1241
|
+
return jsx(type, children.length === 0 ? props : { ...props, children: children.length === 1 ? children[0] : children });
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
const createRef = (state) => ({
|
|
1245
|
+
current: null,
|
|
1246
|
+
state
|
|
1247
|
+
});
|
|
1248
|
+
const isRef = (value) => typeof value === "object" && value !== null && "current" in value;
|
|
1249
|
+
|
|
1250
|
+
const isElement = (value) => typeof value === "object" && value !== null && value.nodeType === 1;
|
|
1251
|
+
const toStores = (store, caller) => {
|
|
1252
|
+
const stores = Array.isArray(store) ? store : [store];
|
|
1253
|
+
stores.forEach((candidate, index) => {
|
|
1254
|
+
if (typeof candidate?.subscribe !== "function") {
|
|
1255
|
+
throw new TypeError(`${caller}: store${Array.isArray(store) ? ` ${index}` : ""} has no subscribe() method`);
|
|
1256
|
+
}
|
|
1257
|
+
});
|
|
1258
|
+
return stores;
|
|
1259
|
+
};
|
|
1260
|
+
const listen = (stores, update) => {
|
|
1261
|
+
const unsubscribes = stores.map((store) => {
|
|
1262
|
+
const result = store.subscribe(() => update());
|
|
1263
|
+
if (typeof result === "function") return result;
|
|
1264
|
+
return () => result?.unsubscribe?.();
|
|
1265
|
+
});
|
|
1266
|
+
return () => {
|
|
1267
|
+
for (const unsubscribe of unsubscribes) unsubscribe();
|
|
1268
|
+
};
|
|
1269
|
+
};
|
|
1270
|
+
const resolveTarget = (target) => {
|
|
1271
|
+
if (isElement(target)) return () => [target];
|
|
1272
|
+
if (isRef(target)) {
|
|
1273
|
+
if (!isElement(target.current)) {
|
|
1274
|
+
throw new TypeError(
|
|
1275
|
+
"reactive: the ref has no element yet; call reactive() after the element is rendered (in onMount or later)"
|
|
1276
|
+
);
|
|
1277
|
+
}
|
|
1278
|
+
return () => isElement(target.current) ? [target.current] : [];
|
|
1279
|
+
}
|
|
1280
|
+
if (target && typeof target.length === "number") {
|
|
1281
|
+
const elements = Array.from(target).filter(isElement);
|
|
1282
|
+
return () => elements;
|
|
1283
|
+
}
|
|
1284
|
+
throw new TypeError("reactive: the target must be an element, a ref or a list of elements");
|
|
1285
|
+
};
|
|
1286
|
+
function reactive(config, target) {
|
|
1287
|
+
const stores = toStores(config?.store, "reactive");
|
|
1288
|
+
if (typeof config.render !== "function") throw new TypeError("reactive: config.render must be a function");
|
|
1289
|
+
const elements = resolveTarget(target);
|
|
1290
|
+
if (config.signal?.aborted || elements().length === 0) return () => {
|
|
1291
|
+
};
|
|
1292
|
+
const update = () => {
|
|
1293
|
+
for (const el of elements()) morph(el, config.render());
|
|
1294
|
+
};
|
|
1295
|
+
update();
|
|
1296
|
+
const unsubscribe = listen(stores, update);
|
|
1297
|
+
let disposed = false;
|
|
1298
|
+
const dispose = () => {
|
|
1299
|
+
if (disposed) return;
|
|
1300
|
+
disposed = true;
|
|
1301
|
+
unsubscribe();
|
|
1302
|
+
config.signal?.removeEventListener("abort", dispose);
|
|
1303
|
+
config.cleanup?.();
|
|
1304
|
+
};
|
|
1305
|
+
config.signal?.addEventListener("abort", dispose, { once: true });
|
|
1306
|
+
return dispose;
|
|
1307
|
+
}
|
|
1308
|
+
const mounted = /* @__PURE__ */ new WeakMap();
|
|
1309
|
+
const toChildren = (content) => {
|
|
1310
|
+
if (typeof content === "string") {
|
|
1311
|
+
if (!/[<&]/.test(content)) return content === "" ? [] : [content];
|
|
1312
|
+
const Parser = globalThis.DOMParser;
|
|
1313
|
+
if (!Parser) {
|
|
1314
|
+
throw new TypeError("Reactive: rendering an HTML string needs a global DOMParser; return JSX/VNodes, or call reactive()");
|
|
1315
|
+
}
|
|
1316
|
+
return htmlStringToVNodes(content, Parser);
|
|
1317
|
+
}
|
|
1318
|
+
return Array.isArray(content) ? content.flat(Infinity) : [content];
|
|
1319
|
+
};
|
|
1320
|
+
function Reactive(props) {
|
|
1321
|
+
const { store, render, cleanup, tag = "div", onMount, onUnmount, children: _children, ...attributes } = props;
|
|
1322
|
+
const stores = toStores(store, "Reactive");
|
|
1323
|
+
if (typeof render !== "function") throw new TypeError("Reactive: the render prop must be a function");
|
|
1324
|
+
return {
|
|
1325
|
+
type: tag,
|
|
1326
|
+
attributes: {
|
|
1327
|
+
...attributes,
|
|
1328
|
+
// morph runs onMount in a microtask after inserting the wrapper, and again after every patch of it by a
|
|
1329
|
+
// parent re-render: the subscription moves to this render's stores and render function, and one render here
|
|
1330
|
+
// picks up a store change made between the render that built the wrapper and this callback. VERIFIED:
|
|
1331
|
+
// reactive.test.ts ("picks up a store change made before its mount callback ran", "keeps one subscription")
|
|
1332
|
+
onMount: (el) => {
|
|
1333
|
+
mounted.get(el)?.unsubscribe();
|
|
1334
|
+
mounted.set(el, { unsubscribe: listen(stores, () => morph(el, render())), cleanup });
|
|
1335
|
+
morph(el, render());
|
|
1336
|
+
if (typeof onMount === "function") onMount(el);
|
|
1337
|
+
},
|
|
1338
|
+
onUnmount: (el) => {
|
|
1339
|
+
const entry = mounted.get(el);
|
|
1340
|
+
mounted.delete(el);
|
|
1341
|
+
entry?.unsubscribe();
|
|
1342
|
+
entry?.cleanup?.();
|
|
1343
|
+
if (typeof onUnmount === "function") onUnmount(el);
|
|
1344
|
+
}
|
|
1345
|
+
},
|
|
1346
|
+
children: toChildren(render())
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
const installReactive = (df$) => {
|
|
1350
|
+
const fn = df$?.fn;
|
|
1351
|
+
if (typeof fn !== "object" || fn === null) {
|
|
1352
|
+
throw new TypeError("installReactive: expected defuss-query's df$ (a df$ with a df$.fn prototype)");
|
|
1353
|
+
}
|
|
1354
|
+
Object.defineProperty(fn, "reactive", {
|
|
1355
|
+
configurable: true,
|
|
1356
|
+
writable: true,
|
|
1357
|
+
value: function(config) {
|
|
1358
|
+
reactive(config, this);
|
|
1359
|
+
return this;
|
|
1360
|
+
}
|
|
1361
|
+
});
|
|
1362
|
+
return df$;
|
|
1363
|
+
};
|
|
1364
|
+
|
|
1206
1365
|
exports.CAPTURE_ONLY_EVENTS = CAPTURE_ONLY_EVENTS;
|
|
1207
1366
|
exports.CLASS_ATTRIBUTE_NAME = CLASS_ATTRIBUTE_NAME;
|
|
1367
|
+
exports.COMMENT_TYPE = COMMENT_TYPE;
|
|
1208
1368
|
exports.DANGEROUSLY_SET_INNER_HTML_ATTRIBUTE = DANGEROUSLY_SET_INNER_HTML_ATTRIBUTE;
|
|
1209
1369
|
exports.DEFAULT_TRANSITION_CONFIG = DEFAULT_TRANSITION_CONFIG;
|
|
1210
1370
|
exports.FROM_DOM_MARKER = FROM_DOM_MARKER;
|
|
1371
|
+
exports.Fragment = Fragment;
|
|
1211
1372
|
exports.REF_ATTRIBUTE_NAME = REF_ATTRIBUTE_NAME;
|
|
1373
|
+
exports.Reactive = Reactive;
|
|
1212
1374
|
exports.XLINK_ATTRIBUTE_NAME = XLINK_ATTRIBUTE_NAME;
|
|
1213
1375
|
exports.XMLNS_ATTRIBUTE_NAME = XMLNS_ATTRIBUTE_NAME;
|
|
1214
1376
|
exports.applyStyles = applyStyles;
|
|
1215
1377
|
exports.areDomNodesEqual = areDomNodesEqual;
|
|
1216
1378
|
exports.clearDelegatedEvents = clearDelegatedEvents;
|
|
1217
1379
|
exports.clearDelegatedEventsDeep = clearDelegatedEventsDeep;
|
|
1380
|
+
exports.createRef = createRef;
|
|
1218
1381
|
exports.domNodeToVNode = domNodeToVNode;
|
|
1219
1382
|
exports.getMimeType = getMimeType;
|
|
1220
1383
|
exports.getRegisteredEventKeys = getRegisteredEventKeys;
|
|
1221
1384
|
exports.getRegisteredEventTypes = getRegisteredEventTypes;
|
|
1222
1385
|
exports.getRenderer = getRenderer;
|
|
1223
1386
|
exports.getTransitionStyles = getTransitionStyles;
|
|
1387
|
+
exports.h = h;
|
|
1224
1388
|
exports.handleLifecycleEventsForOnMount = handleLifecycleEventsForOnMount;
|
|
1225
1389
|
exports.htmlStringToVNodes = htmlStringToVNodes;
|
|
1390
|
+
exports.installReactive = installReactive;
|
|
1391
|
+
exports.isCommentVNode = isCommentVNode;
|
|
1226
1392
|
exports.isHTML = isHTML;
|
|
1227
1393
|
exports.isMarkup = isMarkup;
|
|
1394
|
+
exports.isRef = isRef;
|
|
1228
1395
|
exports.isSVG = isSVG;
|
|
1396
|
+
exports.jsx = jsx;
|
|
1397
|
+
exports.jsxDEV = jsxDEV;
|
|
1398
|
+
exports.jsxs = jsxs;
|
|
1229
1399
|
exports.morph = morph;
|
|
1230
1400
|
exports.nsMap = nsMap;
|
|
1231
1401
|
exports.observeUnmount = observeUnmount;
|
|
1402
|
+
exports.onClearDelegatedEvents = onClearDelegatedEvents;
|
|
1232
1403
|
exports.parseDOM = parseDOM;
|
|
1233
1404
|
exports.parseEventPropName = parseEventPropName;
|
|
1234
1405
|
exports.performTransition = performTransition;
|
|
1235
1406
|
exports.queueCallback = queueCallback;
|
|
1407
|
+
exports.reactive = reactive;
|
|
1236
1408
|
exports.registerDelegatedEvent = registerDelegatedEvent;
|
|
1237
1409
|
exports.removeDelegatedEvent = removeDelegatedEvent;
|
|
1238
1410
|
exports.removeDelegatedEventByKey = removeDelegatedEventByKey;
|
|
1239
1411
|
exports.renderMarkup = renderMarkup;
|
|
1240
1412
|
exports.replaceDomWithVdom = replaceDomWithVdom;
|
|
1241
1413
|
exports.resolveGlobals = resolveGlobals;
|
|
1414
|
+
exports.templateContent = templateContent;
|
|
1242
1415
|
exports.updateDomWithVdom = updateDomWithVdom;
|