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