streetui 1.8.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.cjs +1 -1
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/bin.js.map +1 -1
- package/dist/create-bin.cjs +1 -1
- package/dist/create-bin.cjs.map +1 -1
- package/dist/create-bin.js +1 -1
- package/dist/create-bin.js.map +1 -1
- package/dist/{compile-CJJQYi71.d.cts → hydration-diagnostics-CFYDXBlm.d.cts} +471 -5
- package/dist/{compile-CJJQYi71.d.ts → hydration-diagnostics-CFYDXBlm.d.ts} +471 -5
- package/dist/index.cjs +1401 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +598 -147
- package/dist/index.d.ts +598 -147
- package/dist/index.js +1379 -47
- package/dist/index.js.map +1 -1
- package/dist/renderer-interface-BUhHV7ja.d.cts +26 -0
- package/dist/renderer-interface-B_-hWnKk.d.ts +26 -0
- package/dist/{server-CnLEnlYU.d.cts → server-C0PvFBzD.d.ts} +181 -2
- package/dist/{server-DNlg7ou2.d.ts → server-DnAOFpMP.d.cts} +181 -2
- package/dist/server.cjs +482 -32
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +481 -32
- package/dist/server.js.map +1 -1
- package/dist/testing.cjs +754 -41
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +42 -4
- package/dist/testing.d.ts +42 -4
- package/dist/testing.js +746 -40
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
- package/dist/hydration-diagnostics-BQeehq3i.d.cts +0 -89
- package/dist/hydration-diagnostics-DNAJumYF.d.ts +0 -89
package/dist/testing.cjs
CHANGED
|
@@ -22,22 +22,29 @@ var testing_exports = {};
|
|
|
22
22
|
__export(testing_exports, {
|
|
23
23
|
VERSION: () => VERSION,
|
|
24
24
|
analyzeGraph: () => analyzeGraph,
|
|
25
|
+
blur: () => blur,
|
|
26
|
+
clickOutside: () => clickOutside,
|
|
27
|
+
closeOverlay: () => closeOverlay,
|
|
25
28
|
findAllByRole: () => findAllByRole,
|
|
26
29
|
findAllComponents: () => findAllComponents,
|
|
27
30
|
findByRole: () => findByRole,
|
|
28
31
|
findByText: () => findByText,
|
|
29
32
|
findComponent: () => findComponent,
|
|
30
33
|
flushUpdates: () => flushUpdates,
|
|
34
|
+
focus: () => focus,
|
|
31
35
|
formatInspection: () => formatInspection,
|
|
32
36
|
getComponentName: () => getComponentName,
|
|
33
37
|
hydrateComponent: () => hydrateComponent,
|
|
34
38
|
inspectCompilation: () => inspectCompilation,
|
|
39
|
+
openOverlay: () => openOverlay,
|
|
40
|
+
pressKey: () => pressKey,
|
|
35
41
|
render: () => render,
|
|
36
42
|
renderComponent: () => renderComponent,
|
|
37
43
|
renderOnce: () => renderOnce,
|
|
38
44
|
renderServerThenHydrate: () => renderServerThenHydrate,
|
|
39
45
|
trigger: () => trigger,
|
|
40
|
-
waitFor: () => waitFor
|
|
46
|
+
waitFor: () => waitFor,
|
|
47
|
+
waitForTransition: () => waitForTransition
|
|
41
48
|
});
|
|
42
49
|
module.exports = __toCommonJS(testing_exports);
|
|
43
50
|
|
|
@@ -308,6 +315,9 @@ var BrowserDOMAdapter = class {
|
|
|
308
315
|
body() {
|
|
309
316
|
return document.body ?? null;
|
|
310
317
|
}
|
|
318
|
+
head() {
|
|
319
|
+
return document.head ?? null;
|
|
320
|
+
}
|
|
311
321
|
activeElement() {
|
|
312
322
|
return document.activeElement ?? null;
|
|
313
323
|
}
|
|
@@ -686,6 +696,9 @@ var ServerDOMAdapter = class {
|
|
|
686
696
|
body() {
|
|
687
697
|
return null;
|
|
688
698
|
}
|
|
699
|
+
head() {
|
|
700
|
+
return null;
|
|
701
|
+
}
|
|
689
702
|
activeElement() {
|
|
690
703
|
return null;
|
|
691
704
|
}
|
|
@@ -799,18 +812,25 @@ function trapFocus(dom, container) {
|
|
|
799
812
|
dom.addEventListener(container, "keydown", onKeydown);
|
|
800
813
|
return () => dom.removeEventListener(container, "keydown", onKeydown);
|
|
801
814
|
}
|
|
815
|
+
var containmentStack = [];
|
|
802
816
|
function containFocus(dom, container) {
|
|
803
817
|
const body = dom.body();
|
|
804
818
|
if (body === null) return () => {
|
|
805
819
|
};
|
|
820
|
+
containmentStack.push(container);
|
|
806
821
|
const onFocusIn = (event) => {
|
|
822
|
+
if (containmentStack[containmentStack.length - 1] !== container) return;
|
|
807
823
|
const target = event.target;
|
|
808
824
|
if (target !== null && !dom.contains(container, target)) {
|
|
809
825
|
focusFirst(dom, container);
|
|
810
826
|
}
|
|
811
827
|
};
|
|
812
828
|
dom.addEventListener(body, "focusin", onFocusIn);
|
|
813
|
-
return () =>
|
|
829
|
+
return () => {
|
|
830
|
+
dom.removeEventListener(body, "focusin", onFocusIn);
|
|
831
|
+
const index = containmentStack.lastIndexOf(container);
|
|
832
|
+
if (index !== -1) containmentStack.splice(index, 1);
|
|
833
|
+
};
|
|
814
834
|
}
|
|
815
835
|
function onEscape(dom, target, handler) {
|
|
816
836
|
const onKeydown = (event) => {
|
|
@@ -819,6 +839,33 @@ function onEscape(dom, target, handler) {
|
|
|
819
839
|
dom.addEventListener(target, "keydown", onKeydown);
|
|
820
840
|
return () => dom.removeEventListener(target, "keydown", onKeydown);
|
|
821
841
|
}
|
|
842
|
+
function rovingMenu(dom, container, selector = FOCUSABLE_SELECTOR) {
|
|
843
|
+
const onKeydown = (event) => {
|
|
844
|
+
const key = event.key;
|
|
845
|
+
const isActivate = key === "Enter" || key === " " || key === "Spacebar";
|
|
846
|
+
const isMove = key === "ArrowDown" || key === "ArrowUp" || key === "Home" || key === "End";
|
|
847
|
+
if (!isActivate && !isMove) return;
|
|
848
|
+
const items = getFocusable(dom, container, selector);
|
|
849
|
+
if (items.length === 0) return;
|
|
850
|
+
const active = dom.activeElement();
|
|
851
|
+
const index = active === null ? -1 : items.indexOf(active);
|
|
852
|
+
if (isActivate) {
|
|
853
|
+
if (index < 0) return;
|
|
854
|
+
event.preventDefault();
|
|
855
|
+
items[index].click?.();
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
event.preventDefault();
|
|
859
|
+
let next;
|
|
860
|
+
if (key === "Home") next = 0;
|
|
861
|
+
else if (key === "End") next = items.length - 1;
|
|
862
|
+
else if (key === "ArrowDown") next = index < 0 ? 0 : (index + 1) % items.length;
|
|
863
|
+
else next = index <= 0 ? items.length - 1 : index - 1;
|
|
864
|
+
dom.focus(items[next]);
|
|
865
|
+
};
|
|
866
|
+
dom.addEventListener(container, "keydown", onKeydown);
|
|
867
|
+
return () => dom.removeEventListener(container, "keydown", onKeydown);
|
|
868
|
+
}
|
|
822
869
|
|
|
823
870
|
// ../renderer/src/render-context.ts
|
|
824
871
|
function createRenderContext(dom, graph, container, hydrationDiagnostics, staticHTML) {
|
|
@@ -982,7 +1029,13 @@ var TAG_MAP = {
|
|
|
982
1029
|
// its children are relocated to a document.body container on the browser
|
|
983
1030
|
// (see the portal branch in mount.ts). On the server (no body) it renders
|
|
984
1031
|
// inline, so the anchor tag is what SSR/hydration positionally match on.
|
|
985
|
-
portal: "div"
|
|
1032
|
+
portal: "div",
|
|
1033
|
+
// A `head()` node renders as a neutral, empty inline anchor <div> at its
|
|
1034
|
+
// declaration site (like a portal anchor). Its actual contribution — title/
|
|
1035
|
+
// meta/link/etc. — is applied to `document.head` by `wireHeadBehavior` on the
|
|
1036
|
+
// browser, and emitted separately by `renderHead()` on the server. Keeping a
|
|
1037
|
+
// one-node/one-element anchor preserves positional hydration.
|
|
1038
|
+
head: "div"
|
|
986
1039
|
};
|
|
987
1040
|
function resolveTag(type) {
|
|
988
1041
|
return TAG_MAP[type] ?? "div";
|
|
@@ -1030,7 +1083,7 @@ function patchNode(ctx, graphNode, propKey, newValue) {
|
|
|
1030
1083
|
}
|
|
1031
1084
|
|
|
1032
1085
|
// ../renderer/src/reconciliation.ts
|
|
1033
|
-
function reconcileChildren(ctx, parentDom, oldInstances, newNodes, mountFn) {
|
|
1086
|
+
function reconcileChildren(ctx, parentDom, oldInstances, newNodes, mountFn, hooks) {
|
|
1034
1087
|
const oldByKey = /* @__PURE__ */ new Map();
|
|
1035
1088
|
for (const inst of oldInstances) {
|
|
1036
1089
|
const key = inst.graphNode.key ?? inst.graphNode.id;
|
|
@@ -1051,28 +1104,33 @@ function reconcileChildren(ctx, parentDom, oldInstances, newNodes, mountFn) {
|
|
|
1051
1104
|
}
|
|
1052
1105
|
newInstances.push(existing);
|
|
1053
1106
|
} else {
|
|
1054
|
-
const
|
|
1055
|
-
|
|
1107
|
+
const reclaimed = hooks?.takeLeaving(key);
|
|
1108
|
+
if (reclaimed !== void 0) {
|
|
1109
|
+
patchExistingInstance(ctx, reclaimed, newNode);
|
|
1110
|
+
reconcileItemChildren(ctx, reclaimed, newNode, mountFn);
|
|
1111
|
+
hooks?.onEnter(reclaimed);
|
|
1112
|
+
newInstances.push(reclaimed);
|
|
1113
|
+
} else {
|
|
1114
|
+
const inst = mountFn(newNode, parentDom);
|
|
1115
|
+
hooks?.onEnter(inst);
|
|
1116
|
+
newInstances.push(inst);
|
|
1117
|
+
}
|
|
1056
1118
|
}
|
|
1057
1119
|
}
|
|
1058
1120
|
const removed = [];
|
|
1059
1121
|
for (const inst of oldInstances) {
|
|
1060
1122
|
const key = inst.graphNode.key ?? inst.graphNode.id;
|
|
1061
|
-
if (
|
|
1062
|
-
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
for (const inst of removed) {
|
|
1123
|
+
if (usedKeys.has(key)) continue;
|
|
1124
|
+
if (hooks !== void 0 && hooks.beginLeave(inst)) continue;
|
|
1066
1125
|
const parent = ctx.dom.parentNode(inst.domNode);
|
|
1067
|
-
if (parent !== null)
|
|
1068
|
-
ctx.dom.removeChild(parent, inst.domNode);
|
|
1069
|
-
}
|
|
1126
|
+
if (parent !== null) ctx.dom.removeChild(parent, inst.domNode);
|
|
1070
1127
|
inst.dispose();
|
|
1128
|
+
removed.push(inst);
|
|
1071
1129
|
}
|
|
1072
1130
|
reorderDom(ctx, parentDom, newInstances);
|
|
1073
1131
|
return { instances: newInstances, removed };
|
|
1074
1132
|
}
|
|
1075
|
-
function reconcileChildrenByPlan(ctx, parentDom, oldInstances, plan, mountFn) {
|
|
1133
|
+
function reconcileChildrenByPlan(ctx, parentDom, oldInstances, plan, mountFn, hooks) {
|
|
1076
1134
|
const oldByKey = /* @__PURE__ */ new Map();
|
|
1077
1135
|
for (const inst of oldInstances) {
|
|
1078
1136
|
oldByKey.set(inst.graphNode.key ?? inst.graphNode.id, inst);
|
|
@@ -1099,21 +1157,34 @@ function reconcileChildrenByPlan(ctx, parentDom, oldInstances, plan, mountFn) {
|
|
|
1099
1157
|
}
|
|
1100
1158
|
newInstances.push(existing);
|
|
1101
1159
|
} else {
|
|
1102
|
-
const
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1160
|
+
const reclaimed = hooks?.takeLeaving(entry.key);
|
|
1161
|
+
if (reclaimed !== void 0) {
|
|
1162
|
+
const freshNode = entry.build();
|
|
1163
|
+
built.push(freshNode);
|
|
1164
|
+
patchExistingInstance(ctx, reclaimed, freshNode);
|
|
1165
|
+
reconcileItemChildren(ctx, reclaimed, freshNode, mountFn);
|
|
1166
|
+
reclaimed.graphNode.setProp("_sig", freshNode.getProp("_sig"));
|
|
1167
|
+
reclaimed.graphNode.setProp("_item", entry.item);
|
|
1168
|
+
hooks?.onEnter(reclaimed);
|
|
1169
|
+
newInstances.push(reclaimed);
|
|
1170
|
+
} else {
|
|
1171
|
+
const freshNode = entry.build();
|
|
1172
|
+
built.push(freshNode);
|
|
1173
|
+
const inst = mountFn(freshNode, parentDom);
|
|
1174
|
+
hooks?.onEnter(inst);
|
|
1175
|
+
newInstances.push(inst);
|
|
1176
|
+
}
|
|
1106
1177
|
}
|
|
1107
1178
|
}
|
|
1108
1179
|
const removed = [];
|
|
1109
1180
|
for (const inst of oldInstances) {
|
|
1110
1181
|
const key = inst.graphNode.key ?? inst.graphNode.id;
|
|
1111
|
-
if (
|
|
1112
|
-
|
|
1113
|
-
for (const inst of removed) {
|
|
1182
|
+
if (usedKeys.has(key)) continue;
|
|
1183
|
+
if (hooks !== void 0 && hooks.beginLeave(inst)) continue;
|
|
1114
1184
|
const parent = ctx.dom.parentNode(inst.domNode);
|
|
1115
1185
|
if (parent !== null) ctx.dom.removeChild(parent, inst.domNode);
|
|
1116
1186
|
inst.dispose();
|
|
1187
|
+
removed.push(inst);
|
|
1117
1188
|
}
|
|
1118
1189
|
reorderDomMinimal(ctx, parentDom, oldInstances, newInstances);
|
|
1119
1190
|
return { instances: newInstances, removed, built };
|
|
@@ -1239,6 +1310,328 @@ function patchExistingInstance(ctx, instance, newNode) {
|
|
|
1239
1310
|
}
|
|
1240
1311
|
}
|
|
1241
1312
|
|
|
1313
|
+
// ../renderer/src/transition.ts
|
|
1314
|
+
function getResolvedTransition(graph, nodeId) {
|
|
1315
|
+
const fn = graph.getHandler(`__transition__${nodeId}`);
|
|
1316
|
+
return fn === void 0 ? void 0 : fn();
|
|
1317
|
+
}
|
|
1318
|
+
function host() {
|
|
1319
|
+
return globalThis;
|
|
1320
|
+
}
|
|
1321
|
+
function nextFrame(cb) {
|
|
1322
|
+
const h = host();
|
|
1323
|
+
const raf = h.requestAnimationFrame;
|
|
1324
|
+
if (typeof raf === "function") {
|
|
1325
|
+
raf(() => raf(cb));
|
|
1326
|
+
} else {
|
|
1327
|
+
h.setTimeout(cb, 0);
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
function splitClass(value) {
|
|
1331
|
+
if (value === null) return [];
|
|
1332
|
+
const out = [];
|
|
1333
|
+
for (const t of value.split(/\s+/)) if (t.length > 0) out.push(t);
|
|
1334
|
+
return out;
|
|
1335
|
+
}
|
|
1336
|
+
function addClasses(dom, el, classes2) {
|
|
1337
|
+
if (classes2.length === 0) return;
|
|
1338
|
+
const current = splitClass(dom.getAttribute(el, "class"));
|
|
1339
|
+
let changed = false;
|
|
1340
|
+
for (const c of classes2) {
|
|
1341
|
+
if (!current.includes(c)) {
|
|
1342
|
+
current.push(c);
|
|
1343
|
+
changed = true;
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
if (changed) dom.setAttribute(el, "class", current.join(" "));
|
|
1347
|
+
}
|
|
1348
|
+
function removeClasses(dom, el, classes2) {
|
|
1349
|
+
if (classes2.length === 0) return;
|
|
1350
|
+
const current = splitClass(dom.getAttribute(el, "class"));
|
|
1351
|
+
const next = current.filter((c) => !classes2.includes(c));
|
|
1352
|
+
if (next.length !== current.length) {
|
|
1353
|
+
if (next.length === 0) dom.removeAttribute(el, "class");
|
|
1354
|
+
else dom.setAttribute(el, "class", next.join(" "));
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
function startRun(dom, el, active, from, to, duration, onDone) {
|
|
1358
|
+
const h = host();
|
|
1359
|
+
let finished = false;
|
|
1360
|
+
let timer = null;
|
|
1361
|
+
const onEvent = (e) => {
|
|
1362
|
+
if (e.target !== el) return;
|
|
1363
|
+
finish();
|
|
1364
|
+
};
|
|
1365
|
+
const detach = () => {
|
|
1366
|
+
dom.removeEventListener(el, "transitionend", onEvent);
|
|
1367
|
+
dom.removeEventListener(el, "animationend", onEvent);
|
|
1368
|
+
if (timer !== null) {
|
|
1369
|
+
h.clearTimeout(timer);
|
|
1370
|
+
timer = null;
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
const finish = () => {
|
|
1374
|
+
if (finished) return;
|
|
1375
|
+
finished = true;
|
|
1376
|
+
detach();
|
|
1377
|
+
removeClasses(dom, el, active);
|
|
1378
|
+
removeClasses(dom, el, to);
|
|
1379
|
+
removeClasses(dom, el, from);
|
|
1380
|
+
onDone();
|
|
1381
|
+
};
|
|
1382
|
+
addClasses(dom, el, from);
|
|
1383
|
+
addClasses(dom, el, active);
|
|
1384
|
+
dom.addEventListener(el, "transitionend", onEvent);
|
|
1385
|
+
dom.addEventListener(el, "animationend", onEvent);
|
|
1386
|
+
timer = h.setTimeout(finish, duration);
|
|
1387
|
+
nextFrame(() => {
|
|
1388
|
+
if (finished) return;
|
|
1389
|
+
removeClasses(dom, el, from);
|
|
1390
|
+
addClasses(dom, el, to);
|
|
1391
|
+
});
|
|
1392
|
+
return {
|
|
1393
|
+
cancel: () => {
|
|
1394
|
+
if (finished) return;
|
|
1395
|
+
finished = true;
|
|
1396
|
+
detach();
|
|
1397
|
+
removeClasses(dom, el, active);
|
|
1398
|
+
removeClasses(dom, el, to);
|
|
1399
|
+
removeClasses(dom, el, from);
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
var TransitionController = class {
|
|
1404
|
+
constructor(dom, graph, finalize) {
|
|
1405
|
+
this.dom = dom;
|
|
1406
|
+
this.graph = graph;
|
|
1407
|
+
this.finalize = finalize;
|
|
1408
|
+
}
|
|
1409
|
+
leaving = /* @__PURE__ */ new Map();
|
|
1410
|
+
/** True only in a real DOM environment (browser). */
|
|
1411
|
+
get browser() {
|
|
1412
|
+
return this.dom.body() !== null;
|
|
1413
|
+
}
|
|
1414
|
+
keyOf(inst) {
|
|
1415
|
+
return inst.graphNode.key ?? inst.graphNode.id;
|
|
1416
|
+
}
|
|
1417
|
+
resolved(node) {
|
|
1418
|
+
return getResolvedTransition(this.graph, node.id);
|
|
1419
|
+
}
|
|
1420
|
+
/** Run the enter animation for `inst` if it carries a transition (browser only). */
|
|
1421
|
+
enter(inst) {
|
|
1422
|
+
if (!this.browser) return;
|
|
1423
|
+
const rt = this.resolved(inst.graphNode);
|
|
1424
|
+
if (rt === void 0) return;
|
|
1425
|
+
const el = inst.domNode;
|
|
1426
|
+
if (!this.dom.isElement(el)) return;
|
|
1427
|
+
startRun(this.dom, el, rt.enterActive, rt.enterFrom, rt.enterTo, rt.duration, () => {
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1430
|
+
/**
|
|
1431
|
+
* Play `appear` for any initial child that opted into it (fresh browser mount
|
|
1432
|
+
* only — hydration must never animate appear, §22, and this is called only on
|
|
1433
|
+
* the mount path).
|
|
1434
|
+
*/
|
|
1435
|
+
appear(children) {
|
|
1436
|
+
if (!this.browser) return;
|
|
1437
|
+
for (const child of children) {
|
|
1438
|
+
const rt = this.resolved(child.graphNode);
|
|
1439
|
+
if (rt !== void 0 && rt.appear) this.enter(child);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
hooks() {
|
|
1443
|
+
return {
|
|
1444
|
+
takeLeaving: (key) => {
|
|
1445
|
+
const entry = this.leaving.get(key);
|
|
1446
|
+
if (entry === void 0) return void 0;
|
|
1447
|
+
entry.run.cancel();
|
|
1448
|
+
this.leaving.delete(key);
|
|
1449
|
+
return entry.inst;
|
|
1450
|
+
},
|
|
1451
|
+
beginLeave: (inst) => {
|
|
1452
|
+
if (!this.browser) return false;
|
|
1453
|
+
const rt = this.resolved(inst.graphNode);
|
|
1454
|
+
if (rt === void 0) return false;
|
|
1455
|
+
const el = inst.domNode;
|
|
1456
|
+
if (!this.dom.isElement(el)) return false;
|
|
1457
|
+
const key = this.keyOf(inst);
|
|
1458
|
+
const prior = this.leaving.get(key);
|
|
1459
|
+
if (prior !== void 0) prior.run.cancel();
|
|
1460
|
+
const run = startRun(
|
|
1461
|
+
this.dom,
|
|
1462
|
+
el,
|
|
1463
|
+
rt.leaveActive,
|
|
1464
|
+
rt.leaveFrom,
|
|
1465
|
+
rt.leaveTo,
|
|
1466
|
+
rt.duration,
|
|
1467
|
+
() => {
|
|
1468
|
+
const current = this.leaving.get(key);
|
|
1469
|
+
if (current !== void 0 && current.run === run) {
|
|
1470
|
+
this.leaving.delete(key);
|
|
1471
|
+
this.finalize(inst);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
);
|
|
1475
|
+
this.leaving.set(key, { inst, run });
|
|
1476
|
+
return true;
|
|
1477
|
+
},
|
|
1478
|
+
onEnter: (inst) => this.enter(inst)
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1482
|
+
|
|
1483
|
+
// ../renderer/src/head.ts
|
|
1484
|
+
var HEAD_MARKER = "data-streetui-head";
|
|
1485
|
+
var HEAD_KEY = "data-streetui-head-key";
|
|
1486
|
+
function isSignalLike(v) {
|
|
1487
|
+
return v !== null && typeof v === "object" && typeof v["subscribe"] === "function" && typeof v["peek"] === "function";
|
|
1488
|
+
}
|
|
1489
|
+
function readValue(v) {
|
|
1490
|
+
if (isSignalLike(v)) return String(v.peek() ?? "");
|
|
1491
|
+
return String(v ?? "");
|
|
1492
|
+
}
|
|
1493
|
+
function resolveEntry(entry) {
|
|
1494
|
+
const attrs = {};
|
|
1495
|
+
for (const key of Object.keys(entry.attrs)) {
|
|
1496
|
+
attrs[key] = readValue(entry.attrs[key]);
|
|
1497
|
+
}
|
|
1498
|
+
const resolved = { tag: entry.tag, attrs };
|
|
1499
|
+
if (entry.tag === "title") {
|
|
1500
|
+
return { ...resolved, text: readValue(entry.text) };
|
|
1501
|
+
}
|
|
1502
|
+
return resolved;
|
|
1503
|
+
}
|
|
1504
|
+
var HeadManager = class {
|
|
1505
|
+
_dom;
|
|
1506
|
+
_head;
|
|
1507
|
+
_contributions = /* @__PURE__ */ new Map();
|
|
1508
|
+
_applied = /* @__PURE__ */ new Map();
|
|
1509
|
+
_order = 0;
|
|
1510
|
+
_adopted = false;
|
|
1511
|
+
constructor(dom, head) {
|
|
1512
|
+
this._dom = dom;
|
|
1513
|
+
this._head = head;
|
|
1514
|
+
}
|
|
1515
|
+
/** Register (or replace) a node's contribution and re-apply the merged result. */
|
|
1516
|
+
register(nodeId, entries) {
|
|
1517
|
+
this._contributions.set(nodeId, { order: this._order++, entries });
|
|
1518
|
+
this.apply();
|
|
1519
|
+
}
|
|
1520
|
+
/** Withdraw a node's contribution (unmount / route change) and re-apply. */
|
|
1521
|
+
unregister(nodeId) {
|
|
1522
|
+
if (this._contributions.delete(nodeId)) this.apply();
|
|
1523
|
+
}
|
|
1524
|
+
/** Recompute the merged head and patch `document.head` to match. */
|
|
1525
|
+
apply() {
|
|
1526
|
+
if (!this._adopted) {
|
|
1527
|
+
this._adoptServerTags();
|
|
1528
|
+
this._adopted = true;
|
|
1529
|
+
}
|
|
1530
|
+
const ordered = [...this._contributions.values()].sort((a, b) => a.order - b.order);
|
|
1531
|
+
const merged = /* @__PURE__ */ new Map();
|
|
1532
|
+
for (const contribution of ordered) {
|
|
1533
|
+
for (const entry of contribution.entries) {
|
|
1534
|
+
merged.set(entry.dedupKey, resolveEntry(entry));
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
for (const [key, desired] of merged) {
|
|
1538
|
+
const existing = this._applied.get(key);
|
|
1539
|
+
if (existing !== void 0 && existing.tag === desired.tag) {
|
|
1540
|
+
this._reconcileAttrs(existing, desired);
|
|
1541
|
+
} else {
|
|
1542
|
+
if (existing !== void 0) {
|
|
1543
|
+
this._dom.removeChild(this._head, existing.el);
|
|
1544
|
+
this._applied.delete(key);
|
|
1545
|
+
}
|
|
1546
|
+
const el = this._createTag(key, desired);
|
|
1547
|
+
this._dom.appendChild(this._head, el);
|
|
1548
|
+
this._applied.set(key, { el, attrKeys: new Set(Object.keys(desired.attrs)), tag: desired.tag });
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
for (const [key, record] of [...this._applied]) {
|
|
1552
|
+
if (!merged.has(key)) {
|
|
1553
|
+
this._dom.removeChild(this._head, record.el);
|
|
1554
|
+
this._applied.delete(key);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
_createTag(key, desired) {
|
|
1559
|
+
const el = this._dom.createElement(desired.tag);
|
|
1560
|
+
this._dom.setAttribute(el, HEAD_MARKER, "");
|
|
1561
|
+
this._dom.setAttribute(el, HEAD_KEY, key);
|
|
1562
|
+
for (const attr of Object.keys(desired.attrs)) {
|
|
1563
|
+
this._dom.setAttribute(el, attr, desired.attrs[attr]);
|
|
1564
|
+
}
|
|
1565
|
+
if (desired.tag === "title") this._dom.setTextContent(el, desired.text ?? "");
|
|
1566
|
+
return el;
|
|
1567
|
+
}
|
|
1568
|
+
_reconcileAttrs(record, desired) {
|
|
1569
|
+
const nextKeys = new Set(Object.keys(desired.attrs));
|
|
1570
|
+
for (const attr of record.attrKeys) {
|
|
1571
|
+
if (!nextKeys.has(attr)) this._dom.removeAttribute(record.el, attr);
|
|
1572
|
+
}
|
|
1573
|
+
for (const attr of nextKeys) {
|
|
1574
|
+
this._dom.setAttribute(record.el, attr, desired.attrs[attr]);
|
|
1575
|
+
}
|
|
1576
|
+
if (desired.tag === "title") this._dom.setTextContent(record.el, desired.text ?? "");
|
|
1577
|
+
record.attrKeys = nextKeys;
|
|
1578
|
+
}
|
|
1579
|
+
/**
|
|
1580
|
+
* Seed `_applied` from server-emitted `[data-streetui-head-key]` tags already
|
|
1581
|
+
* in `document.head`. The subsequent diff reuses these elements when the
|
|
1582
|
+
* client desires the same key (no duplicate), rewrites them if the value
|
|
1583
|
+
* changed, or removes them if the client graph no longer wants them.
|
|
1584
|
+
*/
|
|
1585
|
+
_adoptServerTags() {
|
|
1586
|
+
for (const child of this._dom.childNodes(this._head)) {
|
|
1587
|
+
if (!this._dom.isElement(child)) continue;
|
|
1588
|
+
const el = child;
|
|
1589
|
+
const key = this._dom.getAttribute(el, HEAD_KEY);
|
|
1590
|
+
if (key === null) continue;
|
|
1591
|
+
this._applied.set(key, {
|
|
1592
|
+
el,
|
|
1593
|
+
attrKeys: new Set(this._attrNames(el)),
|
|
1594
|
+
tag: this._dom.tagName(el)
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
/** The framework-managed attribute names currently on a server tag. */
|
|
1599
|
+
_attrNames(el) {
|
|
1600
|
+
const names = [];
|
|
1601
|
+
if (this._dom.getAttribute(el, HEAD_MARKER) !== null) names.push(HEAD_MARKER);
|
|
1602
|
+
if (this._dom.getAttribute(el, HEAD_KEY) !== null) names.push(HEAD_KEY);
|
|
1603
|
+
return names;
|
|
1604
|
+
}
|
|
1605
|
+
};
|
|
1606
|
+
function getHeadManager(ctx) {
|
|
1607
|
+
const head = ctx.dom.head();
|
|
1608
|
+
if (head === null) return null;
|
|
1609
|
+
const mutable = ctx;
|
|
1610
|
+
if (mutable.head === void 0) mutable.head = new HeadManager(ctx.dom, head);
|
|
1611
|
+
return mutable.head;
|
|
1612
|
+
}
|
|
1613
|
+
function wireHeadBehavior(ctx, graphNode, instance) {
|
|
1614
|
+
const manager = getHeadManager(ctx);
|
|
1615
|
+
if (manager === null) return;
|
|
1616
|
+
const descFn = ctx.graph.getHandler(`__head__${graphNode.id}`);
|
|
1617
|
+
if (descFn === void 0) return;
|
|
1618
|
+
const contribution = descFn();
|
|
1619
|
+
const nodeId = graphNode.id;
|
|
1620
|
+
manager.register(nodeId, contribution.entries);
|
|
1621
|
+
for (const entry of contribution.entries) {
|
|
1622
|
+
for (const attrKey of Object.keys(entry.attrs)) {
|
|
1623
|
+
const v = entry.attrs[attrKey];
|
|
1624
|
+
if (isSignalLike(v)) {
|
|
1625
|
+
instance.trackCleanup(v.subscribe(() => manager.apply()));
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
if (isSignalLike(entry.text)) {
|
|
1629
|
+
instance.trackCleanup(entry.text.subscribe(() => manager.apply()));
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
instance.trackCleanup(() => manager.unregister(nodeId));
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1242
1635
|
// ../renderer/src/mount.ts
|
|
1243
1636
|
var SKIP_PROP_KEYS = /* @__PURE__ */ new Set([
|
|
1244
1637
|
"text",
|
|
@@ -1361,6 +1754,9 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
1361
1754
|
const instance2 = new NodeInstance(graphNode, el2);
|
|
1362
1755
|
ctx.instances.set(graphNode.id, instance2);
|
|
1363
1756
|
wireEvents(dom, graph, graphNode, el2, instance2);
|
|
1757
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
1758
|
+
wireSignalBindings(ctx, graphNode, instance2, linkUpdate(dom, el2));
|
|
1759
|
+
}
|
|
1364
1760
|
dom.appendChild(parentDom, el2);
|
|
1365
1761
|
return instance2;
|
|
1366
1762
|
}
|
|
@@ -1391,7 +1787,7 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
1391
1787
|
instance2.addChild(childInstance);
|
|
1392
1788
|
}
|
|
1393
1789
|
dom.appendChild(parentDom, el2);
|
|
1394
|
-
wireReactiveList(ctx, graphNode, instance2, el2);
|
|
1790
|
+
wireReactiveList(ctx, graphNode, instance2, el2, true);
|
|
1395
1791
|
return instance2;
|
|
1396
1792
|
}
|
|
1397
1793
|
if (graphNode.type === "portal") {
|
|
@@ -1416,6 +1812,16 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
1416
1812
|
wireOverlayBehavior(ctx, graphNode, instance2, target);
|
|
1417
1813
|
return instance2;
|
|
1418
1814
|
}
|
|
1815
|
+
if (graphNode.type === "head") {
|
|
1816
|
+
const anchor = dom.createElement(resolveTag("head"));
|
|
1817
|
+
dom.setAttribute(anchor, "data-streetui-head-anchor", "");
|
|
1818
|
+
applyNodeProps(ctx, graphNode, anchor);
|
|
1819
|
+
const instance2 = new NodeInstance(graphNode, anchor);
|
|
1820
|
+
ctx.instances.set(graphNode.id, instance2);
|
|
1821
|
+
dom.appendChild(parentDom, anchor);
|
|
1822
|
+
wireHeadBehavior(ctx, graphNode, instance2);
|
|
1823
|
+
return instance2;
|
|
1824
|
+
}
|
|
1419
1825
|
const tag = resolveTag(graphNode.type);
|
|
1420
1826
|
const el = dom.createElement(tag);
|
|
1421
1827
|
applyNodeProps(ctx, graphNode, el);
|
|
@@ -1480,6 +1886,17 @@ function buttonUpdate(dom, el) {
|
|
|
1480
1886
|
}
|
|
1481
1887
|
};
|
|
1482
1888
|
}
|
|
1889
|
+
function linkUpdate(dom, el) {
|
|
1890
|
+
return (propKey, value) => {
|
|
1891
|
+
if (propKey === "label") {
|
|
1892
|
+
dom.setTextContent(el, String(value ?? ""));
|
|
1893
|
+
} else if (propKey === "href") {
|
|
1894
|
+
dom.setAttribute(el, "href", String(value ?? ""));
|
|
1895
|
+
} else {
|
|
1896
|
+
applyProp(dom, el, propKey, value);
|
|
1897
|
+
}
|
|
1898
|
+
};
|
|
1899
|
+
}
|
|
1483
1900
|
function applyNodeProps(ctx, graphNode, el) {
|
|
1484
1901
|
const props = graphNode.props;
|
|
1485
1902
|
for (const key in props) {
|
|
@@ -1500,32 +1917,42 @@ function wireSignalBindings(ctx, graphNode, instance, onUpdate) {
|
|
|
1500
1917
|
instance.trackCleanup(unsub);
|
|
1501
1918
|
}
|
|
1502
1919
|
}
|
|
1503
|
-
function wireReactiveList(ctx, graphNode, instance, el) {
|
|
1920
|
+
function wireReactiveList(ctx, graphNode, instance, el, runAppear = false) {
|
|
1504
1921
|
const plan = ctx.graph.getHandler(`__listplan__${graphNode.id}`);
|
|
1505
1922
|
const build = ctx.graph.getHandler(`__listbuild__${graphNode.id}`);
|
|
1506
1923
|
if (plan === void 0 && build === void 0) return;
|
|
1924
|
+
const controller = new TransitionController(ctx.dom, ctx.graph, (leaving) => {
|
|
1925
|
+
const parent = ctx.dom.parentNode(leaving.domNode);
|
|
1926
|
+
if (parent !== null) ctx.dom.removeChild(parent, leaving.domNode);
|
|
1927
|
+
leaving.dispose();
|
|
1928
|
+
forgetInstance(ctx, leaving);
|
|
1929
|
+
ctx.graph.detachNode(leaving.graphNode);
|
|
1930
|
+
});
|
|
1931
|
+
const hooks = controller.hooks();
|
|
1932
|
+
if (runAppear) controller.appear(instance.children);
|
|
1507
1933
|
for (const stateRef of graphNode.stateRefs) {
|
|
1508
1934
|
if (stateRef.propKey !== "items") continue;
|
|
1509
1935
|
const sig = ctx.graph.getHandler(`__signal__${stateRef.signalId}`);
|
|
1510
1936
|
if (sig === void 0 || typeof sig.subscribe !== "function") continue;
|
|
1511
1937
|
const unsub = sig.subscribe((value) => {
|
|
1512
1938
|
if (plan !== void 0) {
|
|
1513
|
-
reconcileReactiveListByPlan(ctx, graphNode, instance, el, plan(value));
|
|
1939
|
+
reconcileReactiveListByPlan(ctx, graphNode, instance, el, plan(value), hooks);
|
|
1514
1940
|
} else {
|
|
1515
|
-
reconcileReactiveList(ctx, graphNode, instance, el, build(value));
|
|
1941
|
+
reconcileReactiveList(ctx, graphNode, instance, el, build(value), hooks);
|
|
1516
1942
|
}
|
|
1517
1943
|
});
|
|
1518
1944
|
instance.trackCleanup(unsub);
|
|
1519
1945
|
}
|
|
1520
1946
|
}
|
|
1521
|
-
function reconcileReactiveListByPlan(ctx, listNode, listInstance, listEl, plan) {
|
|
1947
|
+
function reconcileReactiveListByPlan(ctx, listNode, listInstance, listEl, plan, hooks) {
|
|
1522
1948
|
const oldInstances = [...listInstance.children];
|
|
1523
1949
|
const result = reconcileChildrenByPlan(
|
|
1524
1950
|
ctx,
|
|
1525
1951
|
listEl,
|
|
1526
1952
|
oldInstances,
|
|
1527
1953
|
plan,
|
|
1528
|
-
(node, parent) => mountNode(ctx, node, parent)
|
|
1954
|
+
(node, parent) => mountNode(ctx, node, parent),
|
|
1955
|
+
hooks
|
|
1529
1956
|
);
|
|
1530
1957
|
listInstance.children.length = 0;
|
|
1531
1958
|
for (const inst of result.instances) listInstance.children.push(inst);
|
|
@@ -1540,14 +1967,15 @@ function reconcileReactiveListByPlan(ctx, listNode, listInstance, listEl, plan)
|
|
|
1540
1967
|
for (const child of [...listNode.children]) listNode.removeChild(child);
|
|
1541
1968
|
for (const inst of result.instances) listNode.appendChild(inst.graphNode);
|
|
1542
1969
|
}
|
|
1543
|
-
function reconcileReactiveList(ctx, listNode, listInstance, listEl, newNodes) {
|
|
1970
|
+
function reconcileReactiveList(ctx, listNode, listInstance, listEl, newNodes, hooks) {
|
|
1544
1971
|
const oldInstances = [...listInstance.children];
|
|
1545
1972
|
const result = reconcileChildren(
|
|
1546
1973
|
ctx,
|
|
1547
1974
|
listEl,
|
|
1548
1975
|
oldInstances,
|
|
1549
1976
|
newNodes,
|
|
1550
|
-
(node, parent) => mountNode(ctx, node, parent)
|
|
1977
|
+
(node, parent) => mountNode(ctx, node, parent),
|
|
1978
|
+
hooks
|
|
1551
1979
|
);
|
|
1552
1980
|
listInstance.children.length = 0;
|
|
1553
1981
|
for (const inst of result.instances) listInstance.children.push(inst);
|
|
@@ -1583,11 +2011,14 @@ function wireOverlayBehavior(ctx, graphNode, instance, target) {
|
|
|
1583
2011
|
const onOpenChange = (isOpen) => {
|
|
1584
2012
|
if (isOpen) {
|
|
1585
2013
|
if (desc.restoreFocus) saved = saveFocus(dom);
|
|
1586
|
-
if (desc.takesFocus) focusInitial(dom, target, desc.initialFocusId);
|
|
1587
2014
|
if (desc.modal) {
|
|
1588
2015
|
active.push(trapFocus(dom, target));
|
|
1589
2016
|
active.push(containFocus(dom, target));
|
|
1590
2017
|
}
|
|
2018
|
+
if (desc.menu) {
|
|
2019
|
+
active.push(rovingMenu(dom, target));
|
|
2020
|
+
}
|
|
2021
|
+
if (desc.takesFocus) focusInitial(dom, target, desc.initialFocusId);
|
|
1591
2022
|
if (desc.closeOnEscape && desc.onClose !== void 0) {
|
|
1592
2023
|
active.push(onEscape(dom, target, desc.onClose));
|
|
1593
2024
|
}
|
|
@@ -1693,7 +2124,12 @@ function hydrateNode(ctx, graphNode, domNode, path) {
|
|
|
1693
2124
|
case "link": {
|
|
1694
2125
|
const instance = new NodeInstance(graphNode, domNode);
|
|
1695
2126
|
ctx.instances.set(graphNode.id, instance);
|
|
1696
|
-
if (graphNode.type === "link")
|
|
2127
|
+
if (graphNode.type === "link") {
|
|
2128
|
+
wireEvents(dom, graph, graphNode, domNode, instance);
|
|
2129
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
2130
|
+
wireSignalBindings(ctx, graphNode, instance, linkUpdate(dom, domNode));
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
1697
2133
|
return instance;
|
|
1698
2134
|
}
|
|
1699
2135
|
case "reactive-list":
|
|
@@ -1723,6 +2159,12 @@ function hydrateNode(ctx, graphNode, domNode, path) {
|
|
|
1723
2159
|
wireOverlayBehavior(ctx, graphNode, instance, target);
|
|
1724
2160
|
return instance;
|
|
1725
2161
|
}
|
|
2162
|
+
case "head": {
|
|
2163
|
+
const instance = new NodeInstance(graphNode, domNode);
|
|
2164
|
+
ctx.instances.set(graphNode.id, instance);
|
|
2165
|
+
wireHeadBehavior(ctx, graphNode, instance);
|
|
2166
|
+
return instance;
|
|
2167
|
+
}
|
|
1726
2168
|
default: {
|
|
1727
2169
|
const instance = new NodeInstance(graphNode, domNode);
|
|
1728
2170
|
ctx.instances.set(graphNode.id, instance);
|
|
@@ -1933,7 +2375,8 @@ function analyzeGraph(graph) {
|
|
|
1933
2375
|
const isConditional = node.type === "conditional";
|
|
1934
2376
|
const isPortal = node.type === "portal";
|
|
1935
2377
|
const isComponent = node.type === "component";
|
|
1936
|
-
const
|
|
2378
|
+
const isHead = node.type === "head";
|
|
2379
|
+
const isStatic = node.stateRefs.length === 0 && !hasEvents && !isList && !isConditional && !isPortal && !isComponent && !isHead;
|
|
1937
2380
|
const isStaticSubtree = isStatic && allChildrenStatic;
|
|
1938
2381
|
nodes.set(node.id, {
|
|
1939
2382
|
isStatic,
|
|
@@ -2264,6 +2707,37 @@ async function waitFor(check, options = {}) {
|
|
|
2264
2707
|
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
2265
2708
|
}
|
|
2266
2709
|
}
|
|
2710
|
+
function focus(el) {
|
|
2711
|
+
el.focus?.();
|
|
2712
|
+
}
|
|
2713
|
+
function blur(el) {
|
|
2714
|
+
el.blur?.();
|
|
2715
|
+
}
|
|
2716
|
+
function pressKey(key, el = document.activeElement, init = {}) {
|
|
2717
|
+
if (el === null) return;
|
|
2718
|
+
el.dispatchEvent(
|
|
2719
|
+
new KeyboardEvent("keydown", { key, bubbles: true, cancelable: true, ...init })
|
|
2720
|
+
);
|
|
2721
|
+
}
|
|
2722
|
+
function clickOutside(container, target = document.body) {
|
|
2723
|
+
if (container.contains(target)) {
|
|
2724
|
+
throw new Error("[StreetUI Testing] clickOutside: target is inside the container");
|
|
2725
|
+
}
|
|
2726
|
+
target.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, cancelable: true }));
|
|
2727
|
+
target.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
|
|
2728
|
+
}
|
|
2729
|
+
async function openOverlay(open) {
|
|
2730
|
+
open.set(true);
|
|
2731
|
+
await flushUpdates();
|
|
2732
|
+
}
|
|
2733
|
+
async function closeOverlay(open) {
|
|
2734
|
+
open.set(false);
|
|
2735
|
+
await flushUpdates();
|
|
2736
|
+
}
|
|
2737
|
+
async function waitForTransition(el) {
|
|
2738
|
+
el.dispatchEvent(new Event("transitionend", { bubbles: true }));
|
|
2739
|
+
await flushUpdates();
|
|
2740
|
+
}
|
|
2267
2741
|
function findByText(container, text) {
|
|
2268
2742
|
const match = Array.from(container.querySelectorAll("*")).find(
|
|
2269
2743
|
(el) => el.children.length === 0 && (el.textContent?.includes(text) ?? false)
|
|
@@ -2358,6 +2832,154 @@ function renderServerThenHydrate(build, options = {}) {
|
|
|
2358
2832
|
};
|
|
2359
2833
|
}
|
|
2360
2834
|
|
|
2835
|
+
// ../dsl/src/transition.ts
|
|
2836
|
+
function classes(value) {
|
|
2837
|
+
if (value === void 0) return [];
|
|
2838
|
+
const out = [];
|
|
2839
|
+
for (const token of value.split(/\s+/)) {
|
|
2840
|
+
if (token.length > 0 && !out.includes(token)) out.push(token);
|
|
2841
|
+
}
|
|
2842
|
+
return out;
|
|
2843
|
+
}
|
|
2844
|
+
function merge(a, b) {
|
|
2845
|
+
const out = [...a];
|
|
2846
|
+
for (const token of b) if (!out.includes(token)) out.push(token);
|
|
2847
|
+
return out;
|
|
2848
|
+
}
|
|
2849
|
+
function resolveTransition(config) {
|
|
2850
|
+
const n = config.name;
|
|
2851
|
+
const enterActive = merge(
|
|
2852
|
+
classes(config.enter),
|
|
2853
|
+
classes(config.enterActive ?? (n !== void 0 ? `${n}-enter-active` : void 0))
|
|
2854
|
+
);
|
|
2855
|
+
const leaveActive = merge(
|
|
2856
|
+
classes(config.leave),
|
|
2857
|
+
classes(config.leaveActive ?? (n !== void 0 ? `${n}-leave-active` : void 0))
|
|
2858
|
+
);
|
|
2859
|
+
return {
|
|
2860
|
+
enterActive,
|
|
2861
|
+
enterFrom: classes(config.enterFrom ?? (n !== void 0 ? `${n}-enter-from` : void 0)),
|
|
2862
|
+
enterTo: classes(config.enterTo ?? (n !== void 0 ? `${n}-enter-to` : void 0)),
|
|
2863
|
+
leaveActive,
|
|
2864
|
+
leaveFrom: classes(config.leaveFrom ?? (n !== void 0 ? `${n}-leave-from` : void 0)),
|
|
2865
|
+
leaveTo: classes(config.leaveTo ?? (n !== void 0 ? `${n}-leave-to` : void 0)),
|
|
2866
|
+
appear: config.appear ?? false,
|
|
2867
|
+
duration: config.duration ?? 1e3
|
|
2868
|
+
};
|
|
2869
|
+
}
|
|
2870
|
+
|
|
2871
|
+
// ../dsl/src/head.ts
|
|
2872
|
+
function metaDedupKey(m) {
|
|
2873
|
+
if (m.charset !== void 0) return "meta:charset";
|
|
2874
|
+
if (m.name !== void 0) return `meta:name=${m.name}`;
|
|
2875
|
+
if (m.property !== void 0) return `meta:property=${m.property}`;
|
|
2876
|
+
if (m.httpEquiv !== void 0) return `meta:http-equiv=${m.httpEquiv}`;
|
|
2877
|
+
return void 0;
|
|
2878
|
+
}
|
|
2879
|
+
function metaAttrs(m) {
|
|
2880
|
+
const attrs = {};
|
|
2881
|
+
if (m.charset !== void 0) attrs["charset"] = m.charset;
|
|
2882
|
+
if (m.name !== void 0) attrs["name"] = m.name;
|
|
2883
|
+
if (m.property !== void 0) attrs["property"] = m.property;
|
|
2884
|
+
if (m.httpEquiv !== void 0) attrs["http-equiv"] = m.httpEquiv;
|
|
2885
|
+
if (m.content !== void 0) attrs["content"] = m.content;
|
|
2886
|
+
return attrs;
|
|
2887
|
+
}
|
|
2888
|
+
function linkAttrs(l) {
|
|
2889
|
+
const attrs = { rel: l.rel, href: l.href };
|
|
2890
|
+
if (l.sizes !== void 0) attrs["sizes"] = l.sizes;
|
|
2891
|
+
if (l.type !== void 0) attrs["type"] = l.type;
|
|
2892
|
+
if (l.media !== void 0) attrs["media"] = l.media;
|
|
2893
|
+
if (l.as !== void 0) attrs["as"] = l.as;
|
|
2894
|
+
if (l.crossorigin !== void 0) attrs["crossorigin"] = l.crossorigin;
|
|
2895
|
+
if (l.hreflang !== void 0) attrs["hreflang"] = l.hreflang;
|
|
2896
|
+
return attrs;
|
|
2897
|
+
}
|
|
2898
|
+
function resolveHead(config) {
|
|
2899
|
+
const entries = [];
|
|
2900
|
+
if (config.charset !== void 0) {
|
|
2901
|
+
entries.push({ tag: "meta", dedupKey: "meta:charset", attrs: { charset: config.charset } });
|
|
2902
|
+
}
|
|
2903
|
+
if (config.base !== void 0) {
|
|
2904
|
+
entries.push({ tag: "base", dedupKey: "base", attrs: { href: config.base } });
|
|
2905
|
+
}
|
|
2906
|
+
if (config.title !== void 0) {
|
|
2907
|
+
entries.push({ tag: "title", dedupKey: "title", attrs: {}, text: config.title });
|
|
2908
|
+
}
|
|
2909
|
+
if (config.description !== void 0) {
|
|
2910
|
+
entries.push({
|
|
2911
|
+
tag: "meta",
|
|
2912
|
+
dedupKey: "meta:name=description",
|
|
2913
|
+
attrs: { name: "description", content: config.description }
|
|
2914
|
+
});
|
|
2915
|
+
}
|
|
2916
|
+
if (config.canonical !== void 0) {
|
|
2917
|
+
entries.push({
|
|
2918
|
+
tag: "link",
|
|
2919
|
+
dedupKey: "link:rel=canonical",
|
|
2920
|
+
attrs: { rel: "canonical", href: config.canonical }
|
|
2921
|
+
});
|
|
2922
|
+
}
|
|
2923
|
+
if (config.robots !== void 0) {
|
|
2924
|
+
entries.push({
|
|
2925
|
+
tag: "meta",
|
|
2926
|
+
dedupKey: "meta:name=robots",
|
|
2927
|
+
attrs: { name: "robots", content: config.robots }
|
|
2928
|
+
});
|
|
2929
|
+
}
|
|
2930
|
+
if (config.themeColor !== void 0) {
|
|
2931
|
+
entries.push({
|
|
2932
|
+
tag: "meta",
|
|
2933
|
+
dedupKey: "meta:name=theme-color",
|
|
2934
|
+
attrs: { name: "theme-color", content: config.themeColor }
|
|
2935
|
+
});
|
|
2936
|
+
}
|
|
2937
|
+
if (config.viewport !== void 0) {
|
|
2938
|
+
entries.push({
|
|
2939
|
+
tag: "meta",
|
|
2940
|
+
dedupKey: "meta:name=viewport",
|
|
2941
|
+
attrs: { name: "viewport", content: config.viewport }
|
|
2942
|
+
});
|
|
2943
|
+
}
|
|
2944
|
+
if (config.favicon !== void 0) {
|
|
2945
|
+
const l = typeof config.favicon === "string" ? { rel: "icon", href: config.favicon } : config.favicon;
|
|
2946
|
+
entries.push({ tag: "link", dedupKey: `link:rel=${l.rel}`, attrs: linkAttrs(l) });
|
|
2947
|
+
}
|
|
2948
|
+
if (config.openGraph !== void 0) {
|
|
2949
|
+
for (const key of Object.keys(config.openGraph)) {
|
|
2950
|
+
const property = `og:${key}`;
|
|
2951
|
+
entries.push({
|
|
2952
|
+
tag: "meta",
|
|
2953
|
+
dedupKey: `meta:property=${property}`,
|
|
2954
|
+
attrs: { property, content: config.openGraph[key] }
|
|
2955
|
+
});
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
if (config.twitter !== void 0) {
|
|
2959
|
+
for (const key of Object.keys(config.twitter)) {
|
|
2960
|
+
const name = `twitter:${key}`;
|
|
2961
|
+
entries.push({
|
|
2962
|
+
tag: "meta",
|
|
2963
|
+
dedupKey: `meta:name=${name}`,
|
|
2964
|
+
attrs: { name, content: config.twitter[key] }
|
|
2965
|
+
});
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
if (config.meta !== void 0) {
|
|
2969
|
+
for (const m of config.meta) {
|
|
2970
|
+
const key = metaDedupKey(m);
|
|
2971
|
+
if (key === void 0) continue;
|
|
2972
|
+
entries.push({ tag: "meta", dedupKey: key, attrs: metaAttrs(m) });
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
if (config.link !== void 0) {
|
|
2976
|
+
for (const l of config.link) {
|
|
2977
|
+
entries.push({ tag: "link", dedupKey: `link:rel=${l.rel}:href=${l.href}`, attrs: linkAttrs(l) });
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
return { entries };
|
|
2981
|
+
}
|
|
2982
|
+
|
|
2361
2983
|
// ../state/src/signal.ts
|
|
2362
2984
|
var _activeConsumer = null;
|
|
2363
2985
|
function withConsumer(consumer, fn) {
|
|
@@ -2567,6 +3189,18 @@ function applyA11yProps(props, options) {
|
|
|
2567
3189
|
if (options.ariaInvalid !== void 0) props["aria-invalid"] = String(options.ariaInvalid);
|
|
2568
3190
|
if (options.ariaRequired !== void 0) props["aria-required"] = String(options.ariaRequired);
|
|
2569
3191
|
if (options.ariaModal !== void 0) props["aria-modal"] = String(options.ariaModal);
|
|
3192
|
+
if (options.ariaOwns !== void 0) props["aria-owns"] = options.ariaOwns;
|
|
3193
|
+
if (options.ariaActiveDescendant !== void 0) props["aria-activedescendant"] = options.ariaActiveDescendant;
|
|
3194
|
+
if (options.ariaHasPopup !== void 0) props["aria-haspopup"] = String(options.ariaHasPopup);
|
|
3195
|
+
if (options.ariaSelected !== void 0) props["aria-selected"] = String(options.ariaSelected);
|
|
3196
|
+
}
|
|
3197
|
+
function registerTransition(graph, node, config) {
|
|
3198
|
+
if (config === void 0) return;
|
|
3199
|
+
const resolved = resolveTransition(config);
|
|
3200
|
+
graph.registerHandler(
|
|
3201
|
+
`__transition__${node.id}`,
|
|
3202
|
+
() => resolved
|
|
3203
|
+
);
|
|
2570
3204
|
}
|
|
2571
3205
|
function containerProps(options) {
|
|
2572
3206
|
const props = {};
|
|
@@ -2627,6 +3261,7 @@ var OVERLAY_KINDS = {
|
|
|
2627
3261
|
role: "menu",
|
|
2628
3262
|
modal: false,
|
|
2629
3263
|
takesFocus: true,
|
|
3264
|
+
menu: true,
|
|
2630
3265
|
ariaModal: false,
|
|
2631
3266
|
defaultCloseOnEscape: true,
|
|
2632
3267
|
defaultRestoreFocus: true
|
|
@@ -2759,6 +3394,7 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
2759
3394
|
parent: this._node,
|
|
2760
3395
|
props: containerProps(options)
|
|
2761
3396
|
});
|
|
3397
|
+
registerTransition(this._graph, node, options.transition);
|
|
2762
3398
|
builder(new SectionBuilderImpl(node, this._graph));
|
|
2763
3399
|
}
|
|
2764
3400
|
container(key, builder, options = {}) {
|
|
@@ -2767,6 +3403,7 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
2767
3403
|
parent: this._node,
|
|
2768
3404
|
props: containerProps(options)
|
|
2769
3405
|
});
|
|
3406
|
+
registerTransition(this._graph, node, options.transition);
|
|
2770
3407
|
builder(new ContainerBuilderImpl(node, this._graph));
|
|
2771
3408
|
}
|
|
2772
3409
|
list(key, builder, options = {}) {
|
|
@@ -2775,6 +3412,7 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
2775
3412
|
parent: this._node,
|
|
2776
3413
|
props: containerProps(options)
|
|
2777
3414
|
});
|
|
3415
|
+
registerTransition(this._graph, node, options.transition);
|
|
2778
3416
|
builder(new ListBuilderImpl(node, this._graph));
|
|
2779
3417
|
}
|
|
2780
3418
|
listOf(key, items, renderItem, options = {}) {
|
|
@@ -2784,6 +3422,7 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
2784
3422
|
parent: this._node,
|
|
2785
3423
|
props: containerProps(options)
|
|
2786
3424
|
});
|
|
3425
|
+
registerTransition(graph, node, options.transition);
|
|
2787
3426
|
const signalId = `${node.id}:items`;
|
|
2788
3427
|
node.stateRefs.push({ signalId, propKey: "items" });
|
|
2789
3428
|
graph.registerHandler(`__signal__${signalId}`, items);
|
|
@@ -2805,6 +3444,7 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
2805
3444
|
}
|
|
2806
3445
|
});
|
|
2807
3446
|
renderItem(item, index, new ContainerBuilderImpl(itemNode, graph));
|
|
3447
|
+
registerTransition(graph, itemNode, options.itemTransition);
|
|
2808
3448
|
return itemNode;
|
|
2809
3449
|
};
|
|
2810
3450
|
const buildPlan = (raw) => {
|
|
@@ -2836,6 +3476,7 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
2836
3476
|
parent: this._node,
|
|
2837
3477
|
props
|
|
2838
3478
|
});
|
|
3479
|
+
registerTransition(this._graph, node, options.transition);
|
|
2839
3480
|
if (options.onSubmit !== void 0) {
|
|
2840
3481
|
const handlerKey = `submit:${node.id}`;
|
|
2841
3482
|
this._graph.registerHandler(handlerKey, options.onSubmit);
|
|
@@ -2843,18 +3484,20 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
2843
3484
|
}
|
|
2844
3485
|
builder(new FormBuilderImpl(node, this._graph));
|
|
2845
3486
|
}
|
|
2846
|
-
when(condition, builder, elseBuilder) {
|
|
3487
|
+
when(condition, builder, elseBuilder, options = {}) {
|
|
2847
3488
|
const graph = this._graph;
|
|
2848
3489
|
const node = graph.createNode("conditional", {
|
|
2849
3490
|
parent: this._node,
|
|
2850
3491
|
props: containerProps({})
|
|
2851
3492
|
});
|
|
3493
|
+
const branchTransition = options.transition !== void 0 && options.appear === true ? { ...options.transition, appear: true } : options.transition;
|
|
2852
3494
|
const buildBranch = (build, tag) => {
|
|
2853
3495
|
const branchKey = `when-${tag}:${node.id}`;
|
|
2854
3496
|
const branch = graph.createNode("container", {
|
|
2855
3497
|
key: branchKey,
|
|
2856
3498
|
props: { key: branchKey }
|
|
2857
3499
|
});
|
|
3500
|
+
registerTransition(graph, branch, branchTransition);
|
|
2858
3501
|
build(new ContainerBuilderImpl(branch, graph));
|
|
2859
3502
|
return branch;
|
|
2860
3503
|
};
|
|
@@ -2900,8 +3543,14 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
2900
3543
|
this.container(id, (c) => {
|
|
2901
3544
|
c.when(
|
|
2902
3545
|
hasError,
|
|
2903
|
-
// Error state → fallback.
|
|
2904
|
-
|
|
3546
|
+
// Error state → fallback. The fallback branch mounts exactly when the
|
|
3547
|
+
// boundary enters its error state, so this is also where the optional
|
|
3548
|
+
// `onError` reporting hook fires (observe-only; §6/§7).
|
|
3549
|
+
(fb) => {
|
|
3550
|
+
const currentError = readError();
|
|
3551
|
+
options.onError?.(currentError);
|
|
3552
|
+
options.fallback(fb, currentError, retry);
|
|
3553
|
+
},
|
|
2905
3554
|
// Healthy state → body, guarded against synchronous build throws.
|
|
2906
3555
|
(body) => {
|
|
2907
3556
|
try {
|
|
@@ -2913,6 +3562,24 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
2913
3562
|
);
|
|
2914
3563
|
}, { id });
|
|
2915
3564
|
}
|
|
3565
|
+
asyncBoundary(key, res, branches) {
|
|
3566
|
+
const isError = derived(() => res.status.get() === "error");
|
|
3567
|
+
const showSuccess = derived(
|
|
3568
|
+
() => res.status.get() !== "error" && res.data.get() !== void 0
|
|
3569
|
+
);
|
|
3570
|
+
const showLoading = derived(
|
|
3571
|
+
() => res.status.get() !== "error" && res.data.get() === void 0
|
|
3572
|
+
);
|
|
3573
|
+
const dataSignal = derived(() => res.data.get());
|
|
3574
|
+
const retry = () => {
|
|
3575
|
+
void res.refetch();
|
|
3576
|
+
};
|
|
3577
|
+
this.container(key, (c) => {
|
|
3578
|
+
c.when(isError, (fb) => branches.error?.(fb, res.error.peek(), retry));
|
|
3579
|
+
c.when(showSuccess, (sb) => branches.success(sb, dataSignal));
|
|
3580
|
+
c.when(showLoading, (lb) => branches.loading?.(lb));
|
|
3581
|
+
}, { key });
|
|
3582
|
+
}
|
|
2916
3583
|
// ── Portals & overlays ──────────────────────────────────────────────────────
|
|
2917
3584
|
portal(key, builder, options = {}) {
|
|
2918
3585
|
const node = this._graph.createNode("portal", {
|
|
@@ -2922,6 +3589,31 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
2922
3589
|
});
|
|
2923
3590
|
builder(new ContainerBuilderImpl(node, this._graph));
|
|
2924
3591
|
}
|
|
3592
|
+
/**
|
|
3593
|
+
* Declare document metadata (2.0 §1–§3). Creates a `'head'` node — a neutral,
|
|
3594
|
+
* empty inline anchor at this position (one node / one element, so positional
|
|
3595
|
+
* hydration is preserved) — and registers a `__head__<id>` descriptor holding
|
|
3596
|
+
* this call's normalized, dedup-keyed {@link resolveHead} contribution. The
|
|
3597
|
+
* renderer applies it to `document.head` on the browser (adopting server tags
|
|
3598
|
+
* on hydration, cleaning up on unmount / route change) and emits the active
|
|
3599
|
+
* graph's merged metadata as a string on the server (`renderHead`).
|
|
3600
|
+
*
|
|
3601
|
+
* Multiple `head()` nodes may be live at once (app default + route + component)
|
|
3602
|
+
* — the renderer merges them and, per dedup key, the last in document order
|
|
3603
|
+
* wins (see head.ts). No new render path: this reuses the same graph-node +
|
|
3604
|
+
* handler-registry convention as overlays/transitions/components.
|
|
3605
|
+
*/
|
|
3606
|
+
head(metadata) {
|
|
3607
|
+
const node = this._graph.createNode("head", {
|
|
3608
|
+
parent: this._node,
|
|
3609
|
+
props: { "data-streetui-head-anchor": "" }
|
|
3610
|
+
});
|
|
3611
|
+
const contribution = resolveHead(metadata);
|
|
3612
|
+
this._graph.registerHandler(
|
|
3613
|
+
`__head__${node.id}`,
|
|
3614
|
+
() => contribution
|
|
3615
|
+
);
|
|
3616
|
+
}
|
|
2925
3617
|
/**
|
|
2926
3618
|
* Shared assembly for every overlay kind: a `portal` node whose single child
|
|
2927
3619
|
* is a `when(open, panel)` conditional. The panel container carries the
|
|
@@ -2949,13 +3641,24 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
2949
3641
|
...options.ariaDescribedBy !== void 0 ? { ariaDescribedBy: options.ariaDescribedBy } : {}
|
|
2950
3642
|
};
|
|
2951
3643
|
const portalBuilder = new ContainerBuilderImpl(portalNode, graph);
|
|
2952
|
-
portalBuilder.when(
|
|
2953
|
-
|
|
2954
|
-
|
|
3644
|
+
portalBuilder.when(
|
|
3645
|
+
openSignal,
|
|
3646
|
+
(panelHost) => {
|
|
3647
|
+
panelHost.container(`${key}__panel`, builder, panelOptions);
|
|
3648
|
+
},
|
|
3649
|
+
void 0,
|
|
3650
|
+
// Overlay open/close rides the panel's `when`; a transition animates the
|
|
3651
|
+
// panel in on open and — via the reconciler's deferred-leave — plays the
|
|
3652
|
+
// leave before the panel is removed (§10). Focus is restored at close-
|
|
3653
|
+
// request time (see wireOverlayBehavior), so it never stays trapped inside
|
|
3654
|
+
// a panel that is animating away.
|
|
3655
|
+
options.transition !== void 0 ? { transition: options.transition } : {}
|
|
3656
|
+
);
|
|
2955
3657
|
const descriptor = {
|
|
2956
3658
|
open: openSignal,
|
|
2957
3659
|
modal: kind.modal,
|
|
2958
3660
|
takesFocus: kind.takesFocus,
|
|
3661
|
+
menu: kind.menu ?? false,
|
|
2959
3662
|
closeOnEscape: options.closeOnEscape ?? kind.defaultCloseOnEscape,
|
|
2960
3663
|
restoreFocus: options.restoreFocus ?? kind.defaultRestoreFocus,
|
|
2961
3664
|
...options.initialFocusId !== void 0 ? { initialFocusId: options.initialFocusId } : {},
|
|
@@ -3052,6 +3755,7 @@ var ListBuilderImpl = class extends ContentBuilderBase {
|
|
|
3052
3755
|
parent: this._node,
|
|
3053
3756
|
props: containerProps(options)
|
|
3054
3757
|
});
|
|
3758
|
+
registerTransition(this._graph, node, options.transition);
|
|
3055
3759
|
builder(new ContainerBuilderImpl(node, this._graph));
|
|
3056
3760
|
}
|
|
3057
3761
|
};
|
|
@@ -3239,6 +3943,8 @@ var ApplicationGraph = class {
|
|
|
3239
3943
|
this.handlers.delete(`__listplan__${node.id}`);
|
|
3240
3944
|
this.handlers.delete(`__overlay__${node.id}`);
|
|
3241
3945
|
this.handlers.delete(`__component__${node.id}`);
|
|
3946
|
+
this.handlers.delete(`__transition__${node.id}`);
|
|
3947
|
+
this.handlers.delete(`__head__${node.id}`);
|
|
3242
3948
|
}
|
|
3243
3949
|
// ── Handler registry ──────────────────────────────────────────────────────
|
|
3244
3950
|
registerHandler(key, fn) {
|
|
@@ -3416,26 +4122,33 @@ function trigger(el, type, init = {}) {
|
|
|
3416
4122
|
}
|
|
3417
4123
|
|
|
3418
4124
|
// src/version.ts
|
|
3419
|
-
var VERSION = "
|
|
4125
|
+
var VERSION = "2.0.0";
|
|
3420
4126
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3421
4127
|
0 && (module.exports = {
|
|
3422
4128
|
VERSION,
|
|
3423
4129
|
analyzeGraph,
|
|
4130
|
+
blur,
|
|
4131
|
+
clickOutside,
|
|
4132
|
+
closeOverlay,
|
|
3424
4133
|
findAllByRole,
|
|
3425
4134
|
findAllComponents,
|
|
3426
4135
|
findByRole,
|
|
3427
4136
|
findByText,
|
|
3428
4137
|
findComponent,
|
|
3429
4138
|
flushUpdates,
|
|
4139
|
+
focus,
|
|
3430
4140
|
formatInspection,
|
|
3431
4141
|
getComponentName,
|
|
3432
4142
|
hydrateComponent,
|
|
3433
4143
|
inspectCompilation,
|
|
4144
|
+
openOverlay,
|
|
4145
|
+
pressKey,
|
|
3434
4146
|
render,
|
|
3435
4147
|
renderComponent,
|
|
3436
4148
|
renderOnce,
|
|
3437
4149
|
renderServerThenHydrate,
|
|
3438
4150
|
trigger,
|
|
3439
|
-
waitFor
|
|
4151
|
+
waitFor,
|
|
4152
|
+
waitForTransition
|
|
3440
4153
|
});
|
|
3441
4154
|
//# sourceMappingURL=testing.cjs.map
|