sibujs 4.0.1 → 4.2.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 +29 -1
- package/dist/browser.cjs +21 -22
- package/dist/browser.js +6 -6
- package/dist/build.cjs +363 -110
- package/dist/build.js +15 -15
- package/dist/cdn.dev.global.js +398 -0
- package/dist/cdn.global.js +10 -10
- package/dist/{chunk-AD5YSZAN.js → chunk-2LOYNGBY.js} +2 -2
- package/dist/{chunk-VPP2FONR.js → chunk-2WLZ6757.js} +17 -10
- package/dist/{chunk-UNAYBND2.js → chunk-3VTSPNC5.js} +2 -2
- package/dist/{chunk-TIRZCERI.js → chunk-5DXA2J44.js} +1 -1
- package/dist/{chunk-QD4I7ZGZ.js → chunk-6ZGI3RBE.js} +149 -33
- package/dist/{chunk-JYXQCELN.js → chunk-7ALC7KGE.js} +13 -7
- package/dist/{chunk-XDOMTQHM.js → chunk-7VEC3UD2.js} +3 -3
- package/dist/{chunk-PTLJ7NQX.js → chunk-BMYYZWHX.js} +3 -3
- package/dist/{chunk-PHB3MCK4.js → chunk-BNKV4REK.js} +7 -8
- package/dist/{chunk-XET2RUJF.js → chunk-FOWBJZDL.js} +7 -8
- package/dist/{chunk-5BVKQX2F.js → chunk-GDUNQCCW.js} +4 -4
- package/dist/{chunk-TVURNWDK.js → chunk-HECVW343.js} +20 -9
- package/dist/{chunk-PHRH66Y7.js → chunk-HJEPKZ2O.js} +7 -8
- package/dist/{chunk-PJDSI2K7.js → chunk-HZEBO22J.js} +125 -42
- package/dist/{chunk-5INI7D2L.js → chunk-IXKSNWV5.js} +74 -11
- package/dist/{chunk-IKVUUE7D.js → chunk-KE4KWRDY.js} +3 -3
- package/dist/{chunk-AW6HPJ3L.js → chunk-KKLW7YWL.js} +1 -1
- package/dist/{chunk-XZMCVZW6.js → chunk-NEYWS4EX.js} +6 -6
- package/dist/{chunk-HYCCIYNS.js → chunk-NIOYEGBQ.js} +13 -14
- package/dist/{chunk-YLV26GAJ.js → chunk-OUWAIXIK.js} +3 -3
- package/dist/{chunk-E72G5JSK.js → chunk-P3DHXBR6.js} +1 -1
- package/dist/{chunk-J4WZMCIW.js → chunk-QCFLDD7C.js} +5 -5
- package/dist/{chunk-DHDZ7IVN.js → chunk-QKRPLZ2V.js} +7 -8
- package/dist/{chunk-52ERIVFD.js → chunk-QL4UREZO.js} +6 -6
- package/dist/{chunk-TNEHT3NG.js → chunk-SF3TB4K6.js} +8 -8
- package/dist/{chunk-HPXTUPJD.js → chunk-XZ2N525U.js} +6 -6
- package/dist/{customElement-CKLoO63w.d.ts → customElement-CNZxEB9G.d.cts} +77 -0
- package/dist/{customElement-CKLoO63w.d.cts → customElement-CNZxEB9G.d.ts} +77 -0
- package/dist/data.cjs +23 -25
- package/dist/data.js +9 -9
- package/dist/devtools.cjs +26 -23
- package/dist/devtools.d.cts +1 -1
- package/dist/devtools.d.ts +1 -1
- package/dist/devtools.js +8 -8
- package/dist/{dispose-4FZ6OFXR.js → dispose-46BOMMQJ.js} +2 -2
- package/dist/ecosystem.cjs +230 -68
- package/dist/ecosystem.js +11 -11
- package/dist/extras.cjs +251 -87
- package/dist/extras.d.cts +2 -2
- package/dist/extras.d.ts +2 -2
- package/dist/extras.js +25 -25
- package/dist/index.cjs +358 -104
- package/dist/index.d.cts +287 -34
- package/dist/index.d.ts +287 -34
- package/dist/index.js +17 -15
- package/dist/{introspect-RjLfIFpL.d.cts → introspect-D5xF-0Es.d.cts} +7 -0
- package/dist/{introspect-DOZfmC-4.d.ts → introspect-rSBIxEQi.d.ts} +7 -0
- package/dist/motion.cjs +21 -23
- package/dist/motion.js +5 -5
- package/dist/patterns.cjs +24 -25
- package/dist/patterns.d.cts +8 -0
- package/dist/patterns.d.ts +8 -0
- package/dist/patterns.js +7 -7
- package/dist/performance.cjs +24 -28
- package/dist/performance.js +9 -9
- package/dist/plugins.cjs +268 -87
- package/dist/plugins.d.cts +92 -0
- package/dist/plugins.d.ts +92 -0
- package/dist/plugins.js +18 -17
- package/dist/{ssr-JFP5535P.js → ssr-Y7XOEPEN.js} +5 -5
- package/dist/ssr.cjs +241 -80
- package/dist/ssr.js +12 -12
- package/dist/testing.cjs +19 -20
- package/dist/testing.js +5 -5
- package/dist/ui.cjs +106 -44
- package/dist/ui.d.cts +7 -1
- package/dist/ui.d.ts +7 -1
- package/dist/ui.js +10 -10
- package/dist/widgets.cjs +21 -22
- package/dist/widgets.d.cts +8 -0
- package/dist/widgets.d.ts +8 -0
- package/dist/widgets.js +9 -9
- package/package.json +11 -2
package/dist/index.cjs
CHANGED
|
@@ -95,6 +95,7 @@ __export(index_exports, {
|
|
|
95
95
|
enhance: () => enhance,
|
|
96
96
|
enhanceAll: () => enhanceAll,
|
|
97
97
|
enqueueBatchedSignal: () => enqueueBatchedSignal,
|
|
98
|
+
external: () => external,
|
|
98
99
|
fieldset: () => fieldset,
|
|
99
100
|
figcaption: () => figcaption,
|
|
100
101
|
figure: () => figure,
|
|
@@ -345,23 +346,28 @@ function reportError(error, context2) {
|
|
|
345
346
|
}
|
|
346
347
|
|
|
347
348
|
// src/core/dev.ts
|
|
349
|
+
var DEV = typeof __SIBU_DEV__ !== "undefined" ? !!__SIBU_DEV__ : typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
|
|
350
|
+
// safe default: off in browser, on in test/dev Node
|
|
351
|
+
typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
|
|
352
|
+
);
|
|
348
353
|
function isDev() {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
// this branch is unreachable here.
|
|
353
|
-
/* v8 ignore next 2 */
|
|
354
|
-
typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
|
|
355
|
-
);
|
|
354
|
+
if (typeof globalThis.__SIBU_DEV__ !== "undefined") return !!globalThis.__SIBU_DEV__;
|
|
355
|
+
if (typeof __SIBU_DEV__ !== "undefined") return !!__SIBU_DEV__;
|
|
356
|
+
return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
|
|
356
357
|
}
|
|
357
|
-
var _isDev = isDev();
|
|
358
358
|
function devAssert(condition, message) {
|
|
359
|
-
if (
|
|
359
|
+
if ((typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) && !condition) {
|
|
360
360
|
throw new Error(`[SibuJS] ${message}`);
|
|
361
361
|
}
|
|
362
362
|
}
|
|
363
|
+
function devWarnLazy(build) {
|
|
364
|
+
if (typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) {
|
|
365
|
+
const message = build();
|
|
366
|
+
if (message) console.warn(`[SibuJS] ${message}`);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
363
369
|
function devWarn(message) {
|
|
364
|
-
if (
|
|
370
|
+
if (typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) {
|
|
365
371
|
console.warn(`[SibuJS] ${message}`);
|
|
366
372
|
}
|
|
367
373
|
}
|
|
@@ -376,7 +382,6 @@ function reportDrainRunaway(label2, executed, remaining) {
|
|
|
376
382
|
{ executed, remaining }
|
|
377
383
|
);
|
|
378
384
|
}
|
|
379
|
-
var _isDev2 = isDev();
|
|
380
385
|
var activeBindingCount = 0;
|
|
381
386
|
function registerDisposer(node, teardown) {
|
|
382
387
|
let disposers = elementDisposers.get(node);
|
|
@@ -385,7 +390,7 @@ function registerDisposer(node, teardown) {
|
|
|
385
390
|
elementDisposers.set(node, disposers);
|
|
386
391
|
}
|
|
387
392
|
disposers.push(teardown);
|
|
388
|
-
if (
|
|
393
|
+
if (DEV) activeBindingCount++;
|
|
389
394
|
}
|
|
390
395
|
function unregisterDisposer(node, teardown) {
|
|
391
396
|
const disposers = elementDisposers.get(node);
|
|
@@ -393,7 +398,7 @@ function unregisterDisposer(node, teardown) {
|
|
|
393
398
|
const index = disposers.indexOf(teardown);
|
|
394
399
|
if (index === -1) return;
|
|
395
400
|
disposers.splice(index, 1);
|
|
396
|
-
if (
|
|
401
|
+
if (DEV) activeBindingCount--;
|
|
397
402
|
if (disposers.length === 0) elementDisposers.delete(node);
|
|
398
403
|
}
|
|
399
404
|
function dispose(node) {
|
|
@@ -417,13 +422,13 @@ function dispose(node) {
|
|
|
417
422
|
if (!pending || pending.length === 0) break;
|
|
418
423
|
const snapshot = pending.slice();
|
|
419
424
|
elementDisposers.delete(current);
|
|
420
|
-
if (
|
|
425
|
+
if (DEV) activeBindingCount -= snapshot.length;
|
|
421
426
|
for (let i3 = 0; i3 < snapshot.length; i3++) {
|
|
422
427
|
if (executed >= MAX_DRAIN_TEARDOWNS) {
|
|
423
428
|
const rest = snapshot.slice(i3);
|
|
424
429
|
const added = elementDisposers.get(current);
|
|
425
430
|
elementDisposers.set(current, added ? rest.concat(added) : rest);
|
|
426
|
-
if (
|
|
431
|
+
if (DEV) activeBindingCount += rest.length;
|
|
427
432
|
reportDrainRunaway("dispose", executed, rest.length + (added?.length ?? 0));
|
|
428
433
|
runaway = true;
|
|
429
434
|
break;
|
|
@@ -451,7 +456,7 @@ function replaceChildrenSafely(parent, ...next) {
|
|
|
451
456
|
parent.replaceChildren(...next);
|
|
452
457
|
}
|
|
453
458
|
function checkLeaks(warnThreshold = 0) {
|
|
454
|
-
if (!
|
|
459
|
+
if (!DEV) return 0;
|
|
455
460
|
if (warnThreshold > 0 && activeBindingCount > warnThreshold) {
|
|
456
461
|
devWarn(
|
|
457
462
|
`checkLeaks: ${activeBindingCount} active DOM bindings detected. Expected \u2264${warnThreshold}. This may indicate a component was removed from the DOM without calling dispose().`
|
|
@@ -553,20 +558,75 @@ function decodeCssEscapes(value) {
|
|
|
553
558
|
}
|
|
554
559
|
return out;
|
|
555
560
|
}
|
|
556
|
-
|
|
557
|
-
|
|
561
|
+
var BLOCKED_CSS_NEEDLES = [
|
|
562
|
+
"url(",
|
|
563
|
+
"expression(",
|
|
564
|
+
"javascript:",
|
|
565
|
+
"vbscript:",
|
|
566
|
+
"-moz-binding",
|
|
567
|
+
"behavior:",
|
|
568
|
+
"@import",
|
|
569
|
+
"image-set(",
|
|
570
|
+
"filter:progid"
|
|
571
|
+
];
|
|
572
|
+
function describeElement(el) {
|
|
573
|
+
if (!el) return "an element";
|
|
574
|
+
let out = el.tagName.toLowerCase();
|
|
575
|
+
const id = el.getAttribute("id");
|
|
576
|
+
if (id) out += `#${id}`;
|
|
577
|
+
const cls = el.getAttribute("class");
|
|
578
|
+
if (cls) out += `.${cls.trim().split(/\s+/).slice(0, 3).join(".")}`;
|
|
579
|
+
return `<${out}>`;
|
|
580
|
+
}
|
|
581
|
+
var warnedDeclarations = /* @__PURE__ */ new Set();
|
|
582
|
+
var MAX_WARNED_DECLARATIONS = 100;
|
|
583
|
+
var declarationCapAnnounced = false;
|
|
584
|
+
function warnDroppedDeclaration(value, reason, ctx) {
|
|
585
|
+
devWarnLazy(() => {
|
|
586
|
+
const reasons = [
|
|
587
|
+
"url() in an inline style is an exfiltration channel: the browser fetches it immediately and the URL's path or query can carry page data off-origin",
|
|
588
|
+
"expression() executes arbitrary script in legacy engines",
|
|
589
|
+
"the javascript: scheme executes script",
|
|
590
|
+
"the vbscript: scheme executes script",
|
|
591
|
+
"-moz-binding loads and runs remote XBL",
|
|
592
|
+
"behavior: loads and runs a remote HTC script",
|
|
593
|
+
"@import pulls in a remote stylesheet",
|
|
594
|
+
"image-set() fetches remote images, the same exfiltration channel as url()",
|
|
595
|
+
"filter:progid activates legacy scriptable filters"
|
|
596
|
+
];
|
|
597
|
+
const declaration = ctx?.property ? `${ctx.property}: ${value}` : value;
|
|
598
|
+
const where = describeElement(ctx?.element);
|
|
599
|
+
const key = `${where}|${declaration}`;
|
|
600
|
+
if (warnedDeclarations.has(key)) return "";
|
|
601
|
+
if (warnedDeclarations.size >= MAX_WARNED_DECLARATIONS) {
|
|
602
|
+
if (declarationCapAnnounced) return "";
|
|
603
|
+
declarationCapAnnounced = true;
|
|
604
|
+
return `${MAX_WARNED_DECLARATIONS} distinct dropped style declarations have been reported; suppressing further ones for the rest of this session so they cannot flood the console. The declarations are still dropped \u2014 only the reporting stops. Fix the reported ones and reload to see any that remain.`;
|
|
605
|
+
}
|
|
606
|
+
warnedDeclarations.add(key);
|
|
607
|
+
return `style declaration "${declaration}" was dropped by the style sanitizer on ${where} \u2014 ${reasons[reason]}. The element renders without it, which usually looks like a missing image rather than an error. Sanctioned alternatives: for a content image use an <img> element (or a <picture>); for decoration, put the rule in a stylesheet and set a class instead \u2014 stylesheet CSS is not sanitized, only inline style attributes are. If this declaration is not yours, it came from untrusted data and the drop just prevented an exfiltration.`;
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
function blockedCssReason(value) {
|
|
611
|
+
if (!CSS_DANGER_GATE.test(value)) return -1;
|
|
558
612
|
const normalized = value.includes("\\") ? decodeCssEscapes(value) : value;
|
|
559
613
|
const lower = normalized.toLowerCase().replace(/\s+/g, "");
|
|
560
|
-
|
|
561
|
-
return
|
|
614
|
+
for (let i2 = 0; i2 < BLOCKED_CSS_NEEDLES.length; i2++) {
|
|
615
|
+
if (lower.includes(BLOCKED_CSS_NEEDLES[i2])) return i2;
|
|
562
616
|
}
|
|
563
|
-
return
|
|
617
|
+
return -1;
|
|
618
|
+
}
|
|
619
|
+
function sanitizeCSSValue(value, context2) {
|
|
620
|
+
const reason = blockedCssReason(value);
|
|
621
|
+
if (reason === -1) return value;
|
|
622
|
+
warnDroppedDeclaration(value, reason, context2);
|
|
623
|
+
return "";
|
|
564
624
|
}
|
|
565
|
-
function sanitizeStyleAttribute(cssText) {
|
|
625
|
+
function sanitizeStyleAttribute(cssText, context2) {
|
|
566
626
|
const input2 = String(cssText);
|
|
567
627
|
if (input2.trim() === "") return "";
|
|
568
628
|
if (typeof document === "undefined") {
|
|
569
|
-
return sanitizeCSSValue(input2) === "" ? "" : input2;
|
|
629
|
+
return sanitizeCSSValue(input2, context2) === "" ? "" : input2;
|
|
570
630
|
}
|
|
571
631
|
const probe = document.createElement("div");
|
|
572
632
|
try {
|
|
@@ -578,8 +638,12 @@ function sanitizeStyleAttribute(cssText) {
|
|
|
578
638
|
for (let i2 = 0; i2 < probe.style.length; i2++) {
|
|
579
639
|
const property = probe.style[i2];
|
|
580
640
|
const value = probe.style.getPropertyValue(property);
|
|
581
|
-
|
|
582
|
-
if (
|
|
641
|
+
let reason = blockedCssReason(value);
|
|
642
|
+
if (reason === -1) reason = blockedCssReason(`${property}:${value}`);
|
|
643
|
+
if (reason !== -1) {
|
|
644
|
+
warnDroppedDeclaration(value, reason, { property, element: context2?.element });
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
583
647
|
const priority = probe.style.getPropertyPriority(property);
|
|
584
648
|
declarations.push(`${property}: ${value}${priority ? ` !${priority}` : ""}`);
|
|
585
649
|
}
|
|
@@ -607,16 +671,15 @@ var HTML_CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set(["srcdoc"]);
|
|
|
607
671
|
function isHtmlContentAttribute(name) {
|
|
608
672
|
return HTML_CONTENT_ATTRIBUTES.has(name.toLowerCase());
|
|
609
673
|
}
|
|
610
|
-
function sanitizeAttributeString(attr, value) {
|
|
674
|
+
function sanitizeAttributeString(attr, value, context2) {
|
|
611
675
|
const lower = attr.toLowerCase();
|
|
612
676
|
if (lower === "srcset") return sanitizeSrcset(value);
|
|
613
677
|
if (URL_ATTRIBUTES.has(lower)) return sanitizeUrl(value);
|
|
614
|
-
if (lower === "style") return sanitizeStyleAttribute(value);
|
|
678
|
+
if (lower === "style") return sanitizeStyleAttribute(value, context2);
|
|
615
679
|
return value;
|
|
616
680
|
}
|
|
617
681
|
|
|
618
682
|
// src/utils/setSafeAttribute.ts
|
|
619
|
-
var _isDev3 = isDev();
|
|
620
683
|
var HTML_NS = "http://www.w3.org/1999/xhtml";
|
|
621
684
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
622
685
|
var XLINK_NS = "http://www.w3.org/1999/xlink";
|
|
@@ -639,7 +702,7 @@ function setSafeAttribute(el, name, value, options = {}) {
|
|
|
639
702
|
const ns = namespaceFor(el, name);
|
|
640
703
|
const localName = ns ? name.slice(name.indexOf(":") + 1) : name;
|
|
641
704
|
if (isEventHandlerAttr(name)) {
|
|
642
|
-
if (
|
|
705
|
+
if (DEV) {
|
|
643
706
|
devWarn(
|
|
644
707
|
`${options.label ?? "setSafeAttribute"}: refusing to set event-handler attribute "${name}". Its value would be evaluated as JavaScript. Use on:{ ${name.slice(2)}: fn } instead.`
|
|
645
708
|
);
|
|
@@ -654,7 +717,7 @@ function setSafeAttribute(el, name, value, options = {}) {
|
|
|
654
717
|
return true;
|
|
655
718
|
}
|
|
656
719
|
if (isHtmlContentAttribute(name)) {
|
|
657
|
-
if (
|
|
720
|
+
if (DEV) {
|
|
658
721
|
devWarn(
|
|
659
722
|
`${options.label ?? "setSafeAttribute"}: refusing to set "${name}". The browser parses this attribute as a nested HTML document, so a generic string value cannot be made safe \u2014 attribute escaping is undone before the parse. Build the frame's content another way.`
|
|
660
723
|
);
|
|
@@ -683,7 +746,7 @@ function setSafeAttribute(el, name, value, options = {}) {
|
|
|
683
746
|
setProp(el, idlName, idlName === "checked" ? Boolean(value) : str);
|
|
684
747
|
return true;
|
|
685
748
|
}
|
|
686
|
-
const safe = sanitizeAttributeString(name, str);
|
|
749
|
+
const safe = sanitizeAttributeString(name, str, { element: el });
|
|
687
750
|
const current = ns ? el.getAttributeNS(ns, localName) : el.getAttribute(name);
|
|
688
751
|
if (current === safe) return true;
|
|
689
752
|
if (ns) el.setAttributeNS(ns, name, safe);
|
|
@@ -909,7 +972,14 @@ function reactiveBinding(commit, ownerNode) {
|
|
|
909
972
|
subscriber._disposed = false;
|
|
910
973
|
subscriber._errorPhase = "binding";
|
|
911
974
|
subscriber._errorNode = ownerNode;
|
|
912
|
-
|
|
975
|
+
try {
|
|
976
|
+
run();
|
|
977
|
+
} catch (err) {
|
|
978
|
+
subscriber._disposed = true;
|
|
979
|
+
subscriber._errorNode = void 0;
|
|
980
|
+
cleanup(subscriber);
|
|
981
|
+
throw err;
|
|
982
|
+
}
|
|
913
983
|
return subscriber._dispose ?? (subscriber._dispose = () => {
|
|
914
984
|
subscriber._disposed = true;
|
|
915
985
|
subscriber._errorNode = void 0;
|
|
@@ -1140,17 +1210,16 @@ function forEachSubscriber(signal2, visit) {
|
|
|
1140
1210
|
}
|
|
1141
1211
|
|
|
1142
1212
|
// src/reactivity/track.ts
|
|
1143
|
-
var
|
|
1144
|
-
var _runtimeVersion = true ? "4.0.1" : "dev";
|
|
1213
|
+
var _runtimeVersion = true ? "4.2.0" : "dev";
|
|
1145
1214
|
var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
|
|
1146
1215
|
function resolveReactiveApi() {
|
|
1147
1216
|
const g2 = globalThis;
|
|
1148
1217
|
const existing = g2[REGISTRY_KEY];
|
|
1149
1218
|
if (existing) {
|
|
1150
|
-
if (
|
|
1219
|
+
if (DEV && !existing.__dupWarned) {
|
|
1151
1220
|
existing.__dupWarned = true;
|
|
1152
1221
|
devWarn(
|
|
1153
|
-
`Multiple instances of the reactive runtime detected on this page (active: ${existing.version}, duplicate: ${_runtimeVersion}). Reactivity
|
|
1222
|
+
`Multiple instances of the reactive runtime detected on this page (active: ${existing.version}, duplicate: ${_runtimeVersion}). Reactivity is NOT broken and no bundler configuration is required: every copy routes through the first one via a global registry, so signal writes still reach subscribers registered by any copy. This is only a SIZE issue \u2014 the duplicate copy's bytes are downloaded and parsed for nothing. If you want them back, de-duplicate sibujs in your bundler (Vite: resolve.dedupe: ['sibujs']). Mismatched VERSIONS above are worth fixing regardless, since only the first one loaded is used.`
|
|
1154
1223
|
);
|
|
1155
1224
|
}
|
|
1156
1225
|
return existing;
|
|
@@ -1249,8 +1318,101 @@ function bindDynamic(el, nameGetter, valueGetter) {
|
|
|
1249
1318
|
};
|
|
1250
1319
|
}
|
|
1251
1320
|
|
|
1321
|
+
// src/core/rendering/focusPreservation.ts
|
|
1322
|
+
var IDENTITY_ATTRS = ["data-focus-key", "id", "name"];
|
|
1323
|
+
var SELECTABLE_TYPES = /* @__PURE__ */ new Set(["text", "search", "url", "tel", "password", ""]);
|
|
1324
|
+
function selectionOf(el) {
|
|
1325
|
+
const tag = el.tagName;
|
|
1326
|
+
const selectable = tag === "TEXTAREA" || tag === "INPUT" && SELECTABLE_TYPES.has(el.type.toLowerCase());
|
|
1327
|
+
if (!selectable) return { start: null, end: null, direction: null };
|
|
1328
|
+
const field = el;
|
|
1329
|
+
return { start: field.selectionStart, end: field.selectionEnd, direction: field.selectionDirection };
|
|
1330
|
+
}
|
|
1331
|
+
function captureFocusWithin(nodes) {
|
|
1332
|
+
if (nodes.length === 0 || typeof document === "undefined") return null;
|
|
1333
|
+
const active = document.activeElement;
|
|
1334
|
+
if (!active || active === document.body) return null;
|
|
1335
|
+
let inside = false;
|
|
1336
|
+
for (let i2 = 0; i2 < nodes.length; i2++) {
|
|
1337
|
+
const n = nodes[i2];
|
|
1338
|
+
if (n === active || n.nodeType === 1 && n.contains(active)) {
|
|
1339
|
+
inside = true;
|
|
1340
|
+
break;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
if (!inside) return null;
|
|
1344
|
+
let attr = null;
|
|
1345
|
+
let value = null;
|
|
1346
|
+
for (let i2 = 0; i2 < IDENTITY_ATTRS.length; i2++) {
|
|
1347
|
+
const candidate = active.getAttribute(IDENTITY_ATTRS[i2]);
|
|
1348
|
+
if (candidate) {
|
|
1349
|
+
attr = IDENTITY_ATTRS[i2];
|
|
1350
|
+
value = candidate;
|
|
1351
|
+
break;
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
return { el: active, attr, value, tag: active.tagName, ...selectionOf(active) };
|
|
1355
|
+
}
|
|
1356
|
+
function quote(value) {
|
|
1357
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
1358
|
+
}
|
|
1359
|
+
function findUniqueByIdentity(nodes, selector) {
|
|
1360
|
+
let el = null;
|
|
1361
|
+
let count = 0;
|
|
1362
|
+
for (let i2 = 0; i2 < nodes.length; i2++) {
|
|
1363
|
+
const n = nodes[i2];
|
|
1364
|
+
if (n.nodeType !== 1) continue;
|
|
1365
|
+
const root = n;
|
|
1366
|
+
if (root.matches(selector)) {
|
|
1367
|
+
count++;
|
|
1368
|
+
el ?? (el = root);
|
|
1369
|
+
}
|
|
1370
|
+
const inner = root.querySelectorAll(selector);
|
|
1371
|
+
for (let j = 0; j < inner.length; j++) {
|
|
1372
|
+
count++;
|
|
1373
|
+
el ?? (el = inner[j]);
|
|
1374
|
+
}
|
|
1375
|
+
if (count > 1) return { el: null, count };
|
|
1376
|
+
}
|
|
1377
|
+
return count === 1 ? { el, count } : { el: null, count };
|
|
1378
|
+
}
|
|
1379
|
+
function refocus(el, snapshot) {
|
|
1380
|
+
el.focus();
|
|
1381
|
+
if (snapshot.start !== null && typeof el.setSelectionRange === "function") {
|
|
1382
|
+
const field = el;
|
|
1383
|
+
const max = field.value.length;
|
|
1384
|
+
const start = Math.min(snapshot.start, max);
|
|
1385
|
+
const end = Math.min(snapshot.end ?? start, max);
|
|
1386
|
+
field.setSelectionRange(start, end, snapshot.direction ?? "none");
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
function restoreFocusWithin(snapshot, nodes, where) {
|
|
1390
|
+
if (!snapshot) return;
|
|
1391
|
+
if (typeof document === "undefined") return;
|
|
1392
|
+
const active = document.activeElement;
|
|
1393
|
+
if (active === snapshot.el) return;
|
|
1394
|
+
if (active && active !== document.body && active !== document.documentElement) return;
|
|
1395
|
+
if (snapshot.el.isConnected) {
|
|
1396
|
+
refocus(snapshot.el, snapshot);
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
let ambiguous = false;
|
|
1400
|
+
if (snapshot.attr && snapshot.value) {
|
|
1401
|
+
const { el: replacement, count } = findUniqueByIdentity(nodes, `[${snapshot.attr}=${quote(snapshot.value)}]`);
|
|
1402
|
+
if (replacement && replacement.tagName === snapshot.tag) {
|
|
1403
|
+
refocus(replacement, snapshot);
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
ambiguous = count > 1;
|
|
1407
|
+
}
|
|
1408
|
+
devWarnLazy(() => {
|
|
1409
|
+
const id = snapshot.attr ? ` (${snapshot.attr}="${snapshot.value}")` : "";
|
|
1410
|
+
const why = ambiguous ? `several elements in the rebuilt subtree share ${snapshot.attr}="${snapshot.value}", so there is no way to tell which one the user was in \u2014 a shared name (a radio group) identifies a GROUP, not a control. Add a unique id or data-focus-key to each` : "no element in the rebuilt subtree carries the same identity. Give it a stable id, name, or data-focus-key that the rebuild reproduces";
|
|
1411
|
+
return `${where}: a reactive rebuild discarded the focused element <${snapshot.tag.toLowerCase()}>${id} and could not re-establish it, so focus fell back to <body>. An in-progress edit ends here, and an in-progress IME composition is lost outright \u2014 a composition belongs to the DOM node, so it cannot survive the node being destroyed. ${why}, or stop the rebuild from happening: key the subtree with match(() => shapeKey(), { \u2026 }) so it is re-created only when the shape actually changes, and drive the rest with reactive attributes and text children, which mutate nodes in place instead of replacing them.`;
|
|
1412
|
+
});
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1252
1415
|
// src/reactivity/bindChildNode.ts
|
|
1253
|
-
var _isDev5 = isDev();
|
|
1254
1416
|
function bindChildNode(placeholder, getter) {
|
|
1255
1417
|
let lastNodes = [];
|
|
1256
1418
|
function commit() {
|
|
@@ -1261,6 +1423,7 @@ function bindChildNode(placeholder, getter) {
|
|
|
1261
1423
|
reportError(err, { phase: "binding", name: "bindChildNode", node: placeholder });
|
|
1262
1424
|
return;
|
|
1263
1425
|
}
|
|
1426
|
+
const focused = captureFocusWithin(lastNodes);
|
|
1264
1427
|
if (result == null || typeof result === "boolean") {
|
|
1265
1428
|
for (let i2 = 0; i2 < lastNodes.length; i2++) {
|
|
1266
1429
|
const node = lastNodes[i2];
|
|
@@ -1268,6 +1431,7 @@ function bindChildNode(placeholder, getter) {
|
|
|
1268
1431
|
if (node.parentNode) node.parentNode.removeChild(node);
|
|
1269
1432
|
}
|
|
1270
1433
|
lastNodes.length = 0;
|
|
1434
|
+
restoreFocusWithin(focused, lastNodes, "bindChildNode");
|
|
1271
1435
|
return;
|
|
1272
1436
|
}
|
|
1273
1437
|
const parent = placeholder.parentNode;
|
|
@@ -1284,8 +1448,7 @@ function bindChildNode(placeholder, getter) {
|
|
|
1284
1448
|
if (item == null || typeof item === "boolean") continue;
|
|
1285
1449
|
const node = item instanceof Node ? item : document.createTextNode(String(item));
|
|
1286
1450
|
if (seen.has(node)) {
|
|
1287
|
-
if (
|
|
1288
|
-
devWarn("bindChildNode: duplicate node reference in array \u2014 only the first occurrence is rendered.");
|
|
1451
|
+
if (DEV) devWarn("bindChildNode: duplicate node reference in array \u2014 only the first occurrence is rendered.");
|
|
1289
1452
|
continue;
|
|
1290
1453
|
}
|
|
1291
1454
|
seen.add(node);
|
|
@@ -1318,13 +1481,13 @@ function bindChildNode(placeholder, getter) {
|
|
|
1318
1481
|
prev = node;
|
|
1319
1482
|
}
|
|
1320
1483
|
lastNodes = newNodes;
|
|
1484
|
+
restoreFocusWithin(focused, newNodes, "bindChildNode");
|
|
1321
1485
|
}
|
|
1322
1486
|
return reactiveBinding2(commit, placeholder);
|
|
1323
1487
|
}
|
|
1324
1488
|
|
|
1325
1489
|
// src/core/rendering/tagFactory.ts
|
|
1326
1490
|
var SVG_NS2 = "http://www.w3.org/2000/svg";
|
|
1327
|
-
var _isDev6 = isDev();
|
|
1328
1491
|
var BLOCKED_TAGS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
|
|
1329
1492
|
function isBlockedTag(tag) {
|
|
1330
1493
|
return BLOCKED_TAGS.has(tag.toLowerCase());
|
|
@@ -1342,18 +1505,9 @@ var CLOBBER_RISKY_IDS = /* @__PURE__ */ new Set([
|
|
|
1342
1505
|
"self",
|
|
1343
1506
|
"frames"
|
|
1344
1507
|
]);
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
const tokens = t.split(/\s+/);
|
|
1349
|
-
let sawClassish = false;
|
|
1350
|
-
for (let i2 = 0; i2 < tokens.length; i2++) {
|
|
1351
|
-
const tok = tokens[i2];
|
|
1352
|
-
if (!/^-?[A-Za-z_][A-Za-z0-9_:/.-]*$/.test(tok)) return false;
|
|
1353
|
-
if (/[-:/0-9]/.test(tok)) sawClassish = true;
|
|
1354
|
-
}
|
|
1355
|
-
return sawClassish;
|
|
1356
|
-
}
|
|
1508
|
+
var warnedLoneStrings = /* @__PURE__ */ new Set();
|
|
1509
|
+
var MAX_WARNED_LONE_STRINGS = 100;
|
|
1510
|
+
var loneStringCapAnnounced = false;
|
|
1357
1511
|
var kebabCache = /* @__PURE__ */ new Map();
|
|
1358
1512
|
function toKebab(prop) {
|
|
1359
1513
|
let cached = kebabCache.get(prop);
|
|
@@ -1365,13 +1519,13 @@ function toKebab(prop) {
|
|
|
1365
1519
|
function applyStyle(el, style2) {
|
|
1366
1520
|
if (typeof style2 === "function") {
|
|
1367
1521
|
const teardown = reactiveBinding2(() => {
|
|
1368
|
-
el.setAttribute("style", sanitizeStyleAttribute(String(style2())));
|
|
1522
|
+
el.setAttribute("style", sanitizeStyleAttribute(String(style2()), { element: el }));
|
|
1369
1523
|
}, el);
|
|
1370
1524
|
registerDisposer(el, teardown);
|
|
1371
1525
|
return;
|
|
1372
1526
|
}
|
|
1373
1527
|
if (typeof style2 === "string") {
|
|
1374
|
-
el.setAttribute("style", sanitizeStyleAttribute(style2));
|
|
1528
|
+
el.setAttribute("style", sanitizeStyleAttribute(style2, { element: el }));
|
|
1375
1529
|
return;
|
|
1376
1530
|
}
|
|
1377
1531
|
const htmlEl = el;
|
|
@@ -1381,11 +1535,11 @@ function applyStyle(el, style2) {
|
|
|
1381
1535
|
if (typeof val === "function") {
|
|
1382
1536
|
const getter = val;
|
|
1383
1537
|
const teardown = reactiveBinding2(() => {
|
|
1384
|
-
htmlEl.style.setProperty(name, sanitizeCSSValue(String(getter())));
|
|
1538
|
+
htmlEl.style.setProperty(name, sanitizeCSSValue(String(getter()), { property: name, element: el }));
|
|
1385
1539
|
}, el);
|
|
1386
1540
|
registerDisposer(el, teardown);
|
|
1387
1541
|
} else {
|
|
1388
|
-
htmlEl.style.setProperty(name, sanitizeCSSValue(String(val)));
|
|
1542
|
+
htmlEl.style.setProperty(name, sanitizeCSSValue(String(val), { property: name, element: el }));
|
|
1389
1543
|
}
|
|
1390
1544
|
}
|
|
1391
1545
|
}
|
|
@@ -1492,11 +1646,26 @@ var tagFactory = (tag, ns) => {
|
|
|
1492
1646
|
appendChildren(el, second);
|
|
1493
1647
|
return el;
|
|
1494
1648
|
}
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1649
|
+
devWarnLazy(() => {
|
|
1650
|
+
const tokens = first.trim().split(/\s+/);
|
|
1651
|
+
if (tokens.length < 2) return "";
|
|
1652
|
+
let utilityTokens = 0;
|
|
1653
|
+
for (let i2 = 0; i2 < tokens.length; i2++) {
|
|
1654
|
+
const tok = tokens[i2];
|
|
1655
|
+
if (!/^-?[A-Za-z_][A-Za-z0-9_:/.-]*$/.test(tok)) return "";
|
|
1656
|
+
if (/[-:/0-9]/.test(tok)) utilityTokens++;
|
|
1657
|
+
}
|
|
1658
|
+
if (utilityTokens < 2) return "";
|
|
1659
|
+
const key = `${tag}|${first}`;
|
|
1660
|
+
if (warnedLoneStrings.has(key)) return "";
|
|
1661
|
+
if (warnedLoneStrings.size >= MAX_WARNED_LONE_STRINGS) {
|
|
1662
|
+
if (loneStringCapAnnounced) return "";
|
|
1663
|
+
loneStringCapAnnounced = true;
|
|
1664
|
+
return `tagFactory: ${MAX_WARNED_LONE_STRINGS} distinct lone-string class warnings have been reported; suppressing further ones for the rest of this session so they cannot flood the console. Fix the reported ones and reload to see any that remain.`;
|
|
1665
|
+
}
|
|
1666
|
+
warnedLoneStrings.add(key);
|
|
1667
|
+
return `tagFactory: lone string "${first}" looks like a class list but is being rendered as TEXT. For a class, use ${tag}({ class: "${first}" }) \u2014 or ${tag}("${first}", children) to set the class AND add children.`;
|
|
1668
|
+
});
|
|
1500
1669
|
el.textContent = first;
|
|
1501
1670
|
return el;
|
|
1502
1671
|
}
|
|
@@ -1513,7 +1682,7 @@ var tagFactory = (tag, ns) => {
|
|
|
1513
1682
|
if (pClass != null) applyClass(el, pClass);
|
|
1514
1683
|
const pId = props.id;
|
|
1515
1684
|
if (pId != null) {
|
|
1516
|
-
if (
|
|
1685
|
+
if (DEV && typeof pId === "string" && CLOBBER_RISKY_IDS.has(pId.toLowerCase())) {
|
|
1517
1686
|
devWarn(
|
|
1518
1687
|
`tagFactory: element id="${pId}" matches a common global and may cause DOM clobbering. Avoid setting ids from untrusted input.`
|
|
1519
1688
|
);
|
|
@@ -1528,7 +1697,7 @@ var tagFactory = (tag, ns) => {
|
|
|
1528
1697
|
const handler = pOn[ev];
|
|
1529
1698
|
if (typeof handler === "function") {
|
|
1530
1699
|
el.addEventListener(ev, handler);
|
|
1531
|
-
} else if (
|
|
1700
|
+
} else if (DEV) {
|
|
1532
1701
|
devWarn(
|
|
1533
1702
|
`tagFactory: on.${ev} handler is not a function (got ${typeof handler}). Event listener was not attached.`
|
|
1534
1703
|
);
|
|
@@ -2327,7 +2496,6 @@ var isBatching = API2.isBatching;
|
|
|
2327
2496
|
|
|
2328
2497
|
// src/core/signals/signal.ts
|
|
2329
2498
|
var _g2 = globalThis;
|
|
2330
|
-
var _isDev7 = isDev();
|
|
2331
2499
|
function signal(initial, options) {
|
|
2332
2500
|
const state2 = {
|
|
2333
2501
|
value: initial,
|
|
@@ -2340,7 +2508,7 @@ function signal(initial, options) {
|
|
|
2340
2508
|
_d: false,
|
|
2341
2509
|
_validate: void 0
|
|
2342
2510
|
};
|
|
2343
|
-
const debugName =
|
|
2511
|
+
const debugName = DEV ? options?.name : void 0;
|
|
2344
2512
|
const equalsFn = options?.equals;
|
|
2345
2513
|
if (debugName) state2.__name = debugName;
|
|
2346
2514
|
function get() {
|
|
@@ -2357,7 +2525,7 @@ function signal(initial, options) {
|
|
|
2357
2525
|
if (equalsFn(prev, newValue)) return;
|
|
2358
2526
|
state2.value = newValue;
|
|
2359
2527
|
state2.__v++;
|
|
2360
|
-
if (
|
|
2528
|
+
if (DEV) {
|
|
2361
2529
|
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
2362
2530
|
if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
2363
2531
|
}
|
|
@@ -2365,7 +2533,7 @@ function signal(initial, options) {
|
|
|
2365
2533
|
notifySubscribers2(state2);
|
|
2366
2534
|
}
|
|
2367
2535
|
};
|
|
2368
|
-
} else if (
|
|
2536
|
+
} else if (DEV) {
|
|
2369
2537
|
set = (next) => {
|
|
2370
2538
|
const prev = state2.value;
|
|
2371
2539
|
const newValue = typeof next === "function" ? next(prev) : next;
|
|
@@ -2390,12 +2558,23 @@ function signal(initial, options) {
|
|
|
2390
2558
|
}
|
|
2391
2559
|
};
|
|
2392
2560
|
}
|
|
2393
|
-
if (
|
|
2561
|
+
if (DEV) {
|
|
2394
2562
|
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
2395
2563
|
if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
|
|
2396
2564
|
}
|
|
2397
2565
|
return [get, set];
|
|
2398
2566
|
}
|
|
2567
|
+
function external(options) {
|
|
2568
|
+
const [version, bump] = signal(0, options?.name ? { name: options.name } : void 0);
|
|
2569
|
+
return {
|
|
2570
|
+
track() {
|
|
2571
|
+
version();
|
|
2572
|
+
},
|
|
2573
|
+
invalidate() {
|
|
2574
|
+
bump((n) => n + 1);
|
|
2575
|
+
}
|
|
2576
|
+
};
|
|
2577
|
+
}
|
|
2399
2578
|
|
|
2400
2579
|
// src/components/ErrorDisplay.ts
|
|
2401
2580
|
var STYLES = `
|
|
@@ -3371,11 +3550,10 @@ function setGlobalErrorHandler(handler) {
|
|
|
3371
3550
|
}
|
|
3372
3551
|
|
|
3373
3552
|
// src/core/rendering/context.ts
|
|
3374
|
-
var _isDev8 = isDev();
|
|
3375
3553
|
function context(defaultValue) {
|
|
3376
3554
|
const [getValue, setValue] = signal(defaultValue);
|
|
3377
3555
|
const warnIfSSR = (method) => {
|
|
3378
|
-
if (!
|
|
3556
|
+
if (!DEV || !isSSR()) return;
|
|
3379
3557
|
devWarn(
|
|
3380
3558
|
`context.${method}() called during SSR. SibuJS context() is application-global \u2014 it is NOT isolated per request, so a concurrent request can observe this value. Do not use context() for request-specific SSR data; pass it explicitly or use request-scoped storage instead.`
|
|
3381
3559
|
);
|
|
@@ -3403,7 +3581,7 @@ function context(defaultValue) {
|
|
|
3403
3581
|
setValue(value);
|
|
3404
3582
|
try {
|
|
3405
3583
|
const result = fn();
|
|
3406
|
-
if (
|
|
3584
|
+
if (DEV && result !== null && typeof result?.then === "function") {
|
|
3407
3585
|
devWarn(
|
|
3408
3586
|
"context.withContext() received an async callback. Scoping is synchronous only \u2014 the previous value is restored as soon as the callback returns its promise, so anything after an `await` runs outside the scope."
|
|
3409
3587
|
);
|
|
@@ -3430,34 +3608,53 @@ function __resetIdCounter() {
|
|
|
3430
3608
|
|
|
3431
3609
|
// src/core/rendering/directives.ts
|
|
3432
3610
|
function show(condition, element) {
|
|
3611
|
+
const resolved = typeof element === "function" ? element() : element;
|
|
3433
3612
|
const update = () => {
|
|
3434
|
-
|
|
3613
|
+
resolved.style.display = condition() ? "" : "none";
|
|
3435
3614
|
};
|
|
3436
|
-
registerDisposer(
|
|
3437
|
-
return
|
|
3615
|
+
registerDisposer(resolved, track2(update));
|
|
3616
|
+
return resolved;
|
|
3617
|
+
}
|
|
3618
|
+
function attachBranch(parent, anchor, branch, attachedOnce, where) {
|
|
3619
|
+
const result = typeof branch === "function" ? branch() : branch;
|
|
3620
|
+
if (result == null || typeof result === "boolean") return { node: null, owned: false };
|
|
3621
|
+
const node = result instanceof Node ? result : document.createTextNode(String(result));
|
|
3622
|
+
const handedIn = node === branch;
|
|
3623
|
+
const seenBefore = attachedOnce.has(node);
|
|
3624
|
+
const owned = !handedIn && !seenBefore;
|
|
3625
|
+
attachedOnce.add(node);
|
|
3626
|
+
if (seenBefore) {
|
|
3627
|
+
devWarnLazy(
|
|
3628
|
+
() => `${where}: ${handedIn ? "a branch was given as an element rather than a function" : "a branch factory returned a node it had already returned"}, so the SAME node is being re-attached \u2014 any state it accumulated while detached (input value, scroll position, classes set imperatively) comes back with it. Its reactive bindings are left intact rather than disposed, because the node is not this directive's to tear down. Return a FRESH node per call (\`() => div(\u2026)\`) for rebuild semantics, or keep this form deliberately when the reuse is what you want.`
|
|
3629
|
+
);
|
|
3630
|
+
}
|
|
3631
|
+
parent.insertBefore(node, anchor.nextSibling);
|
|
3632
|
+
return { node, owned };
|
|
3438
3633
|
}
|
|
3439
3634
|
function when(condition, thenBranch, elseBranch) {
|
|
3440
3635
|
const anchor = document.createComment("when");
|
|
3441
3636
|
let currentNode = null;
|
|
3442
3637
|
let lastCondition;
|
|
3443
3638
|
let initialized = false;
|
|
3639
|
+
const attachedOnce = /* @__PURE__ */ new WeakSet();
|
|
3640
|
+
let currentNodeOwned = false;
|
|
3444
3641
|
const update = () => {
|
|
3445
3642
|
const show2 = condition();
|
|
3446
3643
|
const parent = anchor.parentNode;
|
|
3447
3644
|
if (!parent) return;
|
|
3448
3645
|
if (initialized && show2 === lastCondition) return;
|
|
3449
3646
|
lastCondition = show2;
|
|
3647
|
+
const focused = currentNode ? captureFocusWithin([currentNode]) : null;
|
|
3450
3648
|
if (currentNode?.parentNode) {
|
|
3451
|
-
dispose(currentNode);
|
|
3649
|
+
if (currentNodeOwned) dispose(currentNode);
|
|
3452
3650
|
currentNode.parentNode.removeChild(currentNode);
|
|
3453
3651
|
currentNode = null;
|
|
3454
3652
|
}
|
|
3455
|
-
const
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
}
|
|
3653
|
+
const branch = show2 ? thenBranch : elseBranch !== void 0 ? elseBranch : null;
|
|
3654
|
+
const attached = attachBranch(parent, anchor, branch, attachedOnce, "when");
|
|
3655
|
+
currentNode = attached.node;
|
|
3656
|
+
currentNodeOwned = attached.owned;
|
|
3657
|
+
restoreFocusWithin(focused, currentNode ? [currentNode] : [], "when");
|
|
3461
3658
|
initialized = true;
|
|
3462
3659
|
};
|
|
3463
3660
|
registerDisposer(anchor, track2(update));
|
|
@@ -3473,26 +3670,25 @@ function match(value, cases, fallback) {
|
|
|
3473
3670
|
let currentNode = null;
|
|
3474
3671
|
let lastKey;
|
|
3475
3672
|
let initialized = false;
|
|
3673
|
+
const attachedOnce = /* @__PURE__ */ new WeakSet();
|
|
3674
|
+
let currentNodeOwned = false;
|
|
3476
3675
|
const update = () => {
|
|
3477
3676
|
const key = String(value());
|
|
3478
3677
|
const parent = anchor.parentNode;
|
|
3479
3678
|
if (!parent) return;
|
|
3480
3679
|
if (initialized && key === lastKey) return;
|
|
3481
3680
|
lastKey = key;
|
|
3681
|
+
const focused = currentNode ? captureFocusWithin([currentNode]) : null;
|
|
3482
3682
|
if (currentNode?.parentNode) {
|
|
3483
|
-
dispose(currentNode);
|
|
3683
|
+
if (currentNodeOwned) dispose(currentNode);
|
|
3484
3684
|
currentNode.parentNode.removeChild(currentNode);
|
|
3485
3685
|
currentNode = null;
|
|
3486
3686
|
}
|
|
3487
|
-
const
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
parent.insertBefore(node, anchor.nextSibling);
|
|
3493
|
-
currentNode = node;
|
|
3494
|
-
}
|
|
3495
|
-
}
|
|
3687
|
+
const branch = Object.hasOwn(cases, key) ? cases[key] : fallback;
|
|
3688
|
+
const attached = attachBranch(parent, anchor, branch ?? null, attachedOnce, "match");
|
|
3689
|
+
currentNode = attached.node;
|
|
3690
|
+
currentNodeOwned = attached.owned;
|
|
3691
|
+
restoreFocusWithin(focused, currentNode ? [currentNode] : [], "match");
|
|
3496
3692
|
initialized = true;
|
|
3497
3693
|
};
|
|
3498
3694
|
registerDisposer(anchor, track2(update));
|
|
@@ -3537,7 +3733,6 @@ function DynamicComponent(is) {
|
|
|
3537
3733
|
}
|
|
3538
3734
|
|
|
3539
3735
|
// src/core/rendering/each.ts
|
|
3540
|
-
var _isDev9 = isDev();
|
|
3541
3736
|
function resolveNodeChild(child) {
|
|
3542
3737
|
if (typeof child === "function") {
|
|
3543
3738
|
return resolveNodeChild(child());
|
|
@@ -3628,7 +3823,7 @@ function each(getArray, render, options) {
|
|
|
3628
3823
|
workMap.clear();
|
|
3629
3824
|
keyIndexMap.clear();
|
|
3630
3825
|
for (let i2 = 0; i2 < newLen; i2++) {
|
|
3631
|
-
if (
|
|
3826
|
+
if (DEV && keyIndexMap.has(newKeys[i2])) {
|
|
3632
3827
|
devWarn(
|
|
3633
3828
|
`each: duplicate key "${String(newKeys[i2])}" at index ${i2} (first seen at ${keyIndexMap.get(newKeys[i2])}). Keys must be unique \u2014 duplicates cause rows to be dropped or mis-ordered.`
|
|
3634
3829
|
);
|
|
@@ -3812,7 +4007,6 @@ function resolveChild(child) {
|
|
|
3812
4007
|
}
|
|
3813
4008
|
|
|
3814
4009
|
// src/core/rendering/htm.ts
|
|
3815
|
-
var _isDev10 = isDev();
|
|
3816
4010
|
var RAW_TEXT_TAGS = /* @__PURE__ */ new Set(["script", "style"]);
|
|
3817
4011
|
var VOID_ELEMENTS = /* @__PURE__ */ new Set([
|
|
3818
4012
|
"area",
|
|
@@ -3899,13 +4093,13 @@ function parseTemplate(strings) {
|
|
|
3899
4093
|
skipWs();
|
|
3900
4094
|
const exprIdx = tryExprIdx();
|
|
3901
4095
|
if (exprIdx >= 0) return { kind: "expr", idx: exprIdx };
|
|
3902
|
-
const
|
|
3903
|
-
if (
|
|
4096
|
+
const quote2 = template2[pos];
|
|
4097
|
+
if (quote2 === '"' || quote2 === "'") {
|
|
3904
4098
|
pos++;
|
|
3905
4099
|
const statics = [];
|
|
3906
4100
|
const exprs = [];
|
|
3907
4101
|
let current = "";
|
|
3908
|
-
while (pos < len && template2[pos] !==
|
|
4102
|
+
while (pos < len && template2[pos] !== quote2) {
|
|
3909
4103
|
const innerIdx = tryExprIdx();
|
|
3910
4104
|
if (innerIdx >= 0) {
|
|
3911
4105
|
statics.push(current);
|
|
@@ -4092,7 +4286,7 @@ function executeElement(tmpl, values) {
|
|
|
4092
4286
|
const fn = values[attr.idx];
|
|
4093
4287
|
if (typeof fn === "function") {
|
|
4094
4288
|
el.addEventListener(attr.name, fn);
|
|
4095
|
-
} else if (
|
|
4289
|
+
} else if (DEV) {
|
|
4096
4290
|
devWarn(
|
|
4097
4291
|
`html: on:${attr.name} handler is not a function (got ${typeof fn}). Event listener was not attached.`
|
|
4098
4292
|
);
|
|
@@ -4960,6 +5154,7 @@ function drainTeardowns(teardowns, label2) {
|
|
|
4960
5154
|
}
|
|
4961
5155
|
}
|
|
4962
5156
|
}
|
|
5157
|
+
var EACH_KEYS = ["text", "attr", "class", "show", "on", "cleanup"];
|
|
4963
5158
|
function resolveTarget(root, target) {
|
|
4964
5159
|
if (target == null) return root;
|
|
4965
5160
|
if (typeof target !== "string") return target;
|
|
@@ -4970,6 +5165,11 @@ function resolveTarget(root, target) {
|
|
|
4970
5165
|
return null;
|
|
4971
5166
|
}
|
|
4972
5167
|
}
|
|
5168
|
+
function bindNode(el, commit) {
|
|
5169
|
+
if (isSSR()) return () => {
|
|
5170
|
+
};
|
|
5171
|
+
return reactiveBinding2(commit, el);
|
|
5172
|
+
}
|
|
4973
5173
|
function readControlValue(el) {
|
|
4974
5174
|
if (el instanceof HTMLInputElement) {
|
|
4975
5175
|
if (el.type === "checkbox") return el.checked;
|
|
@@ -4993,6 +5193,50 @@ function writeControlValue(el, value) {
|
|
|
4993
5193
|
const next = value == null ? "" : String(value);
|
|
4994
5194
|
if (el.value !== next) el.value = next;
|
|
4995
5195
|
}
|
|
5196
|
+
function applyEachBindings(ctx, el, spec, index) {
|
|
5197
|
+
const where = `ctx.each[${index}]`;
|
|
5198
|
+
if (isDev()) {
|
|
5199
|
+
for (const key of Object.keys(spec)) {
|
|
5200
|
+
devAssert(
|
|
5201
|
+
EACH_KEYS.includes(key),
|
|
5202
|
+
`${where}: unknown binding "${key}". Expected one of: ${EACH_KEYS.join(", ")}.`
|
|
5203
|
+
);
|
|
5204
|
+
}
|
|
5205
|
+
}
|
|
5206
|
+
if (spec.text !== void 0) {
|
|
5207
|
+
devAssert(typeof spec.text === "function", `${where}: "text" must be a function returning the value.`);
|
|
5208
|
+
ctx.text(el, spec.text);
|
|
5209
|
+
}
|
|
5210
|
+
if (spec.attr !== void 0) {
|
|
5211
|
+
for (const name of Object.keys(spec.attr)) {
|
|
5212
|
+
const value = spec.attr[name];
|
|
5213
|
+
devAssert(typeof value === "function", `${where}: attr["${name}"] must be a function returning the value.`);
|
|
5214
|
+
ctx.attr(el, name, value);
|
|
5215
|
+
}
|
|
5216
|
+
}
|
|
5217
|
+
if (spec.class !== void 0) {
|
|
5218
|
+
for (const name of Object.keys(spec.class)) {
|
|
5219
|
+
const on2 = spec.class[name];
|
|
5220
|
+
devAssert(typeof on2 === "function", `${where}: class["${name}"] must be a function returning a boolean.`);
|
|
5221
|
+
ctx.classed(el, name, on2);
|
|
5222
|
+
}
|
|
5223
|
+
}
|
|
5224
|
+
if (spec.show !== void 0) {
|
|
5225
|
+
devAssert(typeof spec.show === "function", `${where}: "show" must be a function returning a boolean.`);
|
|
5226
|
+
ctx.show(el, spec.show);
|
|
5227
|
+
}
|
|
5228
|
+
if (spec.on !== void 0) {
|
|
5229
|
+
for (const event of Object.keys(spec.on)) {
|
|
5230
|
+
const handler = spec.on[event];
|
|
5231
|
+
devAssert(typeof handler === "function", `${where}: on["${String(event)}"] must be a function.`);
|
|
5232
|
+
ctx.on(el, event, handler);
|
|
5233
|
+
}
|
|
5234
|
+
}
|
|
5235
|
+
if (spec.cleanup !== void 0) {
|
|
5236
|
+
devAssert(typeof spec.cleanup === "function", `${where}: "cleanup" must be a function.`);
|
|
5237
|
+
ctx.cleanup(spec.cleanup);
|
|
5238
|
+
}
|
|
5239
|
+
}
|
|
4996
5240
|
function enhance(target, setup) {
|
|
4997
5241
|
const root = typeof target === "string" ? typeof document !== "undefined" ? document.querySelector(target) : null : target;
|
|
4998
5242
|
if (!root) {
|
|
@@ -5043,7 +5287,7 @@ function enhance(target, setup) {
|
|
|
5043
5287
|
text: (t, value) => {
|
|
5044
5288
|
bind(t, (el) => {
|
|
5045
5289
|
teardowns.push(
|
|
5046
|
-
|
|
5290
|
+
bindNode(el, () => {
|
|
5047
5291
|
const v = value();
|
|
5048
5292
|
const next = v == null ? "" : String(v);
|
|
5049
5293
|
if (el.textContent !== next) el.textContent = next;
|
|
@@ -5054,7 +5298,7 @@ function enhance(target, setup) {
|
|
|
5054
5298
|
attr: (t, name, value) => {
|
|
5055
5299
|
bind(t, (el) => {
|
|
5056
5300
|
teardowns.push(
|
|
5057
|
-
|
|
5301
|
+
bindNode(el, () => {
|
|
5058
5302
|
const v = value();
|
|
5059
5303
|
const next = v == null ? null : String(v);
|
|
5060
5304
|
setSafeAttribute(el, name, next, { syncValueProperty: false, label: "enhance attr()" });
|
|
@@ -5065,7 +5309,7 @@ function enhance(target, setup) {
|
|
|
5065
5309
|
classed: (t, name, on2) => {
|
|
5066
5310
|
bind(t, (el) => {
|
|
5067
5311
|
teardowns.push(
|
|
5068
|
-
|
|
5312
|
+
bindNode(el, () => {
|
|
5069
5313
|
el.classList.toggle(name, Boolean(on2()));
|
|
5070
5314
|
})
|
|
5071
5315
|
);
|
|
@@ -5075,7 +5319,7 @@ function enhance(target, setup) {
|
|
|
5075
5319
|
bind(t, (el) => {
|
|
5076
5320
|
const prevHidden = el.hidden;
|
|
5077
5321
|
teardowns.push(
|
|
5078
|
-
|
|
5322
|
+
bindNode(el, () => {
|
|
5079
5323
|
el.hidden = !when2();
|
|
5080
5324
|
})
|
|
5081
5325
|
);
|
|
@@ -5090,7 +5334,7 @@ function enhance(target, setup) {
|
|
|
5090
5334
|
const [get, set] = state2;
|
|
5091
5335
|
const evt = options?.event ?? (control instanceof HTMLSelectElement || control instanceof HTMLInputElement && (control.type === "checkbox" || control.type === "radio") ? "change" : "input");
|
|
5092
5336
|
teardowns.push(
|
|
5093
|
-
|
|
5337
|
+
bindNode(el, () => {
|
|
5094
5338
|
writeControlValue(control, get());
|
|
5095
5339
|
})
|
|
5096
5340
|
);
|
|
@@ -5099,6 +5343,16 @@ function enhance(target, setup) {
|
|
|
5099
5343
|
teardowns.push(() => control.removeEventListener(evt, onInput));
|
|
5100
5344
|
});
|
|
5101
5345
|
},
|
|
5346
|
+
each: (target_, describe2) => {
|
|
5347
|
+
devAssert(typeof describe2 === "function", "ctx.each: second argument must be a function.");
|
|
5348
|
+
const elements = typeof target_ === "string" ? ctx.refs(target_) : Array.from(target_);
|
|
5349
|
+
for (let i2 = 0; i2 < elements.length; i2++) {
|
|
5350
|
+
const el = elements[i2];
|
|
5351
|
+
const spec = describe2(el, i2);
|
|
5352
|
+
if (spec == null) continue;
|
|
5353
|
+
applyEachBindings(ctx, el, spec, i2);
|
|
5354
|
+
}
|
|
5355
|
+
},
|
|
5102
5356
|
cleanup: (fn) => {
|
|
5103
5357
|
teardowns.push(fn);
|
|
5104
5358
|
}
|
|
@@ -5307,7 +5561,6 @@ function schedule(strategy, el, activate, options) {
|
|
|
5307
5561
|
}
|
|
5308
5562
|
|
|
5309
5563
|
// src/platform/ssr.ts
|
|
5310
|
-
var _isDev11 = isDev();
|
|
5311
5564
|
function trustHTML(html3) {
|
|
5312
5565
|
return html3;
|
|
5313
5566
|
}
|
|
@@ -5474,6 +5727,7 @@ function nextTick() {
|
|
|
5474
5727
|
enhance,
|
|
5475
5728
|
enhanceAll,
|
|
5476
5729
|
enqueueBatchedSignal,
|
|
5730
|
+
external,
|
|
5477
5731
|
fieldset,
|
|
5478
5732
|
figcaption,
|
|
5479
5733
|
figure,
|