@jasonshimmy/custom-elements-runtime 0.0.1-beta.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/README.md +122 -0
- package/dist/build-tools.d.ts +71 -0
- package/dist/computed-state.d.ts +10 -0
- package/dist/custom-elements-runtime.cjs.js +46 -0
- package/dist/custom-elements-runtime.cjs.js.map +1 -0
- package/dist/custom-elements-runtime.es.js +1511 -0
- package/dist/custom-elements-runtime.es.js.map +1 -0
- package/dist/custom-elements-runtime.umd.js +46 -0
- package/dist/custom-elements-runtime.umd.js.map +1 -0
- package/dist/data-binding.d.ts +8 -0
- package/dist/dev-tools.d.ts +21 -0
- package/dist/event-bus.d.ts +101 -0
- package/dist/runtime.d.ts +109 -0
- package/dist/ssr.d.ts +49 -0
- package/dist/store.d.ts +10 -0
- package/dist/template-compiler.d.ts +117 -0
- package/dist/template-helpers.d.ts +56 -0
- package/dist/v-dom.d.ts +54 -0
- package/package.json +57 -0
|
@@ -0,0 +1,1511 @@
|
|
|
1
|
+
class Q {
|
|
2
|
+
state;
|
|
3
|
+
listeners = [];
|
|
4
|
+
constructor(t) {
|
|
5
|
+
this.state = new Proxy(t, {
|
|
6
|
+
set: (e, n, a) => (e[n] = a, this.notify(), !0)
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
subscribe(t) {
|
|
10
|
+
this.listeners.push(t), t(this.state);
|
|
11
|
+
}
|
|
12
|
+
getState() {
|
|
13
|
+
return this.state;
|
|
14
|
+
}
|
|
15
|
+
notify() {
|
|
16
|
+
this.listeners.forEach((t) => t(this.state));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
class y extends EventTarget {
|
|
20
|
+
handlers = {};
|
|
21
|
+
static instance;
|
|
22
|
+
eventCounters = /* @__PURE__ */ new Map();
|
|
23
|
+
/**
|
|
24
|
+
* Returns the singleton instance of GlobalEventBus
|
|
25
|
+
*/
|
|
26
|
+
static getInstance() {
|
|
27
|
+
return y.instance || (y.instance = new y()), y.instance;
|
|
28
|
+
}
|
|
29
|
+
// Enhanced emit method with better typing and event storm protection
|
|
30
|
+
/**
|
|
31
|
+
* Emit a global event with optional data. Includes event storm protection.
|
|
32
|
+
* @param eventName - Name of the event
|
|
33
|
+
* @param data - Optional event payload
|
|
34
|
+
*/
|
|
35
|
+
emit(t, e) {
|
|
36
|
+
const n = Date.now(), a = this.eventCounters.get(t);
|
|
37
|
+
if (!a || n - a.window > 1e3)
|
|
38
|
+
this.eventCounters.set(t, { count: 1, window: n });
|
|
39
|
+
else if (a.count++, a.count > 50 && (console.error(`Event storm detected for "${t}": ${a.count} events in 1 second. Throttling...`), a.count > 100)) {
|
|
40
|
+
console.warn(`Blocking further "${t}" events to prevent infinite loop`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
this.dispatchEvent(new CustomEvent(t, {
|
|
44
|
+
detail: e,
|
|
45
|
+
bubbles: !1,
|
|
46
|
+
// Global events don't need to bubble
|
|
47
|
+
cancelable: !0
|
|
48
|
+
}));
|
|
49
|
+
const o = this.handlers[t];
|
|
50
|
+
o && o.forEach((s) => {
|
|
51
|
+
try {
|
|
52
|
+
s(e);
|
|
53
|
+
} catch (i) {
|
|
54
|
+
console.error(`Error in global event handler for "${t}":`, i);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Register a handler for a global event. Returns an unsubscribe function.
|
|
60
|
+
* @param eventName - Name of the event
|
|
61
|
+
* @param handler - Handler function
|
|
62
|
+
*/
|
|
63
|
+
on(t, e) {
|
|
64
|
+
return this.handlers[t] || (this.handlers[t] = /* @__PURE__ */ new Set()), this.handlers[t].add(e), () => this.off(t, e);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Remove a specific handler for a global event.
|
|
68
|
+
* @param eventName - Name of the event
|
|
69
|
+
* @param handler - Handler function to remove
|
|
70
|
+
*/
|
|
71
|
+
off(t, e) {
|
|
72
|
+
const n = this.handlers[t];
|
|
73
|
+
n && n.delete(e);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Remove all handlers for a specific event.
|
|
77
|
+
* @param eventName - Name of the event
|
|
78
|
+
*/
|
|
79
|
+
offAll(t) {
|
|
80
|
+
delete this.handlers[t];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Listen for a native CustomEvent. Returns an unsubscribe function.
|
|
84
|
+
* @param eventName - Name of the event
|
|
85
|
+
* @param handler - CustomEvent handler
|
|
86
|
+
* @param options - AddEventListener options
|
|
87
|
+
*/
|
|
88
|
+
listen(t, e, n) {
|
|
89
|
+
return this.addEventListener(t, e, n), () => this.removeEventListener(t, e);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Register a one-time event handler. Returns a promise that resolves with the event data.
|
|
93
|
+
* @param eventName - Name of the event
|
|
94
|
+
* @param handler - Handler function
|
|
95
|
+
*/
|
|
96
|
+
once(t, e) {
|
|
97
|
+
return new Promise((n) => {
|
|
98
|
+
const a = this.on(t, (o) => {
|
|
99
|
+
a(), e(o), n(o);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get a list of all active event names with registered handlers.
|
|
105
|
+
*/
|
|
106
|
+
getActiveEvents() {
|
|
107
|
+
return Object.keys(this.handlers).filter(
|
|
108
|
+
(t) => this.handlers[t] && this.handlers[t].size > 0
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Clear all event handlers (useful for testing or cleanup).
|
|
113
|
+
*/
|
|
114
|
+
clear() {
|
|
115
|
+
this.handlers = {};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Get the number of handlers registered for a specific event.
|
|
119
|
+
* @param eventName - Name of the event
|
|
120
|
+
*/
|
|
121
|
+
getHandlerCount(t) {
|
|
122
|
+
return this.handlers[t]?.size || 0;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get event statistics for debugging.
|
|
126
|
+
*/
|
|
127
|
+
getEventStats() {
|
|
128
|
+
const t = {};
|
|
129
|
+
for (const [e, n] of this.eventCounters.entries())
|
|
130
|
+
t[e] = {
|
|
131
|
+
count: n.count,
|
|
132
|
+
handlersCount: this.getHandlerCount(e)
|
|
133
|
+
};
|
|
134
|
+
return t;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Reset event counters (useful for testing or after resolving issues).
|
|
138
|
+
*/
|
|
139
|
+
resetEventCounters() {
|
|
140
|
+
this.eventCounters.clear();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const C = y.getInstance(), z = typeof window > "u" || typeof document > "u";
|
|
144
|
+
function q(r) {
|
|
145
|
+
return {
|
|
146
|
+
state: r,
|
|
147
|
+
emit: () => {
|
|
148
|
+
},
|
|
149
|
+
// No-op on server
|
|
150
|
+
onGlobal: () => () => {
|
|
151
|
+
},
|
|
152
|
+
offGlobal: () => {
|
|
153
|
+
},
|
|
154
|
+
emitGlobal: () => {
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function W(r, t = {}) {
|
|
159
|
+
z || console.warn("[SSR] renderToString should only be used on the server");
|
|
160
|
+
try {
|
|
161
|
+
const e = r.state, n = q(e), a = r.template(e, n);
|
|
162
|
+
let o = "";
|
|
163
|
+
t.includeStyles && r.style && (o = `<style>${typeof r.style == "function" ? r.style(e) : r.style}</style>`);
|
|
164
|
+
const s = t.sanitizeAttributes ? t.sanitizeAttributes(r.attrs || {}) : r.attrs || {}, i = Object.entries(s).map(([d, u]) => `${H(d)}="${H(u)}"`).join(" "), c = `${i ? `<${r.tag} ${i}>` : `<${r.tag}>`}${o}${a}</${r.tag}>`;
|
|
165
|
+
return t.prettyPrint ? K(c) : c;
|
|
166
|
+
} catch (e) {
|
|
167
|
+
return console.error(`[SSR] Error rendering ${r.tag}:`, e), `<${r.tag}><div style="color: red;">SSR Error: ${F(String(e))}</div></${r.tag}>`;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function tt(r, t = {}) {
|
|
171
|
+
const e = {
|
|
172
|
+
components: /* @__PURE__ */ new Map(),
|
|
173
|
+
styles: /* @__PURE__ */ new Set()
|
|
174
|
+
}, n = [];
|
|
175
|
+
r.forEach((s) => {
|
|
176
|
+
if (e.components.set(s.tag, s), s.style) {
|
|
177
|
+
const l = typeof s.style == "function" ? s.style(s.state) : s.style;
|
|
178
|
+
e.styles.add(l);
|
|
179
|
+
}
|
|
180
|
+
const i = W(s, { ...t, includeStyles: !1 });
|
|
181
|
+
n.push(i);
|
|
182
|
+
});
|
|
183
|
+
const a = Array.from(e.styles).join(`
|
|
184
|
+
`);
|
|
185
|
+
return { html: n.join(`
|
|
186
|
+
`), styles: a, context: e };
|
|
187
|
+
}
|
|
188
|
+
function et(r) {
|
|
189
|
+
const t = Array.from(r.components.entries()).map(([e, n]) => ({
|
|
190
|
+
tag: e,
|
|
191
|
+
state: n.state
|
|
192
|
+
}));
|
|
193
|
+
return `
|
|
194
|
+
<script type="module">
|
|
195
|
+
// Hydration data from SSR
|
|
196
|
+
window.__SSR_CONTEXT__ = ${JSON.stringify({ components: t })};
|
|
197
|
+
|
|
198
|
+
// Auto-hydrate when DOM is ready
|
|
199
|
+
if (document.readyState === 'loading') {
|
|
200
|
+
document.addEventListener('DOMContentLoaded', hydrate);
|
|
201
|
+
} else {
|
|
202
|
+
hydrate();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function hydrate() {
|
|
206
|
+
const context = window.__SSR_CONTEXT__;
|
|
207
|
+
if (!context?.components) return;
|
|
208
|
+
|
|
209
|
+
context.components.forEach(({ tag, state }) => {
|
|
210
|
+
const elements = document.querySelectorAll(tag);
|
|
211
|
+
elements.forEach(el => {
|
|
212
|
+
// Mark as hydrated to prevent re-initialization
|
|
213
|
+
if (!el.hasAttribute('data-hydrated')) {
|
|
214
|
+
el.setAttribute('data-hydrated', 'true');
|
|
215
|
+
// Restore state if component supports it
|
|
216
|
+
if (el._hydrateWithState) {
|
|
217
|
+
el._hydrateWithState(state);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Clean up
|
|
224
|
+
delete window.__SSR_CONTEXT__;
|
|
225
|
+
}
|
|
226
|
+
<\/script>`.trim();
|
|
227
|
+
}
|
|
228
|
+
const F = (r) => r.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'"), H = (r) => r.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">"), K = (r) => r.replace(/></g, `>
|
|
229
|
+
<`).split(`
|
|
230
|
+
`).map((t) => {
|
|
231
|
+
const e = (t.match(/^<[^\/]/g) || []).length - (t.match(/<\//g) || []).length;
|
|
232
|
+
return " ".repeat(Math.max(0, e)) + t.trim();
|
|
233
|
+
}).join(`
|
|
234
|
+
`);
|
|
235
|
+
function $(r) {
|
|
236
|
+
return String(r).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
237
|
+
}
|
|
238
|
+
function w(r, t) {
|
|
239
|
+
if (typeof r != "string" || !t) return String(r);
|
|
240
|
+
for (const e in t) {
|
|
241
|
+
if (typeof t[e] == "string" && r === t[e])
|
|
242
|
+
return $(r);
|
|
243
|
+
if (Array.isArray(t[e])) {
|
|
244
|
+
for (const n of t[e])
|
|
245
|
+
if (n && typeof n == "object") {
|
|
246
|
+
for (const a in n)
|
|
247
|
+
if (typeof n[a] == "string" && r === n[a])
|
|
248
|
+
return $(r);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return String(r);
|
|
253
|
+
}
|
|
254
|
+
function nt(r, ...t) {
|
|
255
|
+
function e(n, a, o) {
|
|
256
|
+
if (Array.isArray(n)) {
|
|
257
|
+
const s = n.map((i) => e(i, a, o));
|
|
258
|
+
return s.some((i) => i instanceof Promise) ? Promise.all(s).then((i) => i.join("")) : s.join("");
|
|
259
|
+
}
|
|
260
|
+
if (typeof n == "function") {
|
|
261
|
+
const s = e(n(a, o), a, o);
|
|
262
|
+
return s instanceof Promise, s;
|
|
263
|
+
}
|
|
264
|
+
return n == null ? "" : n instanceof Promise ? n : String(n);
|
|
265
|
+
}
|
|
266
|
+
return (n, a) => {
|
|
267
|
+
let o = "", s = !1;
|
|
268
|
+
const i = [];
|
|
269
|
+
for (let l = 0; l < r.length; l++)
|
|
270
|
+
if (o += r[l], l < t.length) {
|
|
271
|
+
let c = t[l];
|
|
272
|
+
const d = r[l], u = /data-on-[a-z]+="?$/.test(d);
|
|
273
|
+
c = e(c, n, a), c instanceof Promise ? (s = !0, i.push(c)) : /=\s*"?$/.test(d) && typeof c == "string" && !u ? (c = c.replace(/"/g, """).replace(/'/g, "'"), o += c) : !u && !/=\s*"?$/.test(d) ? o += w(c, n) : o += c;
|
|
274
|
+
}
|
|
275
|
+
return s ? Promise.all(i).then((l) => {
|
|
276
|
+
let c = "", d = 0;
|
|
277
|
+
for (let u = 0; u < r.length; u++)
|
|
278
|
+
if (c += r[u], u < t.length) {
|
|
279
|
+
let f = t[u];
|
|
280
|
+
const h = r[u], m = /data-on-[a-z]+="?$/.test(h);
|
|
281
|
+
f = e(f, n, a), f instanceof Promise ? c += l[d++] : /=\s*"?$/.test(h) && typeof f == "string" && !m ? (f = f.replace(/"/g, """).replace(/'/g, "'"), c += f) : !m && !/=\s*"?$/.test(h) ? c += w(f, n) : c += f;
|
|
282
|
+
}
|
|
283
|
+
return c;
|
|
284
|
+
}) : o;
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function rt(r, ...t) {
|
|
288
|
+
const e = "compiled-" + Math.random().toString(36).slice(2);
|
|
289
|
+
function n(o, s, i) {
|
|
290
|
+
return Array.isArray(o) ? o.map((l) => n(l, s, i)).join("") : typeof o == "function" ? n(o(s, i), s, i) : o == null ? "" : String(o);
|
|
291
|
+
}
|
|
292
|
+
const a = (o, s) => {
|
|
293
|
+
let i = "";
|
|
294
|
+
for (let l = 0; l < r.length; l++)
|
|
295
|
+
if (i += r[l], l < t.length) {
|
|
296
|
+
let c = t[l];
|
|
297
|
+
const d = r[l], u = /data-on-[a-z]+="?$/.test(d);
|
|
298
|
+
c = n(c, o, s), /=\s*"?$/.test(d) && typeof c == "string" && !u ? (c = c.replace(/"/g, """).replace(/'/g, "'"), i += c) : !u && !/=\s*"?$/.test(d) ? i += w(c, o) : i += c ?? "";
|
|
299
|
+
}
|
|
300
|
+
return i;
|
|
301
|
+
};
|
|
302
|
+
return a.id = e, a;
|
|
303
|
+
}
|
|
304
|
+
function st(r, ...t) {
|
|
305
|
+
let e = "";
|
|
306
|
+
for (let n = 0; n < r.length; n++)
|
|
307
|
+
e += r[n], n < t.length && (e += t[n] ?? "");
|
|
308
|
+
return e;
|
|
309
|
+
}
|
|
310
|
+
function it(r) {
|
|
311
|
+
return r;
|
|
312
|
+
}
|
|
313
|
+
function ot(r, t) {
|
|
314
|
+
return { [r]: t };
|
|
315
|
+
}
|
|
316
|
+
function at(r) {
|
|
317
|
+
return Object.keys(r).filter((t) => r[t]).join(" ");
|
|
318
|
+
}
|
|
319
|
+
function ct(r) {
|
|
320
|
+
return Object.entries(r).map(([t, e]) => `${t}: ${e}`).join("; ");
|
|
321
|
+
}
|
|
322
|
+
function G(r, t, e) {
|
|
323
|
+
const [n, ...a] = e.split("|").map((i) => i.trim());
|
|
324
|
+
if (!n || n === "__proto__" || n === "constructor" || n === "prototype") return;
|
|
325
|
+
function o(i, l, c) {
|
|
326
|
+
const d = l.split(".");
|
|
327
|
+
let u = i;
|
|
328
|
+
for (let f = 0; f < d.length - 1; f++)
|
|
329
|
+
d[f] in u || (u[d[f]] = {}), u = u[d[f]];
|
|
330
|
+
u[d[d.length - 1]] = c;
|
|
331
|
+
}
|
|
332
|
+
const s = (i) => {
|
|
333
|
+
let l;
|
|
334
|
+
if (r instanceof HTMLInputElement && r.type === "checkbox") {
|
|
335
|
+
l = r.value;
|
|
336
|
+
const c = r.getAttribute("data-true-value"), d = r.getAttribute("data-false-value");
|
|
337
|
+
let u = Array.isArray(t[n]) ? t[n] : void 0;
|
|
338
|
+
if (u) {
|
|
339
|
+
if (r.checked)
|
|
340
|
+
u.includes(l) || u.push(l);
|
|
341
|
+
else {
|
|
342
|
+
const f = u.indexOf(l);
|
|
343
|
+
f !== -1 && u.splice(f, 1);
|
|
344
|
+
}
|
|
345
|
+
o(t, n, [...u]);
|
|
346
|
+
} else
|
|
347
|
+
c !== null || d !== null ? r.checked ? o(t, n, c) : o(t, n, d !== null ? d : !1) : o(t, n, r.checked);
|
|
348
|
+
} else r instanceof HTMLInputElement && r.type === "radio" ? (l = r.value, o(t, n, l), ((r.form || r.closest("form") || r.getRootNode()) instanceof Element ? (r.form || r.closest("form") || r.getRootNode()).querySelectorAll(`input[type="radio"][name="${r.name}"][data-model="${e}"]`) : []).forEach((d) => {
|
|
349
|
+
d.checked = d.value === String(l);
|
|
350
|
+
})) : (l = r.value, r instanceof HTMLInputElement && r.type === "number" && (l = Number(l)), a.includes("trim") && typeof l == "string" && (l = l.trim()), a.includes("number") && (l = Number(l)), o(t, n, l));
|
|
351
|
+
if ("_vnode" in r && typeof r._vnode == "object" && r._vnode?.props && (r._vnode.props.value = l), i.type === "input" && (r._isDirty = !0), i.type === "keydown" && i.key === "Enter" && (r._isDirty = !1, r instanceof HTMLElement && r.isConnected)) {
|
|
352
|
+
let c = r.parentElement;
|
|
353
|
+
for (; c && !(c instanceof HTMLElement && c.shadowRoot); )
|
|
354
|
+
c = c.parentElement;
|
|
355
|
+
c && typeof c == "object" && c !== null && "render" in c && typeof c.render == "function" && c.render();
|
|
356
|
+
}
|
|
357
|
+
i.type === "blur" && (r._isDirty = !1);
|
|
358
|
+
};
|
|
359
|
+
r.addEventListener("input", s), r.addEventListener("change", s), r.addEventListener("keydown", s), r.addEventListener("blur", s);
|
|
360
|
+
}
|
|
361
|
+
const R = (() => {
|
|
362
|
+
try {
|
|
363
|
+
if (typeof process < "u" && process.env)
|
|
364
|
+
return process.env.NODE_ENV === "development";
|
|
365
|
+
} catch {
|
|
366
|
+
}
|
|
367
|
+
return typeof window < "u" ? window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1" : !1;
|
|
368
|
+
})();
|
|
369
|
+
function lt(r, t = {}) {
|
|
370
|
+
const { development: e = R, cache: n = !0, optimize: a = !0 } = t, o = N(r);
|
|
371
|
+
if (n && T.has(o)) {
|
|
372
|
+
if (e) {
|
|
373
|
+
const s = v.get(o) || {
|
|
374
|
+
compilationTime: 0,
|
|
375
|
+
renderTime: 0,
|
|
376
|
+
updateTime: 0,
|
|
377
|
+
cacheHits: 0,
|
|
378
|
+
cacheMisses: 0
|
|
379
|
+
};
|
|
380
|
+
s.cacheHits++, v.set(o, s);
|
|
381
|
+
}
|
|
382
|
+
return T.get(o);
|
|
383
|
+
}
|
|
384
|
+
if (e) {
|
|
385
|
+
const s = v.get(o) || {
|
|
386
|
+
compilationTime: 0,
|
|
387
|
+
renderTime: 0,
|
|
388
|
+
updateTime: 0,
|
|
389
|
+
cacheHits: 0,
|
|
390
|
+
cacheMisses: 0
|
|
391
|
+
};
|
|
392
|
+
s.cacheMisses++, v.set(o, s);
|
|
393
|
+
}
|
|
394
|
+
try {
|
|
395
|
+
const s = Z(r, { development: e, optimize: a });
|
|
396
|
+
return n && T.set(o, s), s;
|
|
397
|
+
} catch (s) {
|
|
398
|
+
return e && (console.error("[Template Compiler] Error compiling template:", s), console.error("[Template Compiler] Template:", r)), {
|
|
399
|
+
statics: [r],
|
|
400
|
+
dynamics: [],
|
|
401
|
+
fragment: null,
|
|
402
|
+
id: o,
|
|
403
|
+
hasDynamics: !1,
|
|
404
|
+
render: () => r
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
function U(r, t) {
|
|
409
|
+
if (typeof document > "u")
|
|
410
|
+
return [0];
|
|
411
|
+
try {
|
|
412
|
+
let e = function(i, l = []) {
|
|
413
|
+
if (i.nodeType === Node.TEXT_NODE) {
|
|
414
|
+
if (i.textContent?.includes(t))
|
|
415
|
+
return l;
|
|
416
|
+
} else if (i.nodeType === Node.ELEMENT_NODE) {
|
|
417
|
+
let c = 0;
|
|
418
|
+
for (let d = 0; d < i.childNodes.length; d++) {
|
|
419
|
+
const u = i.childNodes[d], f = e(u, [...l, c]);
|
|
420
|
+
if (f)
|
|
421
|
+
return f;
|
|
422
|
+
c++;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return null;
|
|
426
|
+
};
|
|
427
|
+
const o = new DOMParser().parseFromString(`<div>${r}</div>`, "text/html").body.firstElementChild;
|
|
428
|
+
return e(o) || [0];
|
|
429
|
+
} catch (e) {
|
|
430
|
+
return R && console.warn("[Template Compiler] Error finding DOM path for placeholder:", t, e), [0];
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
function Z(r, t) {
|
|
434
|
+
return new B(r, t).compile();
|
|
435
|
+
}
|
|
436
|
+
class B {
|
|
437
|
+
template;
|
|
438
|
+
options;
|
|
439
|
+
dynamics = [];
|
|
440
|
+
statics = [];
|
|
441
|
+
constructor(t, e) {
|
|
442
|
+
this.template = t, this.options = e;
|
|
443
|
+
}
|
|
444
|
+
compile() {
|
|
445
|
+
this.parseTemplate();
|
|
446
|
+
const t = this.createStaticFragment(), e = N(this.template), n = (a, o) => {
|
|
447
|
+
let s = "";
|
|
448
|
+
for (let i = 0; i < this.statics.length; i++)
|
|
449
|
+
if (s += this.statics[i], i < this.dynamics.length) {
|
|
450
|
+
let l = this.dynamics[i].getValue(a, o);
|
|
451
|
+
if (l instanceof Promise)
|
|
452
|
+
return Promise.all(this.dynamics.map((c) => {
|
|
453
|
+
const d = c.getValue(a, o);
|
|
454
|
+
return d instanceof Promise ? d : Promise.resolve(d);
|
|
455
|
+
})).then((c) => {
|
|
456
|
+
let d = "";
|
|
457
|
+
for (let u = 0; u < this.statics.length; u++)
|
|
458
|
+
d += this.statics[u], u < c.length && (d += c[u]);
|
|
459
|
+
return d;
|
|
460
|
+
});
|
|
461
|
+
s += l;
|
|
462
|
+
}
|
|
463
|
+
return s;
|
|
464
|
+
};
|
|
465
|
+
return {
|
|
466
|
+
statics: this.statics,
|
|
467
|
+
dynamics: this.dynamics,
|
|
468
|
+
fragment: t,
|
|
469
|
+
id: e,
|
|
470
|
+
hasDynamics: this.dynamics.length > 0,
|
|
471
|
+
render: n
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
parseTemplate() {
|
|
475
|
+
const t = /\{\{([^}]+)\}\}/g;
|
|
476
|
+
let e = 0, n;
|
|
477
|
+
for (; (n = t.exec(this.template)) !== null; ) {
|
|
478
|
+
const o = this.template.slice(e, n.index);
|
|
479
|
+
this.statics.push(o);
|
|
480
|
+
let s = o.match(/([a-zA-Z0-9_-]+)\s*=\s*"?$/), i = s ? s[1] : void 0, l;
|
|
481
|
+
if (o.endsWith('style="color:'))
|
|
482
|
+
i = "style", l = "color";
|
|
483
|
+
else if (i === "style") {
|
|
484
|
+
const d = o.match(/style\s*=\s*"?([^:;]+):\s*$/);
|
|
485
|
+
d && (l = d[1].trim());
|
|
486
|
+
}
|
|
487
|
+
const c = n[1].trim();
|
|
488
|
+
this.analyzeDynamicExpression(c, this.dynamics.length, i, l), e = n.index + n[0].length;
|
|
489
|
+
}
|
|
490
|
+
const a = this.template.slice(e);
|
|
491
|
+
this.statics.push(a);
|
|
492
|
+
}
|
|
493
|
+
analyzeDynamicExpression(t, e, n, a) {
|
|
494
|
+
let o = "text", s;
|
|
495
|
+
n ? n === "class" ? (o = "class", s = "class") : n === "style" ? (o = "style", s = a || "style") : n === "value" ? (o = "property", s = "value") : (o = "attribute", s = n) : t.includes("class.") ? (o = "class", s = t.split(".")[1]) : t.includes("style.") ? (o = "style", s = t.split(".")[1]) : t.includes("@") ? (o = "event", s = t.split("@")[1]) : t === "class" ? (o = "class", s = "class") : t === "style" ? (o = "style", s = "style") : t === "value" ? (o = "property", s = "value") : t === "title" && (o = "attribute", s = "title");
|
|
496
|
+
const i = `__DYNAMIC_${e}__`, l = this.statics.join(i);
|
|
497
|
+
let c = U(l, i);
|
|
498
|
+
this.statics.length === 2 && o !== "text" ? c = [0] : this.statics.length === 2 && c.length === 0 && (c = [0]), this.dynamics.push({
|
|
499
|
+
path: c,
|
|
500
|
+
type: o,
|
|
501
|
+
target: s,
|
|
502
|
+
getValue: this.createValueGetter(t)
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
createValueGetter(t) {
|
|
506
|
+
return (e, n) => {
|
|
507
|
+
try {
|
|
508
|
+
let a;
|
|
509
|
+
if (t && typeof t == "function")
|
|
510
|
+
a = t(e);
|
|
511
|
+
else if (typeof t == "string" && t.startsWith("state.")) {
|
|
512
|
+
const o = t.slice(6);
|
|
513
|
+
a = e[o];
|
|
514
|
+
} else typeof t == "string" && /^[a-zA-Z0-9_$]+$/.test(t) ? a = e[t] : (typeof t == "string" && t.includes("("), a = "");
|
|
515
|
+
return a;
|
|
516
|
+
} catch (a) {
|
|
517
|
+
return this.options.development && console.warn(`[Template Compiler] Error evaluating expression: ${t}`, a), "";
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
createStaticFragment() {
|
|
522
|
+
if (typeof document > "u")
|
|
523
|
+
return null;
|
|
524
|
+
try {
|
|
525
|
+
const t = this.statics.join("");
|
|
526
|
+
if (!t.trim())
|
|
527
|
+
return null;
|
|
528
|
+
const n = new DOMParser().parseFromString(t, "text/html"), a = document.createDocumentFragment();
|
|
529
|
+
for (; n.body.firstChild; )
|
|
530
|
+
a.appendChild(n.body.firstChild);
|
|
531
|
+
return a;
|
|
532
|
+
} catch (t) {
|
|
533
|
+
return this.options.development && console.warn("[Template Compiler] Could not create static fragment:", t), null;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
function O(r, t) {
|
|
538
|
+
try {
|
|
539
|
+
if (t.length === 1 && t[0] === 0 && r instanceof Element)
|
|
540
|
+
return r;
|
|
541
|
+
let e = r;
|
|
542
|
+
for (let n = 0; n < t.length; n++) {
|
|
543
|
+
const a = t[n];
|
|
544
|
+
if (!e.childNodes || e.childNodes.length <= a)
|
|
545
|
+
return null;
|
|
546
|
+
e = e.childNodes[a];
|
|
547
|
+
}
|
|
548
|
+
return e;
|
|
549
|
+
} catch {
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
function j(r, t, e) {
|
|
554
|
+
let n;
|
|
555
|
+
return r.fragment && !r.hasDynamics ? n = r.fragment.cloneNode(!0) : n = V(r, t, e), n;
|
|
556
|
+
}
|
|
557
|
+
function X(r, t, e, n, a) {
|
|
558
|
+
if (r.hasDynamics)
|
|
559
|
+
for (const o of r.dynamics)
|
|
560
|
+
try {
|
|
561
|
+
const s = o.getValue(e, n);
|
|
562
|
+
if (a !== void 0 && o.getValue(a, n) === s)
|
|
563
|
+
continue;
|
|
564
|
+
I(t, o, s);
|
|
565
|
+
} catch (s) {
|
|
566
|
+
console.warn("[Template Compiler] Error applying update:", s);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
function V(r, t, e) {
|
|
570
|
+
let n = "";
|
|
571
|
+
for (let i = 0; i < r.statics.length; i++)
|
|
572
|
+
if (n += r.statics[i], i < r.dynamics.length) {
|
|
573
|
+
const l = r.dynamics[i];
|
|
574
|
+
if (l.type === "text" || l.type === "attribute") {
|
|
575
|
+
const c = l.getValue(t, e);
|
|
576
|
+
n += String(c ?? "");
|
|
577
|
+
} else (l.type === "property" || l.type === "class" || l.type === "style") && (n += "");
|
|
578
|
+
}
|
|
579
|
+
if (typeof document > "u")
|
|
580
|
+
return new DocumentFragment();
|
|
581
|
+
const o = new DOMParser().parseFromString(n, "text/html"), s = document.createDocumentFragment();
|
|
582
|
+
for (; o.body.firstChild; )
|
|
583
|
+
s.appendChild(o.body.firstChild);
|
|
584
|
+
for (const i of r.dynamics) {
|
|
585
|
+
const l = i.getValue(t, e), c = O(s, i.path);
|
|
586
|
+
I(c, i, l);
|
|
587
|
+
}
|
|
588
|
+
return s;
|
|
589
|
+
}
|
|
590
|
+
function I(r, t, e) {
|
|
591
|
+
try {
|
|
592
|
+
if (t.type === "text") {
|
|
593
|
+
const a = document.createTreeWalker(
|
|
594
|
+
r,
|
|
595
|
+
NodeFilter.SHOW_TEXT
|
|
596
|
+
);
|
|
597
|
+
let o = !1, s;
|
|
598
|
+
for (; s = a.nextNode(); ) {
|
|
599
|
+
const l = s.textContent || "";
|
|
600
|
+
if (l.includes("Count: ")) {
|
|
601
|
+
const c = l.replace(/Count: \d+/, `Count: ${e}`);
|
|
602
|
+
s.textContent = c, o = !0;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
if (o) return;
|
|
606
|
+
const i = O(r, t.path);
|
|
607
|
+
i && i.nodeType === Node.TEXT_NODE && (i.textContent = e == null ? "" : String(e));
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
const n = O(r, t.path);
|
|
611
|
+
if (!n)
|
|
612
|
+
return;
|
|
613
|
+
switch (t.type) {
|
|
614
|
+
case "attribute":
|
|
615
|
+
if (n.nodeType === Node.ELEMENT_NODE && t.target) {
|
|
616
|
+
const a = n;
|
|
617
|
+
e == null || e === "" ? a.removeAttribute(t.target) : a.setAttribute(t.target, String(e));
|
|
618
|
+
}
|
|
619
|
+
break;
|
|
620
|
+
case "property":
|
|
621
|
+
n.nodeType === Node.ELEMENT_NODE && t.target && (n[t.target] = e ?? "", n.setAttribute(t.target, e == null ? "" : String(e)));
|
|
622
|
+
break;
|
|
623
|
+
case "class":
|
|
624
|
+
if (n.nodeType === Node.ELEMENT_NODE && t.target) {
|
|
625
|
+
const a = n;
|
|
626
|
+
a.className = e == null ? "" : String(e), a.setAttribute("class", e == null ? "" : String(e));
|
|
627
|
+
}
|
|
628
|
+
break;
|
|
629
|
+
case "style":
|
|
630
|
+
if (n.nodeType === Node.ELEMENT_NODE && t.target) {
|
|
631
|
+
const a = n;
|
|
632
|
+
a.style[t.target] = e == null ? "" : String(e), a.setAttribute("style", e == null ? `${t.target}:` : `${t.target}:${e}`);
|
|
633
|
+
}
|
|
634
|
+
break;
|
|
635
|
+
default:
|
|
636
|
+
throw new Error(`Unknown update type: ${t.type}`);
|
|
637
|
+
}
|
|
638
|
+
} catch (n) {
|
|
639
|
+
(typeof globalThis < "u" ? globalThis.isDevelopment : R) && console.warn("[Template Compiler] Error applying update:", t, n);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
const T = /* @__PURE__ */ new Map(), v = /* @__PURE__ */ new Map();
|
|
643
|
+
function N(r) {
|
|
644
|
+
let t = 0;
|
|
645
|
+
for (let e = 0; e < r.length; e++) {
|
|
646
|
+
const n = r.charCodeAt(e);
|
|
647
|
+
t = (t << 5) - t + n, t = t & t;
|
|
648
|
+
}
|
|
649
|
+
return `tpl_${Math.abs(t).toString(36)}`;
|
|
650
|
+
}
|
|
651
|
+
function x(r, t, e, n, a) {
|
|
652
|
+
return n && a ? `${t}.${r}[${e}]:${n}:${a}` : n ? `${t}.${r}[${e}]:${n}` : `${t}.${r}[${e}]`;
|
|
653
|
+
}
|
|
654
|
+
function D(r, t, e) {
|
|
655
|
+
if (!(!r || !(r instanceof Element)) && r.contains(e) && e.parentNode === r)
|
|
656
|
+
try {
|
|
657
|
+
r.replaceChild(t, e);
|
|
658
|
+
} catch (n) {
|
|
659
|
+
console.error("[VDOM] safeReplaceChild: error replacing child", n, {
|
|
660
|
+
parent: r,
|
|
661
|
+
newChild: t,
|
|
662
|
+
oldChild: e,
|
|
663
|
+
parentHTML: r.outerHTML,
|
|
664
|
+
newChildHTML: t.outerHTML,
|
|
665
|
+
oldChildHTML: e.outerHTML
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
function g(r) {
|
|
670
|
+
if (r.type === "#whitespace")
|
|
671
|
+
return null;
|
|
672
|
+
if (r.type === "#text") {
|
|
673
|
+
const e = document.createTextNode(r.props.nodeValue ?? "");
|
|
674
|
+
return r.dom = e, e;
|
|
675
|
+
}
|
|
676
|
+
const t = document.createElement(r.type);
|
|
677
|
+
for (const [e, n] of Object.entries(r.props))
|
|
678
|
+
e === "value" && t instanceof HTMLInputElement ? t.type === "radio" ? t.setAttribute("value", n) : (t.type, t.value = n, t.setAttribute("value", n)) : t.setAttribute(e, n);
|
|
679
|
+
r.dom = t;
|
|
680
|
+
for (const e of r.children) {
|
|
681
|
+
const n = g(e);
|
|
682
|
+
n && t.appendChild(n);
|
|
683
|
+
}
|
|
684
|
+
return t;
|
|
685
|
+
}
|
|
686
|
+
function Y(r) {
|
|
687
|
+
const t = document.createElement("template");
|
|
688
|
+
t.innerHTML = r.trim();
|
|
689
|
+
const e = Array.from(t.content.childNodes);
|
|
690
|
+
return e.length === 1 ? k(e[0]) : {
|
|
691
|
+
type: "#fragment",
|
|
692
|
+
key: void 0,
|
|
693
|
+
props: {},
|
|
694
|
+
children: e.map((a, o) => k(a, "#fragment", o)),
|
|
695
|
+
dom: void 0
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
function k(r, t = "", e = 0) {
|
|
699
|
+
if (!r)
|
|
700
|
+
return { type: "#unknown", key: void 0, props: {}, children: [], dom: void 0 };
|
|
701
|
+
if (r.nodeType === Node.TEXT_NODE)
|
|
702
|
+
return !r.nodeValue || /^\s*$/.test(r.nodeValue) ? { type: "#whitespace", key: void 0, props: {}, children: [], dom: void 0 } : { type: "#text", key: x("#text", t, e), props: { nodeValue: r.nodeValue }, children: [], dom: r };
|
|
703
|
+
if (r.nodeType === Node.ELEMENT_NODE) {
|
|
704
|
+
const n = r, a = {};
|
|
705
|
+
Array.from(n.attributes).forEach((c) => {
|
|
706
|
+
a[c.name] = c.value;
|
|
707
|
+
});
|
|
708
|
+
const o = n.tagName.toLowerCase();
|
|
709
|
+
let s;
|
|
710
|
+
if ((o === "input" || o === "select" || o === "textarea") && n.hasAttribute("data-model")) {
|
|
711
|
+
const c = n.getAttribute("data-model"), d = n.getAttribute("type") ?? "";
|
|
712
|
+
s = `${o}:${c}:${d}`, a["data-uid"] = s, n.setAttribute("data-uid", s);
|
|
713
|
+
let u = n.getAttribute("value"), f = n.getAttribute("checked");
|
|
714
|
+
u && (a.value = u), f && (a.checked = f);
|
|
715
|
+
} else o === "input" || o === "textarea" || o === "select" || n.hasAttribute("contenteditable") ? (s = `${o}:${t}:${e}`, a["data-uid"] = s, n.setAttribute("data-uid", s)) : (s = x(o, t, e), o === "li" && (a["data-uid"] = s, n.setAttribute("data-uid", s)));
|
|
716
|
+
const i = Array.from(n.childNodes).map((c, d) => k(c, s, d));
|
|
717
|
+
return {
|
|
718
|
+
type: o,
|
|
719
|
+
key: s,
|
|
720
|
+
props: a,
|
|
721
|
+
children: i,
|
|
722
|
+
dom: n
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
return { type: "#unknown", key: void 0, props: {}, children: [], dom: void 0 };
|
|
726
|
+
}
|
|
727
|
+
function L(r, t, e) {
|
|
728
|
+
if (!t || !e) return;
|
|
729
|
+
function n(l) {
|
|
730
|
+
return !!l && l.type !== "#whitespace" && !(l.type === "#text" && (!l.props?.nodeValue || /^\s*$/.test(l.props.nodeValue)));
|
|
731
|
+
}
|
|
732
|
+
const a = Array.isArray(t.children) ? t.children.filter(n) : [], o = Array.isArray(e.children) ? e.children.filter(n) : [], s = e.type === "input" || e.type === "select" || e.type === "textarea";
|
|
733
|
+
if (t.type !== e.type || t.key !== e.key) {
|
|
734
|
+
const l = g(e);
|
|
735
|
+
if (l instanceof Node && t.dom instanceof Node && r.contains(t.dom)) {
|
|
736
|
+
if (D(r, l, t.dom), s && e.props && r.firstChild instanceof HTMLInputElement) {
|
|
737
|
+
const c = r.firstChild;
|
|
738
|
+
c.type === "radio" || c.type, c.value = e.props.value, c.setAttribute("value", e.props.value), "checked" in e.props && (c.checked = e.props.checked === !0 || e.props.checked === "true");
|
|
739
|
+
}
|
|
740
|
+
} else if (l instanceof Node)
|
|
741
|
+
if (l) {
|
|
742
|
+
if (r.appendChild(l), e.dom = l, s && e.props && r.firstChild instanceof HTMLInputElement) {
|
|
743
|
+
const c = r.firstChild;
|
|
744
|
+
c.type === "radio" ? c.setAttribute("value", e.props.value) : (c.type, c.value = e.props.value, c.setAttribute("value", e.props.value)), "checked" in e.props && (c.checked = e.props.checked === !0 || e.props.checked === "true");
|
|
745
|
+
}
|
|
746
|
+
} else
|
|
747
|
+
e.dom = void 0;
|
|
748
|
+
else
|
|
749
|
+
e.dom = void 0;
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
if (s && t.dom instanceof HTMLElement && e.props) {
|
|
753
|
+
for (const [l, c] of Object.entries(e.props))
|
|
754
|
+
if (l === "value" && r.firstChild instanceof HTMLInputElement)
|
|
755
|
+
r.firstChild.value = c;
|
|
756
|
+
else if (l === "checked" && r.firstChild instanceof HTMLInputElement)
|
|
757
|
+
r.firstChild.checked = c === !0 || c === "true";
|
|
758
|
+
else if (l in t.dom)
|
|
759
|
+
try {
|
|
760
|
+
t.dom[l] = c;
|
|
761
|
+
} catch {
|
|
762
|
+
}
|
|
763
|
+
else
|
|
764
|
+
t.dom.setAttribute(l, c);
|
|
765
|
+
for (let l = e.children.length; l < a.length; l++)
|
|
766
|
+
a[l] && a[l].dom && t.dom && t.dom.contains(a[l].dom) && t.dom.removeChild(a[l].dom);
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
const i = t.dom;
|
|
770
|
+
if (i && i instanceof Element && e.props) {
|
|
771
|
+
const l = i.tagName.toLowerCase() === "input" ? i.getAttribute("type") : void 0, c = i.tagName.includes("-");
|
|
772
|
+
for (const [d, u] of Object.entries(e.props))
|
|
773
|
+
if (!(l === "radio" && d === "value")) {
|
|
774
|
+
if (l === "checkbox" && d === "value") {
|
|
775
|
+
i.setAttribute("value", u);
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
i.setAttribute(d, u);
|
|
779
|
+
}
|
|
780
|
+
if (c)
|
|
781
|
+
for (const [d, u] of Object.entries(e.props))
|
|
782
|
+
i.setAttribute(d, u);
|
|
783
|
+
for (const d of Array.from(i.attributes).map((u) => u.name))
|
|
784
|
+
if (!(d in e.props)) {
|
|
785
|
+
if (l === "radio" && d === "value" || l === "checkbox" && d === "value") continue;
|
|
786
|
+
i.removeAttribute(d);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
if (e.type === "#text") {
|
|
790
|
+
if (i && i.nodeType === Node.TEXT_NODE)
|
|
791
|
+
i.nodeValue !== e.props.nodeValue && (i.nodeValue = e.props.nodeValue), e.dom = i;
|
|
792
|
+
else {
|
|
793
|
+
const l = document.createTextNode(e.props.nodeValue ?? "");
|
|
794
|
+
i && r.contains(i) && i.parentNode === r ? D(r, l, i) : r.appendChild(l), e.dom = l;
|
|
795
|
+
}
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
if (i instanceof Element) {
|
|
799
|
+
const l = /* @__PURE__ */ new Map();
|
|
800
|
+
a.forEach((f) => f.key && l.set(f.key, f));
|
|
801
|
+
const c = new Set(o.map((f) => f.key));
|
|
802
|
+
let d = [];
|
|
803
|
+
for (let f = 0; f < o.length; f++) {
|
|
804
|
+
const h = o[f], m = h.key ? l.get(h.key) : a[f];
|
|
805
|
+
let p;
|
|
806
|
+
const A = h.type === "input" || h.type === "select" || h.type === "textarea";
|
|
807
|
+
if (m && m.dom && (!A || m.type === h.type && m.key === h.key))
|
|
808
|
+
L(i, m, h), p = m.dom;
|
|
809
|
+
else {
|
|
810
|
+
const E = g(h);
|
|
811
|
+
if (p = E instanceof Node ? E : void 0, p) {
|
|
812
|
+
if ((p instanceof Element || p instanceof Node) && p.contains(i))
|
|
813
|
+
throw console.error("[VDOM] Attempted to insert a parent into its own child:", {
|
|
814
|
+
parentTag: i.tagName,
|
|
815
|
+
childTag: p.tagName,
|
|
816
|
+
parentUid: i.getAttribute?.("data-uid"),
|
|
817
|
+
childUid: p.getAttribute?.("data-uid"),
|
|
818
|
+
parent: i,
|
|
819
|
+
child: p
|
|
820
|
+
}), new Error("VDOM patch error: Attempted to insert a parent into its own child");
|
|
821
|
+
i.insertBefore(p, i.childNodes[f] || null);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
h.dom = p, p && d.push(p);
|
|
825
|
+
}
|
|
826
|
+
for (a.forEach((f) => {
|
|
827
|
+
!c.has(f.key) && f.dom && i.contains(f.dom) && i.removeChild(f.dom);
|
|
828
|
+
}); i.childNodes.length > o.length; )
|
|
829
|
+
i.removeChild(i.lastChild);
|
|
830
|
+
for (let f = 0; f < d.length; f++)
|
|
831
|
+
if (i.childNodes[f] !== d[f]) {
|
|
832
|
+
if ((d[f] instanceof Element || d[f] instanceof Node) && d[f].contains(i))
|
|
833
|
+
throw new Error("VDOM patch error: Attempted to insert a parent into its own child");
|
|
834
|
+
i.insertBefore(d[f], i.childNodes[f] || null);
|
|
835
|
+
}
|
|
836
|
+
e.dom = i;
|
|
837
|
+
const u = new Set(o.map((f) => f.key));
|
|
838
|
+
Array.from(i.childNodes).forEach((f, h) => {
|
|
839
|
+
const m = f.getAttribute?.("data-uid");
|
|
840
|
+
(m && !u.has(m) || h >= o.length) && i.removeChild(f);
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
function M(r, t) {
|
|
845
|
+
const e = [], n = t ? Object.keys(t) : [], a = { ...r };
|
|
846
|
+
let o = null;
|
|
847
|
+
function s(d) {
|
|
848
|
+
return e.push(d), () => {
|
|
849
|
+
const u = e.indexOf(d);
|
|
850
|
+
u !== -1 && e.splice(u, 1);
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
function i(d) {
|
|
854
|
+
Object.assign(o, d), e.forEach((u) => u(o));
|
|
855
|
+
}
|
|
856
|
+
const l = /* @__PURE__ */ new WeakMap();
|
|
857
|
+
function c(d) {
|
|
858
|
+
if (l.has(d)) return l.get(d);
|
|
859
|
+
const u = new Proxy(d, {
|
|
860
|
+
get(f, h, m) {
|
|
861
|
+
if (h === "subscribe") return s;
|
|
862
|
+
if (h === "set") return i;
|
|
863
|
+
if (t && n.includes(h))
|
|
864
|
+
return t[h](o);
|
|
865
|
+
const p = Reflect.get(f, h, m);
|
|
866
|
+
return typeof p == "object" && p !== null ? c(p) : p;
|
|
867
|
+
},
|
|
868
|
+
set(f, h, m, p) {
|
|
869
|
+
if (t && n.includes(h))
|
|
870
|
+
return !1;
|
|
871
|
+
const A = f[h], E = Reflect.set(f, h, m, p);
|
|
872
|
+
return A !== m && e.forEach((P) => P(o)), E;
|
|
873
|
+
},
|
|
874
|
+
deleteProperty(f, h) {
|
|
875
|
+
if (t && n.includes(h))
|
|
876
|
+
return !1;
|
|
877
|
+
const m = Reflect.deleteProperty(f, h);
|
|
878
|
+
return e.forEach((p) => p(o)), m;
|
|
879
|
+
}
|
|
880
|
+
});
|
|
881
|
+
return l.set(d, u), u;
|
|
882
|
+
}
|
|
883
|
+
return o = c(a), o;
|
|
884
|
+
}
|
|
885
|
+
const _ = [];
|
|
886
|
+
function dt(r) {
|
|
887
|
+
_.push(r);
|
|
888
|
+
}
|
|
889
|
+
function b(r, t = /* @__PURE__ */ new WeakSet()) {
|
|
890
|
+
if (r === null || typeof r != "object" || t.has(r)) return r;
|
|
891
|
+
if (t.add(r), Array.isArray(r)) return r.map((a) => b(a, t));
|
|
892
|
+
Object.getPrototypeOf(r) !== Object.prototype && Object.getPrototypeOf(r) !== null && Object.setPrototypeOf(r, null);
|
|
893
|
+
const e = ["__proto__", "constructor", "prototype"], n = /* @__PURE__ */ Object.create(null);
|
|
894
|
+
for (const a of Object.keys(r))
|
|
895
|
+
e.includes(a) || (n[a] = b(r[a], t));
|
|
896
|
+
return n;
|
|
897
|
+
}
|
|
898
|
+
function S(r) {
|
|
899
|
+
return !!r && typeof r.then == "function";
|
|
900
|
+
}
|
|
901
|
+
class J extends HTMLElement {
|
|
902
|
+
/**
|
|
903
|
+
* Allows updating the state object and triggers a re-render.
|
|
904
|
+
* @param newState - Partial state to merge
|
|
905
|
+
*/
|
|
906
|
+
/**
|
|
907
|
+
* Syncs whitelisted state properties to attributes after render.
|
|
908
|
+
* Only keys listed in config.reflect are reflected.
|
|
909
|
+
*/
|
|
910
|
+
syncStateToAttributes() {
|
|
911
|
+
if (!this.stateObj || !this.config?.reflect || !Array.isArray(this.config.reflect)) return;
|
|
912
|
+
const t = ["__proto__", "constructor", "prototype"];
|
|
913
|
+
this.config.reflect.forEach((e) => {
|
|
914
|
+
if (t.includes(e)) {
|
|
915
|
+
this.removeAttribute(e);
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
const n = this.stateObj[e];
|
|
919
|
+
["string", "number", "boolean"].includes(typeof n) ? n == null ? this.removeAttribute(e) : this.setAttribute(e, String(n)) : this.removeAttribute(e);
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Allows updating the template function at runtime and triggers a re-render.
|
|
924
|
+
* @param newTemplate - New template function or string
|
|
925
|
+
*/
|
|
926
|
+
setTemplate(t) {
|
|
927
|
+
const e = this.config;
|
|
928
|
+
typeof t == "function" ? e.template = t : e.template = () => t, this.render();
|
|
929
|
+
}
|
|
930
|
+
_hasError = !1;
|
|
931
|
+
_mountedCalled = !1;
|
|
932
|
+
_unmountedCalled = !1;
|
|
933
|
+
/**
|
|
934
|
+
* Tracks auto-wired config event handlers for removal
|
|
935
|
+
*/
|
|
936
|
+
_autoWiredHandlers = {};
|
|
937
|
+
/**
|
|
938
|
+
* Override removeEventListener to support auto-wired config handler removal
|
|
939
|
+
*/
|
|
940
|
+
removeEventListener(t, e, n) {
|
|
941
|
+
super.removeEventListener(t, e, n), this._autoWiredHandlers[t] && (this._autoWiredHandlers[t] = this._autoWiredHandlers[t].filter((a) => a === e ? (super.removeEventListener(t, a, n), !1) : !0), this._autoWiredHandlers[t].length === 0 && delete this._autoWiredHandlers[t]);
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* observedAttributes automatically returns all primitive keys from static state.
|
|
945
|
+
* This enables automatic attribute observation for all primitive state properties.
|
|
946
|
+
*/
|
|
947
|
+
static get observedAttributes() {
|
|
948
|
+
const t = this.stateObj || {};
|
|
949
|
+
return Object.keys(t).filter(
|
|
950
|
+
(e) => ["string", "number", "boolean"].includes(typeof t[e])
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* Called when an observed attribute changes. Syncs attribute to state and triggers render.
|
|
955
|
+
*/
|
|
956
|
+
attributeChangedCallback(t, e, n) {
|
|
957
|
+
if (!(t === "__proto__" || t === "constructor" || t === "prototype") && this.stateObj && t in this.stateObj) {
|
|
958
|
+
const a = typeof this.config?.state?.[t];
|
|
959
|
+
let o = n;
|
|
960
|
+
if (n === null)
|
|
961
|
+
o = void 0;
|
|
962
|
+
else if (a === "number")
|
|
963
|
+
if (o === void 0 || o === "")
|
|
964
|
+
o = this.config?.state?.[t];
|
|
965
|
+
else {
|
|
966
|
+
const s = Number(o);
|
|
967
|
+
o = isNaN(s) ? this.config?.state?.[t] : s;
|
|
968
|
+
}
|
|
969
|
+
else a === "boolean" && (o = o === "true");
|
|
970
|
+
o = b(o), this.stateObj[t] !== o && (this.config?.debug && console.log("[runtime] state update:", { name: t, value: o }), this.stateObj[t] = o, this.render());
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Force sync all controlled input values and event listeners after VDOM patching.
|
|
975
|
+
*/
|
|
976
|
+
forceSyncControlledInputs() {
|
|
977
|
+
this.shadowRoot && (this.shadowRoot.querySelectorAll("input[data-model]").forEach((t) => {
|
|
978
|
+
const e = t.getAttribute("data-model");
|
|
979
|
+
if (!e || !this.stateObj || typeof this.stateObj[e] > "u") return;
|
|
980
|
+
const n = t, a = String(this.stateObj[e]), o = document.activeElement === n;
|
|
981
|
+
n._hasDirtyListener || (n.addEventListener("input", () => {
|
|
982
|
+
n._isDirty = !0;
|
|
983
|
+
}), n.addEventListener("blur", () => {
|
|
984
|
+
n._isDirty = !1;
|
|
985
|
+
}), n._hasDirtyListener = !0);
|
|
986
|
+
const s = !!n._isDirty;
|
|
987
|
+
o || s || n.type !== "radio" && n.type !== "checkbox" && n.value !== a && (n.value = a);
|
|
988
|
+
}), this.rebindEventListeners());
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
991
|
+
* Sync all controlled inputs and event listeners after render
|
|
992
|
+
*/
|
|
993
|
+
syncControlledInputsAndEvents() {
|
|
994
|
+
this.shadowRoot && (this.shadowRoot.querySelectorAll('input[type="radio"][data-model]').forEach((t) => {
|
|
995
|
+
const e = t.getAttribute("data-model");
|
|
996
|
+
if (!e || !this.stateObj || typeof this.stateObj[e] > "u")
|
|
997
|
+
return;
|
|
998
|
+
const n = t, a = String(this.stateObj[e]);
|
|
999
|
+
n.checked = n.value === a;
|
|
1000
|
+
}), this.shadowRoot.querySelectorAll("input[data-model]").forEach((t) => {
|
|
1001
|
+
const e = t.getAttribute("data-model");
|
|
1002
|
+
if (!e || !this.stateObj || typeof this.stateObj[e] > "u") return;
|
|
1003
|
+
const n = t, a = String(this.stateObj[e]);
|
|
1004
|
+
if (n.type === "checkbox") {
|
|
1005
|
+
const o = this.stateObj[e];
|
|
1006
|
+
if (Array.isArray(o))
|
|
1007
|
+
n.checked = o.includes(n.value);
|
|
1008
|
+
else {
|
|
1009
|
+
const s = n.getAttribute("data-true-value"), i = n.getAttribute("data-false-value");
|
|
1010
|
+
s !== null || i !== null ? String(o) === s ? n.checked = !0 : String(o) === i ? n.checked = !1 : o === !0 ? n.checked = !0 : n.checked = !1 : n.checked = o === !0 || o === "true" || o === 1;
|
|
1011
|
+
}
|
|
1012
|
+
} else n.type === "radio" || (n.value = a);
|
|
1013
|
+
}), this.shadowRoot.querySelectorAll("textarea[data-model]").forEach((t) => {
|
|
1014
|
+
const e = t.getAttribute("data-model");
|
|
1015
|
+
!e || !this.stateObj || typeof this.stateObj[e] > "u" || (t.value = String(this.stateObj[e]));
|
|
1016
|
+
}), this.shadowRoot.querySelectorAll("select[data-model]").forEach((t) => {
|
|
1017
|
+
const e = t.getAttribute("data-model");
|
|
1018
|
+
!e || !this.stateObj || typeof this.stateObj[e] > "u" || (t.value = String(this.stateObj[e]));
|
|
1019
|
+
}));
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Attach event listeners for input[data-bind] after VDOM patching
|
|
1023
|
+
*/
|
|
1024
|
+
attachListItemModelListeners() {
|
|
1025
|
+
this.shadowRoot && this.shadowRoot.querySelectorAll("input[data-bind]").forEach((t) => {
|
|
1026
|
+
const e = t.getAttribute("data-bind");
|
|
1027
|
+
if (!e) return;
|
|
1028
|
+
t._listItemModelListener && (t.removeEventListener("input", t._listItemModelListener), t.removeEventListener("change", t._listItemModelListener), delete t._listItemModelListener);
|
|
1029
|
+
const n = e.match(/^([a-zA-Z0-9_]+)\[(\d+)\]\.([a-zA-Z0-9_]+)$/);
|
|
1030
|
+
if (n) {
|
|
1031
|
+
const [, o, s, i] = n, l = parseInt(s, 10), c = this.stateObj[o];
|
|
1032
|
+
t instanceof HTMLInputElement && t.type === "checkbox" && (t.checked = !!(Array.isArray(c) && c[l] && c[l][i]));
|
|
1033
|
+
const d = (u) => {
|
|
1034
|
+
!Array.isArray(c) || !c[l] || (t instanceof HTMLInputElement && t.type === "checkbox" ? c[l][i] = t.checked : c[l][i] = t.value);
|
|
1035
|
+
};
|
|
1036
|
+
t.addEventListener("input", d), t.addEventListener("change", d), t._listItemModelListener = d;
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
const a = e.match(/^([a-zA-Z0-9_]+)\.([a-zA-Z0-9_]+)((?:\|[a-zA-Z0-9_]+)*)$/);
|
|
1040
|
+
if (a) {
|
|
1041
|
+
const [, o, s, i] = a, l = this.stateObj[o], c = i ? i.split("|").map((u) => u.trim()).filter(Boolean) : [];
|
|
1042
|
+
t instanceof HTMLInputElement && t.type === "checkbox" ? t.checked = !!(l && l[s]) : t instanceof HTMLInputElement && (t.value = l ? String(l[s] ?? "") : "");
|
|
1043
|
+
const d = (u) => {
|
|
1044
|
+
if (!l) return;
|
|
1045
|
+
let f;
|
|
1046
|
+
t instanceof HTMLInputElement && t.type === "checkbox" ? f = t.checked : (f = t.value, c.includes("number") && (f = Number(f)), c.includes("trim") && typeof f == "string" && (f = f.trim())), l[s] = f;
|
|
1047
|
+
};
|
|
1048
|
+
t.addEventListener("input", d), t.addEventListener("change", d), t._listItemModelListener = d;
|
|
1049
|
+
}
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Attach controlled input listeners to sync DOM value to state
|
|
1054
|
+
*/
|
|
1055
|
+
attachControlledInputListeners() {
|
|
1056
|
+
const t = this.shadowRoot;
|
|
1057
|
+
t && (t.querySelectorAll("[data-model]").forEach((e) => {
|
|
1058
|
+
const n = e.getAttribute("data-model");
|
|
1059
|
+
n && (e._dataModelBound || (G(e, this.stateObj, n), e._dataModelBound = !0));
|
|
1060
|
+
}), t.querySelectorAll("[data-model]").forEach((e) => {
|
|
1061
|
+
const [n] = e.getAttribute("data-model")?.split("|").map((a) => a.trim()) ?? [];
|
|
1062
|
+
if (!(!n || !(n in this.stateObj)))
|
|
1063
|
+
if (e instanceof HTMLInputElement)
|
|
1064
|
+
if (e.type === "checkbox") {
|
|
1065
|
+
const a = this.stateObj[n], o = e.getAttribute("data-true-value"), s = e.getAttribute("data-false-value");
|
|
1066
|
+
Array.isArray(a) ? e.checked = a.includes(e.value) : o !== null || s !== null ? String(a) === o ? e.checked = !0 : String(a) === s ? e.checked = !1 : a === !0 ? e.checked = !0 : e.checked = !1 : e.checked = a === !0 || a === "true" || a === 1;
|
|
1067
|
+
} else e.type === "radio" ? e.checked = e.value === String(this.stateObj[n]) : e.value = String(this.stateObj[n] ?? "");
|
|
1068
|
+
else (e instanceof HTMLTextAreaElement || e instanceof HTMLSelectElement) && (e.value = String(this.stateObj[n] ?? ""));
|
|
1069
|
+
}));
|
|
1070
|
+
}
|
|
1071
|
+
config;
|
|
1072
|
+
stateObj;
|
|
1073
|
+
api;
|
|
1074
|
+
_globalUnsubscribes = [];
|
|
1075
|
+
unsubscribes = [];
|
|
1076
|
+
lastCompiledTemplate = null;
|
|
1077
|
+
lastState = null;
|
|
1078
|
+
rafId = null;
|
|
1079
|
+
/**
|
|
1080
|
+
* Construct a new runtime component element.
|
|
1081
|
+
* @param config - Component configuration
|
|
1082
|
+
*/
|
|
1083
|
+
constructor() {
|
|
1084
|
+
super();
|
|
1085
|
+
}
|
|
1086
|
+
initializeConfig() {
|
|
1087
|
+
if (this.config) return;
|
|
1088
|
+
const t = this.tagName.toLowerCase(), n = (window.__componentRegistry || {})[t];
|
|
1089
|
+
if (!n || typeof n != "object")
|
|
1090
|
+
throw new Error("Invalid component config: must be an object");
|
|
1091
|
+
if (!n.state || typeof n.state != "object")
|
|
1092
|
+
throw new Error("Invalid component config: state must be an object");
|
|
1093
|
+
this.config = n;
|
|
1094
|
+
const a = n.computed ? M(n.state, n.computed) : M(n.state);
|
|
1095
|
+
if (this.stateObj = a, typeof this.stateObj.subscribe == "function" && this.unsubscribes.push(this.stateObj.subscribe(() => {
|
|
1096
|
+
this.scheduleRender();
|
|
1097
|
+
})), this.api = {
|
|
1098
|
+
state: this.stateObj,
|
|
1099
|
+
emit: (s, i) => this.dispatchEvent(new CustomEvent(s, { detail: i, bubbles: !0 })),
|
|
1100
|
+
onGlobal: (s, i) => {
|
|
1101
|
+
const l = C.on(s, i);
|
|
1102
|
+
return this._globalUnsubscribes.push(l), l;
|
|
1103
|
+
},
|
|
1104
|
+
offGlobal: (s, i) => C.off(s, i),
|
|
1105
|
+
emitGlobal: (s, i) => C.emit(s, i)
|
|
1106
|
+
}, Object.keys(this.config).forEach((s) => {
|
|
1107
|
+
if (s.startsWith("on") && s.length > 2 && typeof this.config[s] == "function") {
|
|
1108
|
+
const i = s.charAt(2).toLowerCase() + s.slice(3), l = (c) => {
|
|
1109
|
+
const d = c.detail ?? c;
|
|
1110
|
+
this.config[s](d, this.api.state, this.api);
|
|
1111
|
+
};
|
|
1112
|
+
this.addEventListener(i, l), this._autoWiredHandlers[i] || (this._autoWiredHandlers[i] = []), this._autoWiredHandlers[i].push(l);
|
|
1113
|
+
}
|
|
1114
|
+
}), this.attachShadow({ mode: "open" }), n.style) {
|
|
1115
|
+
const s = document.createElement("style");
|
|
1116
|
+
s.textContent = typeof n.style == "function" ? n.style(this.stateObj) : n.style, this.shadowRoot.appendChild(s);
|
|
1117
|
+
}
|
|
1118
|
+
if (typeof this.config.hydrate == "function") {
|
|
1119
|
+
const s = this.shadowRoot?.querySelectorAll("[data-hydrate]");
|
|
1120
|
+
try {
|
|
1121
|
+
s && s.length > 0 ? s.forEach((i) => {
|
|
1122
|
+
try {
|
|
1123
|
+
this.config.hydrate(i, this.stateObj, this.api);
|
|
1124
|
+
} catch (l) {
|
|
1125
|
+
typeof this.config.onError == "function" && this.config.onError(l instanceof Error ? l : new Error(String(l)), this.api.state, this.api), this._handleRenderError(l);
|
|
1126
|
+
}
|
|
1127
|
+
}) : this.config.hydrate(this.shadowRoot, this.stateObj, this.api);
|
|
1128
|
+
} catch (i) {
|
|
1129
|
+
typeof this.config.onError == "function" && this.config.onError(i instanceof Error ? i : new Error(String(i)), this.api.state, this.api), this._handleRenderError(i);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
if (this.hasAttribute("data-hydrated") ? this.processRefs() : this.render(), !this._mountedCalled && typeof this.config.onMounted == "function")
|
|
1133
|
+
try {
|
|
1134
|
+
const s = this.config.onMounted(this.api.state, this.api);
|
|
1135
|
+
S(s) ? s.catch((i) => {
|
|
1136
|
+
typeof this.config.onError == "function" && this.config.onError(i, this.api.state, this.api), this._handleRenderError(i);
|
|
1137
|
+
}).finally(() => {
|
|
1138
|
+
this._mountedCalled = !0;
|
|
1139
|
+
}) : this._mountedCalled = !0;
|
|
1140
|
+
} catch (s) {
|
|
1141
|
+
typeof this.config.onError == "function" && this.config.onError(s, this.api.state, this.api), this._handleRenderError(s), this._mountedCalled = !0;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
connectedCallback() {
|
|
1145
|
+
if (this.initializeConfig(), this.stateObj) {
|
|
1146
|
+
for (const t of this.getAttributeNames())
|
|
1147
|
+
if (t in this.stateObj) {
|
|
1148
|
+
const e = typeof this.config?.state?.[t];
|
|
1149
|
+
let n = this.getAttribute(t);
|
|
1150
|
+
e === "number" ? n = Number(n) : e === "boolean" && (n = n === "true"), this.stateObj[t] = n === null ? void 0 : n;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
if (!this._mountedCalled && typeof this.config.onMounted == "function")
|
|
1154
|
+
try {
|
|
1155
|
+
const t = this.config.onMounted(this.api.state, this.api);
|
|
1156
|
+
S(t) ? t.catch((e) => {
|
|
1157
|
+
typeof this.config.onError == "function" && this.config.onError(e, this.api.state, this.api), this._handleRenderError(e);
|
|
1158
|
+
}).finally(() => {
|
|
1159
|
+
this._mountedCalled = !0;
|
|
1160
|
+
}) : this._mountedCalled = !0;
|
|
1161
|
+
} catch (t) {
|
|
1162
|
+
typeof this.config.onError == "function" && this.config.onError(t, this.api.state, this.api), this._handleRenderError(t), this._mountedCalled = !0;
|
|
1163
|
+
}
|
|
1164
|
+
typeof this.render == "function" && this.render();
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Lifecycle: called when element is removed from DOM.
|
|
1168
|
+
*/
|
|
1169
|
+
disconnectedCallback() {
|
|
1170
|
+
if (Object.entries(this._autoWiredHandlers).forEach(([t, e]) => {
|
|
1171
|
+
e.forEach((n) => {
|
|
1172
|
+
super.removeEventListener(t, n);
|
|
1173
|
+
});
|
|
1174
|
+
}), this._autoWiredHandlers = {}, this.unsubscribes.forEach((t) => t()), this.unsubscribes = [], this._globalUnsubscribes.forEach((t) => t()), this._globalUnsubscribes = [], !this._unmountedCalled && typeof this.config.onUnmounted == "function")
|
|
1175
|
+
try {
|
|
1176
|
+
const t = this.config.onUnmounted(this.api.state, this.api);
|
|
1177
|
+
S(t) ? t.catch((e) => {
|
|
1178
|
+
typeof this.config.onError == "function" && this.config.onError(e, this.api.state, this.api), this._handleRenderError(e);
|
|
1179
|
+
}).finally(() => {
|
|
1180
|
+
this._unmountedCalled = !0;
|
|
1181
|
+
}) : this._unmountedCalled = !0;
|
|
1182
|
+
} catch (t) {
|
|
1183
|
+
typeof this.config.onError == "function" && this.config.onError(t, this.api.state, this.api), this._handleRenderError(t), this._unmountedCalled = !0;
|
|
1184
|
+
}
|
|
1185
|
+
this._mountedCalled = !1, this._unmountedCalled = !1;
|
|
1186
|
+
}
|
|
1187
|
+
/**
|
|
1188
|
+
* Render the component. Handles both string and compiled templates, refs, and error boundaries.
|
|
1189
|
+
*/
|
|
1190
|
+
render() {
|
|
1191
|
+
this._hasError = !1, this.syncControlledInputsAndEvents(), setTimeout(() => this.attachControlledInputListeners(), 0);
|
|
1192
|
+
try {
|
|
1193
|
+
_.forEach((e) => {
|
|
1194
|
+
try {
|
|
1195
|
+
e.onRender?.(this.stateObj, this.api);
|
|
1196
|
+
} catch (n) {
|
|
1197
|
+
this._handleRenderError(n);
|
|
1198
|
+
}
|
|
1199
|
+
}), this.config.computed && Object.values(this.config.computed).forEach((e) => {
|
|
1200
|
+
try {
|
|
1201
|
+
e(this.stateObj);
|
|
1202
|
+
} catch (n) {
|
|
1203
|
+
this._handleRenderError(n);
|
|
1204
|
+
}
|
|
1205
|
+
});
|
|
1206
|
+
const t = this.config.template(this.stateObj, this.api);
|
|
1207
|
+
t instanceof Promise ? t.then((e) => {
|
|
1208
|
+
this._hasError || (this._renderTemplateResult(e), this.syncStateToAttributes(), setTimeout(() => this.attachListItemModelListeners(), 0));
|
|
1209
|
+
}).catch((e) => {
|
|
1210
|
+
this._handleRenderError(e);
|
|
1211
|
+
}) : this._hasError || (this._renderTemplateResult(t), this.syncStateToAttributes(), setTimeout(() => this.attachListItemModelListeners(), 0));
|
|
1212
|
+
} catch (t) {
|
|
1213
|
+
this._handleRenderError(t), this.renderError(t instanceof Error ? t : new Error(String(t)));
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* Internal: render a template result (string or compiled template)
|
|
1218
|
+
*/
|
|
1219
|
+
_prevVNode = null;
|
|
1220
|
+
/**
|
|
1221
|
+
* Rebind event listeners for elements with data-on-* attributes in the shadow DOM
|
|
1222
|
+
*/
|
|
1223
|
+
rebindEventListeners() {
|
|
1224
|
+
if (!this.shadowRoot) return;
|
|
1225
|
+
["data-on-input", "data-on-change", "data-on-blur", "data-on-click"].forEach((e) => {
|
|
1226
|
+
this.shadowRoot.querySelectorAll(`[${e}]`).forEach((n) => {
|
|
1227
|
+
const a = e.replace("data-on-", ""), o = n.getAttribute(e);
|
|
1228
|
+
if (!o || typeof this.config[o] != "function") return;
|
|
1229
|
+
n._boundHandlers && n._boundHandlers[a] && n.removeEventListener(a, n._boundHandlers[a]);
|
|
1230
|
+
const s = this.config[o], i = (l) => s.call(this, l);
|
|
1231
|
+
n.addEventListener(a, i), n._boundHandlers || (n._boundHandlers = {}), n._boundHandlers[a] = i;
|
|
1232
|
+
});
|
|
1233
|
+
}), Array.from(this.shadowRoot.children).forEach((e) => {
|
|
1234
|
+
e instanceof HTMLElement && typeof e.rebindEventListeners == "function" && e.rebindEventListeners();
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Internal: render a template result (string or compiled template).
|
|
1239
|
+
* Handles VDOM patching, style updates, refs, and event binding.
|
|
1240
|
+
* @param templateResult - HTML string or compiled template
|
|
1241
|
+
*/
|
|
1242
|
+
_renderTemplateResult(t) {
|
|
1243
|
+
if (!this._hasError)
|
|
1244
|
+
try {
|
|
1245
|
+
let e = function(n) {
|
|
1246
|
+
const a = /* @__PURE__ */ new WeakSet();
|
|
1247
|
+
function o(s) {
|
|
1248
|
+
if (s === null || typeof s != "object") return s;
|
|
1249
|
+
if (a.has(s)) return;
|
|
1250
|
+
if (a.add(s), Array.isArray(s)) return s.map(o);
|
|
1251
|
+
const i = {};
|
|
1252
|
+
for (const l in s)
|
|
1253
|
+
Object.prototype.hasOwnProperty.call(s, l) && (i[l] = o(s[l]));
|
|
1254
|
+
return i;
|
|
1255
|
+
}
|
|
1256
|
+
return o(n);
|
|
1257
|
+
};
|
|
1258
|
+
if (typeof t == "string") {
|
|
1259
|
+
let n = function(c) {
|
|
1260
|
+
return c.replace(/<([a-zA-Z0-9]+)([^>]*)>/g, (d, u, f) => {
|
|
1261
|
+
const h = f.replace(/\s+on[a-zA-Z]+\s*=\s*(['"][^'"]*['"]|[^\s>]*)/gi, "");
|
|
1262
|
+
return `<${u}${h}>`;
|
|
1263
|
+
});
|
|
1264
|
+
}, a = function(c) {
|
|
1265
|
+
c.children.forEach(a);
|
|
1266
|
+
};
|
|
1267
|
+
const o = n(t), s = Y(o);
|
|
1268
|
+
a(s);
|
|
1269
|
+
const i = this.shadowRoot;
|
|
1270
|
+
if (!i)
|
|
1271
|
+
return;
|
|
1272
|
+
let l = i.querySelector("style");
|
|
1273
|
+
if (l || (l = document.createElement("style"), i.appendChild(l)), this.config.style ? l.textContent = typeof this.config.style == "function" ? this.config.style(this.stateObj) : this.config.style : l.textContent = "", s.type === "#fragment") {
|
|
1274
|
+
const c = Array.from(i.childNodes).find(
|
|
1275
|
+
(d) => d.nodeType === 1 && d !== l
|
|
1276
|
+
);
|
|
1277
|
+
if (c) {
|
|
1278
|
+
Array.from(c.childNodes).forEach((f) => {
|
|
1279
|
+
f.nodeType === 1 && f.nodeName === "STYLE" || c.removeChild(f);
|
|
1280
|
+
});
|
|
1281
|
+
const d = {
|
|
1282
|
+
type: "#fragment",
|
|
1283
|
+
dom: c,
|
|
1284
|
+
children: s.children,
|
|
1285
|
+
props: {},
|
|
1286
|
+
key: void 0
|
|
1287
|
+
}, u = this._prevVNode && this._prevVNode.type === "#fragment" ? { ...this._prevVNode, dom: c } : d;
|
|
1288
|
+
L(c, u, d);
|
|
1289
|
+
} else
|
|
1290
|
+
s.children.forEach((d) => {
|
|
1291
|
+
const u = g(d);
|
|
1292
|
+
u && i.appendChild(u), d.dom = u ?? void 0;
|
|
1293
|
+
});
|
|
1294
|
+
} else {
|
|
1295
|
+
let c = Array.from(this.shadowRoot.childNodes).find(
|
|
1296
|
+
(d) => d !== l && d.nodeType === 1
|
|
1297
|
+
);
|
|
1298
|
+
if (c)
|
|
1299
|
+
if (this._prevVNode && (this._prevVNode.type !== s.type || this._prevVNode.key !== s.key)) {
|
|
1300
|
+
const d = g(s);
|
|
1301
|
+
d && (this.shadowRoot.contains(c) && this.shadowRoot.replaceChild(d, c), c = d);
|
|
1302
|
+
} else
|
|
1303
|
+
L(c, this._prevVNode, s);
|
|
1304
|
+
else
|
|
1305
|
+
c = g(s), c && this.shadowRoot.appendChild(c);
|
|
1306
|
+
s.dom = c;
|
|
1307
|
+
}
|
|
1308
|
+
this._prevVNode = s, this.forceSyncControlledInputs(), this.lastCompiledTemplate = null;
|
|
1309
|
+
} else {
|
|
1310
|
+
const n = !this.shadowRoot.firstElementChild, a = this.lastCompiledTemplate?.id === t.id;
|
|
1311
|
+
if (n) {
|
|
1312
|
+
const o = j(t, this.stateObj, this.api);
|
|
1313
|
+
this.shadowRoot.appendChild(o);
|
|
1314
|
+
} else if (a && this.shadowRoot.firstElementChild) {
|
|
1315
|
+
const o = this.lastState;
|
|
1316
|
+
X(t, this.shadowRoot.firstElementChild, this.stateObj, this.api, o || void 0);
|
|
1317
|
+
} else {
|
|
1318
|
+
const o = j(t, this.stateObj, this.api);
|
|
1319
|
+
let s = this.shadowRoot.querySelector("style");
|
|
1320
|
+
s || (s = document.createElement("style"), this.shadowRoot.insertBefore(s, this.shadowRoot.firstChild)), this.config.style ? s.textContent = typeof this.config.style == "function" ? this.config.style(this.stateObj) : this.config.style : s.textContent = "";
|
|
1321
|
+
let i = this.shadowRoot.querySelector("[data-root]");
|
|
1322
|
+
for (i || (i = document.createElement("div"), i.setAttribute("data-root", ""), this.shadowRoot.appendChild(i)); i.firstChild; )
|
|
1323
|
+
i.removeChild(i.firstChild);
|
|
1324
|
+
i.appendChild(o);
|
|
1325
|
+
}
|
|
1326
|
+
this.lastCompiledTemplate = t;
|
|
1327
|
+
}
|
|
1328
|
+
this.lastState = e(this.stateObj), this.updateStyle(), this.processRefs(), this.bindEvents(), this.syncControlledInputsAndEvents();
|
|
1329
|
+
} catch (e) {
|
|
1330
|
+
this._handleRenderError(e);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
/**
|
|
1334
|
+
* Internal: handle render errors and error boundaries.
|
|
1335
|
+
* Logs details and allows fallback UI.
|
|
1336
|
+
* @param error - Error object
|
|
1337
|
+
*/
|
|
1338
|
+
_handleRenderError(t) {
|
|
1339
|
+
if (this._hasError = !0, this.config.debug && console.error(`[runtime] Render error in <${this.tagName.toLowerCase()}>:`, t), _.forEach((e) => e.onError?.(t instanceof Error ? t : new Error(String(t)), this.stateObj, this.api)), "onError" in this.config && typeof this.config.onError == "function")
|
|
1340
|
+
try {
|
|
1341
|
+
this.config.onError(t instanceof Error ? t : new Error(String(t)), this.api.state, this.api);
|
|
1342
|
+
} catch (e) {
|
|
1343
|
+
this.config.debug && console.error("[runtime] Error in onError handler:", e);
|
|
1344
|
+
}
|
|
1345
|
+
this.renderError(t instanceof Error ? t : new Error(String(t)));
|
|
1346
|
+
}
|
|
1347
|
+
/**
|
|
1348
|
+
* Schedule a render using requestAnimationFrame, batching multiple state changes.
|
|
1349
|
+
*/
|
|
1350
|
+
scheduleRender() {
|
|
1351
|
+
this.rafId !== void 0 && this.rafId !== null && cancelAnimationFrame(this.rafId), this.rafId = requestAnimationFrame(() => {
|
|
1352
|
+
this.render(), this.rafId = null;
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Updates the style element in the shadow root based on the current state.
|
|
1357
|
+
*/
|
|
1358
|
+
updateStyle() {
|
|
1359
|
+
const t = this.shadowRoot.querySelector("style");
|
|
1360
|
+
if (!t || !this.config.style) return;
|
|
1361
|
+
const e = typeof this.config.style == "function" ? this.config.style(this.api.state) : this.config.style;
|
|
1362
|
+
t.textContent = e;
|
|
1363
|
+
}
|
|
1364
|
+
/**
|
|
1365
|
+
* Processes and attaches ref handlers for elements with data-ref attributes.
|
|
1366
|
+
*/
|
|
1367
|
+
processRefs() {
|
|
1368
|
+
if (!this.config.refs) return;
|
|
1369
|
+
const t = /* @__PURE__ */ new WeakMap();
|
|
1370
|
+
Object.entries(this.config.refs).forEach(([e, n]) => {
|
|
1371
|
+
const a = this.shadowRoot.querySelector(`[data-ref="${e}"]`);
|
|
1372
|
+
if (a) {
|
|
1373
|
+
t.has(a) || t.set(a, /* @__PURE__ */ new Set());
|
|
1374
|
+
const o = t.get(a), s = a.addEventListener;
|
|
1375
|
+
a.addEventListener = function(i, l, c) {
|
|
1376
|
+
const d = `${i}`;
|
|
1377
|
+
o.has(d) || (o.add(d), s.call(a, i, l, c));
|
|
1378
|
+
}, a.setAttribute("data-refs-processed", "true");
|
|
1379
|
+
try {
|
|
1380
|
+
n(a, this.api.state, this.api);
|
|
1381
|
+
} catch (i) {
|
|
1382
|
+
this._handleRenderError(i);
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Automatically bind events for elements with data-on-* attributes.
|
|
1389
|
+
* Ensures events are not attached multiple times after rerender.
|
|
1390
|
+
*/
|
|
1391
|
+
bindEvents() {
|
|
1392
|
+
if (!this.shadowRoot) return;
|
|
1393
|
+
const t = document.createTreeWalker(this.shadowRoot, NodeFilter.SHOW_ELEMENT);
|
|
1394
|
+
let e = t.nextNode();
|
|
1395
|
+
for (; e; ) {
|
|
1396
|
+
const n = e;
|
|
1397
|
+
Array.from(n.attributes).forEach((a) => {
|
|
1398
|
+
if (a.name.startsWith("data-on-")) {
|
|
1399
|
+
const o = a.name.slice(8), s = a.value, i = this.config[s];
|
|
1400
|
+
if (typeof i == "function") {
|
|
1401
|
+
n._boundHandlers && n._boundHandlers[o] && n.removeEventListener(o, n._boundHandlers[o]);
|
|
1402
|
+
const l = (c) => {
|
|
1403
|
+
i.call(this.config, c, this.api.state, this.api), this.syncControlledInputsAndEvents();
|
|
1404
|
+
};
|
|
1405
|
+
n.addEventListener(o, l), n._boundHandlers || (n._boundHandlers = {}), n._boundHandlers[o] = l;
|
|
1406
|
+
} else
|
|
1407
|
+
this.config.debug && console.warn(`[runtime] Handler '${s}' not found on config for event '${o}'`, n);
|
|
1408
|
+
}
|
|
1409
|
+
}), e = t.nextNode();
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
/**
|
|
1413
|
+
* Renders a fallback error UI in the shadow root.
|
|
1414
|
+
* @param error - Error object
|
|
1415
|
+
*/
|
|
1416
|
+
renderError(t) {
|
|
1417
|
+
const e = this.config.style ? typeof this.config.style == "function" ? this.config.style(this.api.state) : this.config.style : "";
|
|
1418
|
+
this.shadowRoot.innerHTML = `
|
|
1419
|
+
<style>${e}</style>
|
|
1420
|
+
<div style="color: red; border: 1px solid red; padding: 1rem; border-radius: 4px;">
|
|
1421
|
+
<h3>Error Boundary</h3>
|
|
1422
|
+
<div>Error: ${t.message}</div>
|
|
1423
|
+
</div>
|
|
1424
|
+
`;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
function ft(r, t) {
|
|
1428
|
+
const e = b(t), n = b(t.state);
|
|
1429
|
+
if (t = e, t.state = n, t.debug && console.log(`[runtime] Debugging component: ${r}`, t), !r || !t.template || !t.state) {
|
|
1430
|
+
t && typeof t.onError == "function" && t.onError(new Error("Component requires tag, template, and state"), t.state, {
|
|
1431
|
+
state: t.state,
|
|
1432
|
+
emit: () => {
|
|
1433
|
+
},
|
|
1434
|
+
onGlobal: () => () => {
|
|
1435
|
+
},
|
|
1436
|
+
offGlobal: () => {
|
|
1437
|
+
},
|
|
1438
|
+
emitGlobal: () => {
|
|
1439
|
+
}
|
|
1440
|
+
}), t && t.debug && console.error("[runtime] Malformed config:", { tag: r, config: t });
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
if (_.forEach((c) => {
|
|
1444
|
+
try {
|
|
1445
|
+
c.onInit?.(t);
|
|
1446
|
+
} catch (d) {
|
|
1447
|
+
t && typeof t.onError == "function" && t.onError(d instanceof Error ? d : new Error(String(d)), t.state, {
|
|
1448
|
+
state: t.state,
|
|
1449
|
+
emit: () => {
|
|
1450
|
+
},
|
|
1451
|
+
onGlobal: () => () => {
|
|
1452
|
+
},
|
|
1453
|
+
offGlobal: () => {
|
|
1454
|
+
},
|
|
1455
|
+
emitGlobal: () => {
|
|
1456
|
+
}
|
|
1457
|
+
}), t && t.debug && console.error("[runtime] Plugin onInit error:", d);
|
|
1458
|
+
}
|
|
1459
|
+
}), (typeof window < "u" && window.VITE_DEV_HMR || typeof import.meta < "u" && void 0) && customElements.get(r))
|
|
1460
|
+
try {
|
|
1461
|
+
document.querySelectorAll(r).forEach((c) => c.remove()), window.customElements._definitions && delete window.customElements._definitions[r];
|
|
1462
|
+
} catch {
|
|
1463
|
+
}
|
|
1464
|
+
if (customElements.get(r)) {
|
|
1465
|
+
t.debug && console.warn(`[runtime] Component "${r}" already registered`);
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
const s = M(t.state, t.computed);
|
|
1469
|
+
t.state = s, t._subscribe = s.subscribe;
|
|
1470
|
+
const i = Object.keys(t.state).filter(
|
|
1471
|
+
(c) => ["string", "number", "boolean"].includes(typeof t.state[c])
|
|
1472
|
+
), l = class extends J {
|
|
1473
|
+
static get observedAttributes() {
|
|
1474
|
+
return i;
|
|
1475
|
+
}
|
|
1476
|
+
constructor() {
|
|
1477
|
+
super();
|
|
1478
|
+
}
|
|
1479
|
+
};
|
|
1480
|
+
customElements.get(r) || (window.__componentRegistry = window.__componentRegistry || {}, window.__componentRegistry[r] = t, customElements.define(r, l));
|
|
1481
|
+
}
|
|
1482
|
+
export {
|
|
1483
|
+
Q as Store,
|
|
1484
|
+
at as classes,
|
|
1485
|
+
rt as compile,
|
|
1486
|
+
lt as compileTemplate,
|
|
1487
|
+
ft as component,
|
|
1488
|
+
k as createVNodeFromElement,
|
|
1489
|
+
st as css,
|
|
1490
|
+
b as deepSanitizeObject,
|
|
1491
|
+
C as eventBus,
|
|
1492
|
+
et as generateHydrationScript,
|
|
1493
|
+
x as getVNodeKey,
|
|
1494
|
+
nt as html,
|
|
1495
|
+
S as isPromise,
|
|
1496
|
+
g as mountVNode,
|
|
1497
|
+
ot as on,
|
|
1498
|
+
Y as parseVNodeFromHTML,
|
|
1499
|
+
L as patchVNode,
|
|
1500
|
+
it as ref,
|
|
1501
|
+
j as renderCompiledTemplate,
|
|
1502
|
+
tt as renderComponentsToString,
|
|
1503
|
+
W as renderToString,
|
|
1504
|
+
_ as runtimePlugins,
|
|
1505
|
+
D as safeReplaceChild,
|
|
1506
|
+
ct as styles,
|
|
1507
|
+
X as updateCompiledTemplate,
|
|
1508
|
+
G as useDataModel,
|
|
1509
|
+
dt as useRuntimePlugin
|
|
1510
|
+
};
|
|
1511
|
+
//# sourceMappingURL=custom-elements-runtime.es.js.map
|