streetui 1.7.0 → 1.9.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/{compile-Cte0nf06.d.cts → compile-DqsMQVJk.d.cts} +255 -4
- package/dist/{compile-Cte0nf06.d.ts → compile-DqsMQVJk.d.ts} +255 -4
- 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/{hydration-diagnostics-zK-fihvd.d.cts → hydration-diagnostics-4pwt6v7b.d.cts} +1 -1
- package/dist/{hydration-diagnostics-Ddgs3Puu.d.ts → hydration-diagnostics-B2FmY05M.d.ts} +1 -1
- package/dist/index.cjs +639 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +294 -18
- package/dist/index.d.ts +294 -18
- package/dist/index.js +627 -41
- package/dist/index.js.map +1 -1
- package/dist/{server-cgmCiPsz.d.ts → server-BYnmb_hR.d.ts} +1 -1
- package/dist/{server-D4A8eunr.d.cts → server-DnwqBTfE.d.cts} +1 -1
- package/dist/server.cjs +274 -31
- 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 +274 -31
- package/dist/server.js.map +1 -1
- package/dist/testing.cjs +1470 -33
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +101 -4
- package/dist/testing.d.ts +101 -4
- package/dist/testing.js +1456 -32
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/version.ts
|
|
2
|
-
var VERSION = "1.
|
|
2
|
+
var VERSION = "1.9.0";
|
|
3
3
|
|
|
4
4
|
// ../state/src/signal.ts
|
|
5
5
|
var _activeConsumer = null;
|
|
@@ -354,6 +354,9 @@ function a11yIds(base) {
|
|
|
354
354
|
description: `${token}-description`,
|
|
355
355
|
error: `${token}-error`,
|
|
356
356
|
title: `${token}-title`,
|
|
357
|
+
trigger: `${token}-trigger`,
|
|
358
|
+
controls: `${token}-controls`,
|
|
359
|
+
owns: `${token}-owns`,
|
|
357
360
|
id: (suffix) => `${token}-${toIdToken(suffix)}`
|
|
358
361
|
};
|
|
359
362
|
}
|
|
@@ -803,6 +806,8 @@ var ApplicationGraph = class {
|
|
|
803
806
|
this.handlers.delete(`__listbuild__${node.id}`);
|
|
804
807
|
this.handlers.delete(`__listplan__${node.id}`);
|
|
805
808
|
this.handlers.delete(`__overlay__${node.id}`);
|
|
809
|
+
this.handlers.delete(`__component__${node.id}`);
|
|
810
|
+
this.handlers.delete(`__transition__${node.id}`);
|
|
806
811
|
}
|
|
807
812
|
// ── Handler registry ──────────────────────────────────────────────────────
|
|
808
813
|
registerHandler(key, fn) {
|
|
@@ -891,6 +896,47 @@ var ApplicationGraph = class {
|
|
|
891
896
|
}
|
|
892
897
|
};
|
|
893
898
|
|
|
899
|
+
// ../dsl/src/transition.ts
|
|
900
|
+
function classes(value) {
|
|
901
|
+
if (value === void 0) return [];
|
|
902
|
+
const out = [];
|
|
903
|
+
for (const token of value.split(/\s+/)) {
|
|
904
|
+
if (token.length > 0 && !out.includes(token)) out.push(token);
|
|
905
|
+
}
|
|
906
|
+
return out;
|
|
907
|
+
}
|
|
908
|
+
function merge(a, b) {
|
|
909
|
+
const out = [...a];
|
|
910
|
+
for (const token of b) if (!out.includes(token)) out.push(token);
|
|
911
|
+
return out;
|
|
912
|
+
}
|
|
913
|
+
function resolveTransition(config) {
|
|
914
|
+
const n = config.name;
|
|
915
|
+
const enterActive = merge(
|
|
916
|
+
classes(config.enter),
|
|
917
|
+
classes(config.enterActive ?? (n !== void 0 ? `${n}-enter-active` : void 0))
|
|
918
|
+
);
|
|
919
|
+
const leaveActive = merge(
|
|
920
|
+
classes(config.leave),
|
|
921
|
+
classes(config.leaveActive ?? (n !== void 0 ? `${n}-leave-active` : void 0))
|
|
922
|
+
);
|
|
923
|
+
return {
|
|
924
|
+
enterActive,
|
|
925
|
+
enterFrom: classes(config.enterFrom ?? (n !== void 0 ? `${n}-enter-from` : void 0)),
|
|
926
|
+
enterTo: classes(config.enterTo ?? (n !== void 0 ? `${n}-enter-to` : void 0)),
|
|
927
|
+
leaveActive,
|
|
928
|
+
leaveFrom: classes(config.leaveFrom ?? (n !== void 0 ? `${n}-leave-from` : void 0)),
|
|
929
|
+
leaveTo: classes(config.leaveTo ?? (n !== void 0 ? `${n}-leave-to` : void 0)),
|
|
930
|
+
appear: config.appear ?? false,
|
|
931
|
+
duration: config.duration ?? 1e3
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
function isTransitionConfig(value) {
|
|
935
|
+
if (value === null || typeof value !== "object") return false;
|
|
936
|
+
const o = value;
|
|
937
|
+
return typeof o["name"] === "string" || typeof o["enter"] === "string" || typeof o["enterActive"] === "string" || typeof o["enterFrom"] === "string" || typeof o["leave"] === "string" || typeof o["leaveActive"] === "string" || typeof o["leaveFrom"] === "string";
|
|
938
|
+
}
|
|
939
|
+
|
|
894
940
|
// ../dsl/src/builders.ts
|
|
895
941
|
function isSignal(v) {
|
|
896
942
|
return v !== null && typeof v === "object" && typeof v["get"] === "function" && typeof v["subscribe"] === "function";
|
|
@@ -918,6 +964,18 @@ function applyA11yProps(props, options) {
|
|
|
918
964
|
if (options.ariaInvalid !== void 0) props["aria-invalid"] = String(options.ariaInvalid);
|
|
919
965
|
if (options.ariaRequired !== void 0) props["aria-required"] = String(options.ariaRequired);
|
|
920
966
|
if (options.ariaModal !== void 0) props["aria-modal"] = String(options.ariaModal);
|
|
967
|
+
if (options.ariaOwns !== void 0) props["aria-owns"] = options.ariaOwns;
|
|
968
|
+
if (options.ariaActiveDescendant !== void 0) props["aria-activedescendant"] = options.ariaActiveDescendant;
|
|
969
|
+
if (options.ariaHasPopup !== void 0) props["aria-haspopup"] = String(options.ariaHasPopup);
|
|
970
|
+
if (options.ariaSelected !== void 0) props["aria-selected"] = String(options.ariaSelected);
|
|
971
|
+
}
|
|
972
|
+
function registerTransition(graph, node, config) {
|
|
973
|
+
if (config === void 0) return;
|
|
974
|
+
const resolved = resolveTransition(config);
|
|
975
|
+
graph.registerHandler(
|
|
976
|
+
`__transition__${node.id}`,
|
|
977
|
+
() => resolved
|
|
978
|
+
);
|
|
921
979
|
}
|
|
922
980
|
function containerProps(options) {
|
|
923
981
|
const props = {};
|
|
@@ -978,6 +1036,7 @@ var OVERLAY_KINDS = {
|
|
|
978
1036
|
role: "menu",
|
|
979
1037
|
modal: false,
|
|
980
1038
|
takesFocus: true,
|
|
1039
|
+
menu: true,
|
|
981
1040
|
ariaModal: false,
|
|
982
1041
|
defaultCloseOnEscape: true,
|
|
983
1042
|
defaultRestoreFocus: true
|
|
@@ -1110,6 +1169,7 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1110
1169
|
parent: this._node,
|
|
1111
1170
|
props: containerProps(options)
|
|
1112
1171
|
});
|
|
1172
|
+
registerTransition(this._graph, node, options.transition);
|
|
1113
1173
|
builder(new SectionBuilderImpl(node, this._graph));
|
|
1114
1174
|
}
|
|
1115
1175
|
container(key, builder, options = {}) {
|
|
@@ -1118,6 +1178,7 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1118
1178
|
parent: this._node,
|
|
1119
1179
|
props: containerProps(options)
|
|
1120
1180
|
});
|
|
1181
|
+
registerTransition(this._graph, node, options.transition);
|
|
1121
1182
|
builder(new ContainerBuilderImpl(node, this._graph));
|
|
1122
1183
|
}
|
|
1123
1184
|
list(key, builder, options = {}) {
|
|
@@ -1126,6 +1187,7 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1126
1187
|
parent: this._node,
|
|
1127
1188
|
props: containerProps(options)
|
|
1128
1189
|
});
|
|
1190
|
+
registerTransition(this._graph, node, options.transition);
|
|
1129
1191
|
builder(new ListBuilderImpl(node, this._graph));
|
|
1130
1192
|
}
|
|
1131
1193
|
listOf(key, items, renderItem, options = {}) {
|
|
@@ -1135,6 +1197,7 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1135
1197
|
parent: this._node,
|
|
1136
1198
|
props: containerProps(options)
|
|
1137
1199
|
});
|
|
1200
|
+
registerTransition(graph, node, options.transition);
|
|
1138
1201
|
const signalId = `${node.id}:items`;
|
|
1139
1202
|
node.stateRefs.push({ signalId, propKey: "items" });
|
|
1140
1203
|
graph.registerHandler(`__signal__${signalId}`, items);
|
|
@@ -1156,6 +1219,7 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1156
1219
|
}
|
|
1157
1220
|
});
|
|
1158
1221
|
renderItem(item, index, new ContainerBuilderImpl(itemNode, graph));
|
|
1222
|
+
registerTransition(graph, itemNode, options.itemTransition);
|
|
1159
1223
|
return itemNode;
|
|
1160
1224
|
};
|
|
1161
1225
|
const buildPlan = (raw) => {
|
|
@@ -1187,6 +1251,7 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1187
1251
|
parent: this._node,
|
|
1188
1252
|
props
|
|
1189
1253
|
});
|
|
1254
|
+
registerTransition(this._graph, node, options.transition);
|
|
1190
1255
|
if (options.onSubmit !== void 0) {
|
|
1191
1256
|
const handlerKey = `submit:${node.id}`;
|
|
1192
1257
|
this._graph.registerHandler(handlerKey, options.onSubmit);
|
|
@@ -1194,18 +1259,20 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1194
1259
|
}
|
|
1195
1260
|
builder(new FormBuilderImpl(node, this._graph));
|
|
1196
1261
|
}
|
|
1197
|
-
when(condition, builder, elseBuilder) {
|
|
1262
|
+
when(condition, builder, elseBuilder, options = {}) {
|
|
1198
1263
|
const graph = this._graph;
|
|
1199
1264
|
const node = graph.createNode("conditional", {
|
|
1200
1265
|
parent: this._node,
|
|
1201
1266
|
props: containerProps({})
|
|
1202
1267
|
});
|
|
1268
|
+
const branchTransition = options.transition !== void 0 && options.appear === true ? { ...options.transition, appear: true } : options.transition;
|
|
1203
1269
|
const buildBranch = (build, tag) => {
|
|
1204
1270
|
const branchKey = `when-${tag}:${node.id}`;
|
|
1205
1271
|
const branch = graph.createNode("container", {
|
|
1206
1272
|
key: branchKey,
|
|
1207
1273
|
props: { key: branchKey }
|
|
1208
1274
|
});
|
|
1275
|
+
registerTransition(graph, branch, branchTransition);
|
|
1209
1276
|
build(new ContainerBuilderImpl(branch, graph));
|
|
1210
1277
|
return branch;
|
|
1211
1278
|
};
|
|
@@ -1300,13 +1367,24 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1300
1367
|
...options.ariaDescribedBy !== void 0 ? { ariaDescribedBy: options.ariaDescribedBy } : {}
|
|
1301
1368
|
};
|
|
1302
1369
|
const portalBuilder = new ContainerBuilderImpl(portalNode, graph);
|
|
1303
|
-
portalBuilder.when(
|
|
1304
|
-
|
|
1305
|
-
|
|
1370
|
+
portalBuilder.when(
|
|
1371
|
+
openSignal,
|
|
1372
|
+
(panelHost) => {
|
|
1373
|
+
panelHost.container(`${key}__panel`, builder, panelOptions);
|
|
1374
|
+
},
|
|
1375
|
+
void 0,
|
|
1376
|
+
// Overlay open/close rides the panel's `when`; a transition animates the
|
|
1377
|
+
// panel in on open and — via the reconciler's deferred-leave — plays the
|
|
1378
|
+
// leave before the panel is removed (§10). Focus is restored at close-
|
|
1379
|
+
// request time (see wireOverlayBehavior), so it never stays trapped inside
|
|
1380
|
+
// a panel that is animating away.
|
|
1381
|
+
options.transition !== void 0 ? { transition: options.transition } : {}
|
|
1382
|
+
);
|
|
1306
1383
|
const descriptor = {
|
|
1307
1384
|
open: openSignal,
|
|
1308
1385
|
modal: kind.modal,
|
|
1309
1386
|
takesFocus: kind.takesFocus,
|
|
1387
|
+
menu: kind.menu ?? false,
|
|
1310
1388
|
closeOnEscape: options.closeOnEscape ?? kind.defaultCloseOnEscape,
|
|
1311
1389
|
restoreFocus: options.restoreFocus ?? kind.defaultRestoreFocus,
|
|
1312
1390
|
...options.initialFocusId !== void 0 ? { initialFocusId: options.initialFocusId } : {},
|
|
@@ -1332,6 +1410,63 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1332
1410
|
toast(key, options, builder) {
|
|
1333
1411
|
this._overlay(OVERLAY_KINDS.toast, key, options, builder);
|
|
1334
1412
|
}
|
|
1413
|
+
// ── Components ────────────────────────────────────────────────────────────
|
|
1414
|
+
/**
|
|
1415
|
+
* Instantiate a reusable component (§3–§9). Creates a `'component'` node
|
|
1416
|
+
* (rendered as a `<div>` wrapper — preserves the one-node/one-element
|
|
1417
|
+
* positional-hydration invariant), then runs `def.setup(props, ctx)`
|
|
1418
|
+
* synchronously to obtain the render function and fills the component's own
|
|
1419
|
+
* container scope with it — structurally identical to `container`/
|
|
1420
|
+
* `errorBoundary`. Cleanups the setup registers via `ctx.effect`/
|
|
1421
|
+
* `ctx.onCleanup` are collected into a closure and exposed to the renderer
|
|
1422
|
+
* through a `__component__<id>` handler (mirroring `__overlay__`); the mount/
|
|
1423
|
+
* hydrate paths read it and route each into `NodeInstance.trackCleanup`, so
|
|
1424
|
+
* teardown runs (children-first) when the component leaves the graph.
|
|
1425
|
+
*
|
|
1426
|
+
* `setup` runs once per instance here at build time. When this component sits
|
|
1427
|
+
* inside a keyed list / conditional, a rebuild disposes the old instance
|
|
1428
|
+
* (running its cleanups + pruning its `__component__` entry) and re-runs this
|
|
1429
|
+
* method for the new node — so re-invocation is safe and leak-free.
|
|
1430
|
+
*/
|
|
1431
|
+
component(key, def, props, children) {
|
|
1432
|
+
const graph = this._graph;
|
|
1433
|
+
const node = graph.createNode("component", {
|
|
1434
|
+
key,
|
|
1435
|
+
parent: this._node,
|
|
1436
|
+
// `data-streetui-component` is a non-underscore prop, so it reaches the
|
|
1437
|
+
// DOM as an attribute and is visible to DevTools (§21) — unlike the
|
|
1438
|
+
// internal `_`-prefixed metadata the renderer hides.
|
|
1439
|
+
props: { key, "data-streetui-component": def.name }
|
|
1440
|
+
});
|
|
1441
|
+
const cleanups = [];
|
|
1442
|
+
const ctx = {
|
|
1443
|
+
key,
|
|
1444
|
+
onCleanup(fn) {
|
|
1445
|
+
cleanups.push(fn);
|
|
1446
|
+
},
|
|
1447
|
+
effect(fn) {
|
|
1448
|
+
cleanups.push(effect(fn));
|
|
1449
|
+
},
|
|
1450
|
+
renderChildren(content) {
|
|
1451
|
+
if (children !== void 0) children(content);
|
|
1452
|
+
}
|
|
1453
|
+
};
|
|
1454
|
+
let render;
|
|
1455
|
+
try {
|
|
1456
|
+
render = def.setup(props, ctx);
|
|
1457
|
+
render(new ContainerBuilderImpl(node, graph));
|
|
1458
|
+
} catch (err) {
|
|
1459
|
+
queueMicrotask(() => {
|
|
1460
|
+
throw err;
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
if (cleanups.length > 0) {
|
|
1464
|
+
graph.registerHandler(
|
|
1465
|
+
`__component__${node.id}`,
|
|
1466
|
+
() => cleanups
|
|
1467
|
+
);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1335
1470
|
};
|
|
1336
1471
|
var SectionBuilderImpl = class extends ContainerBuilderBase {
|
|
1337
1472
|
};
|
|
@@ -1346,6 +1481,7 @@ var ListBuilderImpl = class extends ContentBuilderBase {
|
|
|
1346
1481
|
parent: this._node,
|
|
1347
1482
|
props: containerProps(options)
|
|
1348
1483
|
});
|
|
1484
|
+
registerTransition(this._graph, node, options.transition);
|
|
1349
1485
|
builder(new ContainerBuilderImpl(node, this._graph));
|
|
1350
1486
|
}
|
|
1351
1487
|
};
|
|
@@ -1365,6 +1501,18 @@ var AppBuilder = class {
|
|
|
1365
1501
|
}
|
|
1366
1502
|
};
|
|
1367
1503
|
|
|
1504
|
+
// ../dsl/src/component.ts
|
|
1505
|
+
function component(setup, options = {}) {
|
|
1506
|
+
return {
|
|
1507
|
+
__streetui_component: true,
|
|
1508
|
+
name: options.name ?? setup.name ?? "Component",
|
|
1509
|
+
setup
|
|
1510
|
+
};
|
|
1511
|
+
}
|
|
1512
|
+
function isComponentDefinition(value) {
|
|
1513
|
+
return value !== null && typeof value === "object" && value.__streetui_component === true;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1368
1516
|
// ../dsl/src/dsl.ts
|
|
1369
1517
|
var StreetApp = class {
|
|
1370
1518
|
_graph;
|
|
@@ -2428,18 +2576,25 @@ function trapFocus(dom, container) {
|
|
|
2428
2576
|
dom.addEventListener(container, "keydown", onKeydown);
|
|
2429
2577
|
return () => dom.removeEventListener(container, "keydown", onKeydown);
|
|
2430
2578
|
}
|
|
2579
|
+
var containmentStack = [];
|
|
2431
2580
|
function containFocus(dom, container) {
|
|
2432
2581
|
const body = dom.body();
|
|
2433
2582
|
if (body === null) return () => {
|
|
2434
2583
|
};
|
|
2584
|
+
containmentStack.push(container);
|
|
2435
2585
|
const onFocusIn = (event) => {
|
|
2586
|
+
if (containmentStack[containmentStack.length - 1] !== container) return;
|
|
2436
2587
|
const target = event.target;
|
|
2437
2588
|
if (target !== null && !dom.contains(container, target)) {
|
|
2438
2589
|
focusFirst(dom, container);
|
|
2439
2590
|
}
|
|
2440
2591
|
};
|
|
2441
2592
|
dom.addEventListener(body, "focusin", onFocusIn);
|
|
2442
|
-
return () =>
|
|
2593
|
+
return () => {
|
|
2594
|
+
dom.removeEventListener(body, "focusin", onFocusIn);
|
|
2595
|
+
const index = containmentStack.lastIndexOf(container);
|
|
2596
|
+
if (index !== -1) containmentStack.splice(index, 1);
|
|
2597
|
+
};
|
|
2443
2598
|
}
|
|
2444
2599
|
function onEscape(dom, target, handler) {
|
|
2445
2600
|
const onKeydown = (event) => {
|
|
@@ -2448,6 +2603,84 @@ function onEscape(dom, target, handler) {
|
|
|
2448
2603
|
dom.addEventListener(target, "keydown", onKeydown);
|
|
2449
2604
|
return () => dom.removeEventListener(target, "keydown", onKeydown);
|
|
2450
2605
|
}
|
|
2606
|
+
function rovingMenu(dom, container, selector = FOCUSABLE_SELECTOR) {
|
|
2607
|
+
const onKeydown = (event) => {
|
|
2608
|
+
const key = event.key;
|
|
2609
|
+
const isActivate = key === "Enter" || key === " " || key === "Spacebar";
|
|
2610
|
+
const isMove = key === "ArrowDown" || key === "ArrowUp" || key === "Home" || key === "End";
|
|
2611
|
+
if (!isActivate && !isMove) return;
|
|
2612
|
+
const items = getFocusable(dom, container, selector);
|
|
2613
|
+
if (items.length === 0) return;
|
|
2614
|
+
const active = dom.activeElement();
|
|
2615
|
+
const index = active === null ? -1 : items.indexOf(active);
|
|
2616
|
+
if (isActivate) {
|
|
2617
|
+
if (index < 0) return;
|
|
2618
|
+
event.preventDefault();
|
|
2619
|
+
items[index].click?.();
|
|
2620
|
+
return;
|
|
2621
|
+
}
|
|
2622
|
+
event.preventDefault();
|
|
2623
|
+
let next;
|
|
2624
|
+
if (key === "Home") next = 0;
|
|
2625
|
+
else if (key === "End") next = items.length - 1;
|
|
2626
|
+
else if (key === "ArrowDown") next = index < 0 ? 0 : (index + 1) % items.length;
|
|
2627
|
+
else next = index <= 0 ? items.length - 1 : index - 1;
|
|
2628
|
+
dom.focus(items[next]);
|
|
2629
|
+
};
|
|
2630
|
+
dom.addEventListener(container, "keydown", onKeydown);
|
|
2631
|
+
return () => dom.removeEventListener(container, "keydown", onKeydown);
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
// ../dom/src/live-region.ts
|
|
2635
|
+
var VISUALLY_HIDDEN = "position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;";
|
|
2636
|
+
function makeRegion(dom, politeness) {
|
|
2637
|
+
const el = dom.createElement("div");
|
|
2638
|
+
dom.setAttribute(el, "aria-live", politeness);
|
|
2639
|
+
dom.setAttribute(el, "aria-atomic", "true");
|
|
2640
|
+
dom.setAttribute(el, "role", politeness === "assertive" ? "alert" : "status");
|
|
2641
|
+
dom.setAttribute(el, "data-streetui-live", politeness);
|
|
2642
|
+
dom.setAttribute(el, "style", VISUALLY_HIDDEN);
|
|
2643
|
+
return el;
|
|
2644
|
+
}
|
|
2645
|
+
function createAnnouncer(dom) {
|
|
2646
|
+
const body = dom.body();
|
|
2647
|
+
if (body === null) {
|
|
2648
|
+
return { announce() {
|
|
2649
|
+
}, clear() {
|
|
2650
|
+
}, destroy() {
|
|
2651
|
+
} };
|
|
2652
|
+
}
|
|
2653
|
+
const polite = makeRegion(dom, "polite");
|
|
2654
|
+
const assertive = makeRegion(dom, "assertive");
|
|
2655
|
+
dom.appendChild(body, polite);
|
|
2656
|
+
dom.appendChild(body, assertive);
|
|
2657
|
+
let destroyed = false;
|
|
2658
|
+
const write = (region, message) => {
|
|
2659
|
+
dom.setTextContent(region, "");
|
|
2660
|
+
void Promise.resolve().then(() => {
|
|
2661
|
+
if (!destroyed) dom.setTextContent(region, message);
|
|
2662
|
+
});
|
|
2663
|
+
};
|
|
2664
|
+
return {
|
|
2665
|
+
announce(message, options) {
|
|
2666
|
+
if (destroyed) return;
|
|
2667
|
+
write(options?.assertive === true ? assertive : polite, message);
|
|
2668
|
+
},
|
|
2669
|
+
clear() {
|
|
2670
|
+
if (destroyed) return;
|
|
2671
|
+
dom.setTextContent(polite, "");
|
|
2672
|
+
dom.setTextContent(assertive, "");
|
|
2673
|
+
},
|
|
2674
|
+
destroy() {
|
|
2675
|
+
if (destroyed) return;
|
|
2676
|
+
destroyed = true;
|
|
2677
|
+
for (const region of [polite, assertive]) {
|
|
2678
|
+
const parent = dom.parentNode(region);
|
|
2679
|
+
if (parent !== null) dom.removeChild(parent, region);
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2682
|
+
};
|
|
2683
|
+
}
|
|
2451
2684
|
|
|
2452
2685
|
// ../renderer/src/render-context.ts
|
|
2453
2686
|
function createRenderContext(dom, graph, container, hydrationDiagnostics, staticHTML) {
|
|
@@ -2659,7 +2892,7 @@ function patchNode(ctx, graphNode, propKey, newValue) {
|
|
|
2659
2892
|
}
|
|
2660
2893
|
|
|
2661
2894
|
// ../renderer/src/reconciliation.ts
|
|
2662
|
-
function reconcileChildren(ctx, parentDom, oldInstances, newNodes, mountFn) {
|
|
2895
|
+
function reconcileChildren(ctx, parentDom, oldInstances, newNodes, mountFn, hooks) {
|
|
2663
2896
|
const oldByKey = /* @__PURE__ */ new Map();
|
|
2664
2897
|
for (const inst of oldInstances) {
|
|
2665
2898
|
const key = inst.graphNode.key ?? inst.graphNode.id;
|
|
@@ -2680,28 +2913,33 @@ function reconcileChildren(ctx, parentDom, oldInstances, newNodes, mountFn) {
|
|
|
2680
2913
|
}
|
|
2681
2914
|
newInstances.push(existing);
|
|
2682
2915
|
} else {
|
|
2683
|
-
const
|
|
2684
|
-
|
|
2916
|
+
const reclaimed = hooks?.takeLeaving(key);
|
|
2917
|
+
if (reclaimed !== void 0) {
|
|
2918
|
+
patchExistingInstance(ctx, reclaimed, newNode);
|
|
2919
|
+
reconcileItemChildren(ctx, reclaimed, newNode, mountFn);
|
|
2920
|
+
hooks?.onEnter(reclaimed);
|
|
2921
|
+
newInstances.push(reclaimed);
|
|
2922
|
+
} else {
|
|
2923
|
+
const inst = mountFn(newNode, parentDom);
|
|
2924
|
+
hooks?.onEnter(inst);
|
|
2925
|
+
newInstances.push(inst);
|
|
2926
|
+
}
|
|
2685
2927
|
}
|
|
2686
2928
|
}
|
|
2687
2929
|
const removed = [];
|
|
2688
2930
|
for (const inst of oldInstances) {
|
|
2689
2931
|
const key = inst.graphNode.key ?? inst.graphNode.id;
|
|
2690
|
-
if (
|
|
2691
|
-
|
|
2692
|
-
}
|
|
2693
|
-
}
|
|
2694
|
-
for (const inst of removed) {
|
|
2932
|
+
if (usedKeys.has(key)) continue;
|
|
2933
|
+
if (hooks !== void 0 && hooks.beginLeave(inst)) continue;
|
|
2695
2934
|
const parent = ctx.dom.parentNode(inst.domNode);
|
|
2696
|
-
if (parent !== null)
|
|
2697
|
-
ctx.dom.removeChild(parent, inst.domNode);
|
|
2698
|
-
}
|
|
2935
|
+
if (parent !== null) ctx.dom.removeChild(parent, inst.domNode);
|
|
2699
2936
|
inst.dispose();
|
|
2937
|
+
removed.push(inst);
|
|
2700
2938
|
}
|
|
2701
2939
|
reorderDom(ctx, parentDom, newInstances);
|
|
2702
2940
|
return { instances: newInstances, removed };
|
|
2703
2941
|
}
|
|
2704
|
-
function reconcileChildrenByPlan(ctx, parentDom, oldInstances, plan, mountFn) {
|
|
2942
|
+
function reconcileChildrenByPlan(ctx, parentDom, oldInstances, plan, mountFn, hooks) {
|
|
2705
2943
|
const oldByKey = /* @__PURE__ */ new Map();
|
|
2706
2944
|
for (const inst of oldInstances) {
|
|
2707
2945
|
oldByKey.set(inst.graphNode.key ?? inst.graphNode.id, inst);
|
|
@@ -2728,21 +2966,34 @@ function reconcileChildrenByPlan(ctx, parentDom, oldInstances, plan, mountFn) {
|
|
|
2728
2966
|
}
|
|
2729
2967
|
newInstances.push(existing);
|
|
2730
2968
|
} else {
|
|
2731
|
-
const
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2969
|
+
const reclaimed = hooks?.takeLeaving(entry.key);
|
|
2970
|
+
if (reclaimed !== void 0) {
|
|
2971
|
+
const freshNode = entry.build();
|
|
2972
|
+
built.push(freshNode);
|
|
2973
|
+
patchExistingInstance(ctx, reclaimed, freshNode);
|
|
2974
|
+
reconcileItemChildren(ctx, reclaimed, freshNode, mountFn);
|
|
2975
|
+
reclaimed.graphNode.setProp("_sig", freshNode.getProp("_sig"));
|
|
2976
|
+
reclaimed.graphNode.setProp("_item", entry.item);
|
|
2977
|
+
hooks?.onEnter(reclaimed);
|
|
2978
|
+
newInstances.push(reclaimed);
|
|
2979
|
+
} else {
|
|
2980
|
+
const freshNode = entry.build();
|
|
2981
|
+
built.push(freshNode);
|
|
2982
|
+
const inst = mountFn(freshNode, parentDom);
|
|
2983
|
+
hooks?.onEnter(inst);
|
|
2984
|
+
newInstances.push(inst);
|
|
2985
|
+
}
|
|
2735
2986
|
}
|
|
2736
2987
|
}
|
|
2737
2988
|
const removed = [];
|
|
2738
2989
|
for (const inst of oldInstances) {
|
|
2739
2990
|
const key = inst.graphNode.key ?? inst.graphNode.id;
|
|
2740
|
-
if (
|
|
2741
|
-
|
|
2742
|
-
for (const inst of removed) {
|
|
2991
|
+
if (usedKeys.has(key)) continue;
|
|
2992
|
+
if (hooks !== void 0 && hooks.beginLeave(inst)) continue;
|
|
2743
2993
|
const parent = ctx.dom.parentNode(inst.domNode);
|
|
2744
2994
|
if (parent !== null) ctx.dom.removeChild(parent, inst.domNode);
|
|
2745
2995
|
inst.dispose();
|
|
2996
|
+
removed.push(inst);
|
|
2746
2997
|
}
|
|
2747
2998
|
reorderDomMinimal(ctx, parentDom, oldInstances, newInstances);
|
|
2748
2999
|
return { instances: newInstances, removed, built };
|
|
@@ -2868,6 +3119,187 @@ function patchExistingInstance(ctx, instance, newNode) {
|
|
|
2868
3119
|
}
|
|
2869
3120
|
}
|
|
2870
3121
|
|
|
3122
|
+
// ../renderer/src/transition.ts
|
|
3123
|
+
function getResolvedTransition(graph, nodeId) {
|
|
3124
|
+
const fn = graph.getHandler(`__transition__${nodeId}`);
|
|
3125
|
+
return fn === void 0 ? void 0 : fn();
|
|
3126
|
+
}
|
|
3127
|
+
function host() {
|
|
3128
|
+
return globalThis;
|
|
3129
|
+
}
|
|
3130
|
+
function nextFrame(cb) {
|
|
3131
|
+
const h = host();
|
|
3132
|
+
const raf = h.requestAnimationFrame;
|
|
3133
|
+
if (typeof raf === "function") {
|
|
3134
|
+
raf(() => raf(cb));
|
|
3135
|
+
} else {
|
|
3136
|
+
h.setTimeout(cb, 0);
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
function splitClass(value) {
|
|
3140
|
+
if (value === null) return [];
|
|
3141
|
+
const out = [];
|
|
3142
|
+
for (const t of value.split(/\s+/)) if (t.length > 0) out.push(t);
|
|
3143
|
+
return out;
|
|
3144
|
+
}
|
|
3145
|
+
function addClasses(dom, el, classes2) {
|
|
3146
|
+
if (classes2.length === 0) return;
|
|
3147
|
+
const current = splitClass(dom.getAttribute(el, "class"));
|
|
3148
|
+
let changed = false;
|
|
3149
|
+
for (const c of classes2) {
|
|
3150
|
+
if (!current.includes(c)) {
|
|
3151
|
+
current.push(c);
|
|
3152
|
+
changed = true;
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
if (changed) dom.setAttribute(el, "class", current.join(" "));
|
|
3156
|
+
}
|
|
3157
|
+
function removeClasses(dom, el, classes2) {
|
|
3158
|
+
if (classes2.length === 0) return;
|
|
3159
|
+
const current = splitClass(dom.getAttribute(el, "class"));
|
|
3160
|
+
const next = current.filter((c) => !classes2.includes(c));
|
|
3161
|
+
if (next.length !== current.length) {
|
|
3162
|
+
if (next.length === 0) dom.removeAttribute(el, "class");
|
|
3163
|
+
else dom.setAttribute(el, "class", next.join(" "));
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
function startRun(dom, el, active, from, to, duration, onDone) {
|
|
3167
|
+
const h = host();
|
|
3168
|
+
let finished = false;
|
|
3169
|
+
let timer = null;
|
|
3170
|
+
const onEvent = (e) => {
|
|
3171
|
+
if (e.target !== el) return;
|
|
3172
|
+
finish();
|
|
3173
|
+
};
|
|
3174
|
+
const detach = () => {
|
|
3175
|
+
dom.removeEventListener(el, "transitionend", onEvent);
|
|
3176
|
+
dom.removeEventListener(el, "animationend", onEvent);
|
|
3177
|
+
if (timer !== null) {
|
|
3178
|
+
h.clearTimeout(timer);
|
|
3179
|
+
timer = null;
|
|
3180
|
+
}
|
|
3181
|
+
};
|
|
3182
|
+
const finish = () => {
|
|
3183
|
+
if (finished) return;
|
|
3184
|
+
finished = true;
|
|
3185
|
+
detach();
|
|
3186
|
+
removeClasses(dom, el, active);
|
|
3187
|
+
removeClasses(dom, el, to);
|
|
3188
|
+
removeClasses(dom, el, from);
|
|
3189
|
+
onDone();
|
|
3190
|
+
};
|
|
3191
|
+
addClasses(dom, el, from);
|
|
3192
|
+
addClasses(dom, el, active);
|
|
3193
|
+
dom.addEventListener(el, "transitionend", onEvent);
|
|
3194
|
+
dom.addEventListener(el, "animationend", onEvent);
|
|
3195
|
+
timer = h.setTimeout(finish, duration);
|
|
3196
|
+
nextFrame(() => {
|
|
3197
|
+
if (finished) return;
|
|
3198
|
+
removeClasses(dom, el, from);
|
|
3199
|
+
addClasses(dom, el, to);
|
|
3200
|
+
});
|
|
3201
|
+
return {
|
|
3202
|
+
cancel: () => {
|
|
3203
|
+
if (finished) return;
|
|
3204
|
+
finished = true;
|
|
3205
|
+
detach();
|
|
3206
|
+
removeClasses(dom, el, active);
|
|
3207
|
+
removeClasses(dom, el, to);
|
|
3208
|
+
removeClasses(dom, el, from);
|
|
3209
|
+
}
|
|
3210
|
+
};
|
|
3211
|
+
}
|
|
3212
|
+
function runElementTransition(dom, el, rt, phase, onDone) {
|
|
3213
|
+
if (dom.body() === null || !dom.isElement(el)) {
|
|
3214
|
+
onDone();
|
|
3215
|
+
return { cancel: () => {
|
|
3216
|
+
} };
|
|
3217
|
+
}
|
|
3218
|
+
const active = phase === "enter" ? rt.enterActive : rt.leaveActive;
|
|
3219
|
+
const from = phase === "enter" ? rt.enterFrom : rt.leaveFrom;
|
|
3220
|
+
const to = phase === "enter" ? rt.enterTo : rt.leaveTo;
|
|
3221
|
+
return startRun(dom, el, active, from, to, rt.duration, onDone);
|
|
3222
|
+
}
|
|
3223
|
+
var TransitionController = class {
|
|
3224
|
+
constructor(dom, graph, finalize) {
|
|
3225
|
+
this.dom = dom;
|
|
3226
|
+
this.graph = graph;
|
|
3227
|
+
this.finalize = finalize;
|
|
3228
|
+
}
|
|
3229
|
+
leaving = /* @__PURE__ */ new Map();
|
|
3230
|
+
/** True only in a real DOM environment (browser). */
|
|
3231
|
+
get browser() {
|
|
3232
|
+
return this.dom.body() !== null;
|
|
3233
|
+
}
|
|
3234
|
+
keyOf(inst) {
|
|
3235
|
+
return inst.graphNode.key ?? inst.graphNode.id;
|
|
3236
|
+
}
|
|
3237
|
+
resolved(node) {
|
|
3238
|
+
return getResolvedTransition(this.graph, node.id);
|
|
3239
|
+
}
|
|
3240
|
+
/** Run the enter animation for `inst` if it carries a transition (browser only). */
|
|
3241
|
+
enter(inst) {
|
|
3242
|
+
if (!this.browser) return;
|
|
3243
|
+
const rt = this.resolved(inst.graphNode);
|
|
3244
|
+
if (rt === void 0) return;
|
|
3245
|
+
const el = inst.domNode;
|
|
3246
|
+
if (!this.dom.isElement(el)) return;
|
|
3247
|
+
startRun(this.dom, el, rt.enterActive, rt.enterFrom, rt.enterTo, rt.duration, () => {
|
|
3248
|
+
});
|
|
3249
|
+
}
|
|
3250
|
+
/**
|
|
3251
|
+
* Play `appear` for any initial child that opted into it (fresh browser mount
|
|
3252
|
+
* only — hydration must never animate appear, §22, and this is called only on
|
|
3253
|
+
* the mount path).
|
|
3254
|
+
*/
|
|
3255
|
+
appear(children) {
|
|
3256
|
+
if (!this.browser) return;
|
|
3257
|
+
for (const child of children) {
|
|
3258
|
+
const rt = this.resolved(child.graphNode);
|
|
3259
|
+
if (rt !== void 0 && rt.appear) this.enter(child);
|
|
3260
|
+
}
|
|
3261
|
+
}
|
|
3262
|
+
hooks() {
|
|
3263
|
+
return {
|
|
3264
|
+
takeLeaving: (key) => {
|
|
3265
|
+
const entry = this.leaving.get(key);
|
|
3266
|
+
if (entry === void 0) return void 0;
|
|
3267
|
+
entry.run.cancel();
|
|
3268
|
+
this.leaving.delete(key);
|
|
3269
|
+
return entry.inst;
|
|
3270
|
+
},
|
|
3271
|
+
beginLeave: (inst) => {
|
|
3272
|
+
if (!this.browser) return false;
|
|
3273
|
+
const rt = this.resolved(inst.graphNode);
|
|
3274
|
+
if (rt === void 0) return false;
|
|
3275
|
+
const el = inst.domNode;
|
|
3276
|
+
if (!this.dom.isElement(el)) return false;
|
|
3277
|
+
const key = this.keyOf(inst);
|
|
3278
|
+
const prior = this.leaving.get(key);
|
|
3279
|
+
if (prior !== void 0) prior.run.cancel();
|
|
3280
|
+
const run = startRun(
|
|
3281
|
+
this.dom,
|
|
3282
|
+
el,
|
|
3283
|
+
rt.leaveActive,
|
|
3284
|
+
rt.leaveFrom,
|
|
3285
|
+
rt.leaveTo,
|
|
3286
|
+
rt.duration,
|
|
3287
|
+
() => {
|
|
3288
|
+
const current = this.leaving.get(key);
|
|
3289
|
+
if (current !== void 0 && current.run === run) {
|
|
3290
|
+
this.leaving.delete(key);
|
|
3291
|
+
this.finalize(inst);
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
);
|
|
3295
|
+
this.leaving.set(key, { inst, run });
|
|
3296
|
+
return true;
|
|
3297
|
+
},
|
|
3298
|
+
onEnter: (inst) => this.enter(inst)
|
|
3299
|
+
};
|
|
3300
|
+
}
|
|
3301
|
+
};
|
|
3302
|
+
|
|
2871
3303
|
// ../renderer/src/mount.ts
|
|
2872
3304
|
var SKIP_PROP_KEYS = /* @__PURE__ */ new Set([
|
|
2873
3305
|
"text",
|
|
@@ -3020,7 +3452,7 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
3020
3452
|
instance2.addChild(childInstance);
|
|
3021
3453
|
}
|
|
3022
3454
|
dom.appendChild(parentDom, el2);
|
|
3023
|
-
wireReactiveList(ctx, graphNode, instance2, el2);
|
|
3455
|
+
wireReactiveList(ctx, graphNode, instance2, el2, true);
|
|
3024
3456
|
return instance2;
|
|
3025
3457
|
}
|
|
3026
3458
|
if (graphNode.type === "portal") {
|
|
@@ -3064,6 +3496,7 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
3064
3496
|
instance.addChild(childInstance);
|
|
3065
3497
|
}
|
|
3066
3498
|
dom.appendChild(parentDom, el);
|
|
3499
|
+
if (graphNode.type === "component") wireComponentBehavior(ctx, graphNode, instance);
|
|
3067
3500
|
return instance;
|
|
3068
3501
|
}
|
|
3069
3502
|
function textUpdate(dom, el, textNode) {
|
|
@@ -3128,32 +3561,42 @@ function wireSignalBindings(ctx, graphNode, instance, onUpdate) {
|
|
|
3128
3561
|
instance.trackCleanup(unsub);
|
|
3129
3562
|
}
|
|
3130
3563
|
}
|
|
3131
|
-
function wireReactiveList(ctx, graphNode, instance, el) {
|
|
3564
|
+
function wireReactiveList(ctx, graphNode, instance, el, runAppear = false) {
|
|
3132
3565
|
const plan = ctx.graph.getHandler(`__listplan__${graphNode.id}`);
|
|
3133
3566
|
const build = ctx.graph.getHandler(`__listbuild__${graphNode.id}`);
|
|
3134
3567
|
if (plan === void 0 && build === void 0) return;
|
|
3568
|
+
const controller = new TransitionController(ctx.dom, ctx.graph, (leaving) => {
|
|
3569
|
+
const parent = ctx.dom.parentNode(leaving.domNode);
|
|
3570
|
+
if (parent !== null) ctx.dom.removeChild(parent, leaving.domNode);
|
|
3571
|
+
leaving.dispose();
|
|
3572
|
+
forgetInstance(ctx, leaving);
|
|
3573
|
+
ctx.graph.detachNode(leaving.graphNode);
|
|
3574
|
+
});
|
|
3575
|
+
const hooks = controller.hooks();
|
|
3576
|
+
if (runAppear) controller.appear(instance.children);
|
|
3135
3577
|
for (const stateRef of graphNode.stateRefs) {
|
|
3136
3578
|
if (stateRef.propKey !== "items") continue;
|
|
3137
3579
|
const sig = ctx.graph.getHandler(`__signal__${stateRef.signalId}`);
|
|
3138
3580
|
if (sig === void 0 || typeof sig.subscribe !== "function") continue;
|
|
3139
3581
|
const unsub = sig.subscribe((value) => {
|
|
3140
3582
|
if (plan !== void 0) {
|
|
3141
|
-
reconcileReactiveListByPlan(ctx, graphNode, instance, el, plan(value));
|
|
3583
|
+
reconcileReactiveListByPlan(ctx, graphNode, instance, el, plan(value), hooks);
|
|
3142
3584
|
} else {
|
|
3143
|
-
reconcileReactiveList(ctx, graphNode, instance, el, build(value));
|
|
3585
|
+
reconcileReactiveList(ctx, graphNode, instance, el, build(value), hooks);
|
|
3144
3586
|
}
|
|
3145
3587
|
});
|
|
3146
3588
|
instance.trackCleanup(unsub);
|
|
3147
3589
|
}
|
|
3148
3590
|
}
|
|
3149
|
-
function reconcileReactiveListByPlan(ctx, listNode, listInstance, listEl, plan) {
|
|
3591
|
+
function reconcileReactiveListByPlan(ctx, listNode, listInstance, listEl, plan, hooks) {
|
|
3150
3592
|
const oldInstances = [...listInstance.children];
|
|
3151
3593
|
const result = reconcileChildrenByPlan(
|
|
3152
3594
|
ctx,
|
|
3153
3595
|
listEl,
|
|
3154
3596
|
oldInstances,
|
|
3155
3597
|
plan,
|
|
3156
|
-
(node, parent) => mountNode(ctx, node, parent)
|
|
3598
|
+
(node, parent) => mountNode(ctx, node, parent),
|
|
3599
|
+
hooks
|
|
3157
3600
|
);
|
|
3158
3601
|
listInstance.children.length = 0;
|
|
3159
3602
|
for (const inst of result.instances) listInstance.children.push(inst);
|
|
@@ -3168,14 +3611,15 @@ function reconcileReactiveListByPlan(ctx, listNode, listInstance, listEl, plan)
|
|
|
3168
3611
|
for (const child of [...listNode.children]) listNode.removeChild(child);
|
|
3169
3612
|
for (const inst of result.instances) listNode.appendChild(inst.graphNode);
|
|
3170
3613
|
}
|
|
3171
|
-
function reconcileReactiveList(ctx, listNode, listInstance, listEl, newNodes) {
|
|
3614
|
+
function reconcileReactiveList(ctx, listNode, listInstance, listEl, newNodes, hooks) {
|
|
3172
3615
|
const oldInstances = [...listInstance.children];
|
|
3173
3616
|
const result = reconcileChildren(
|
|
3174
3617
|
ctx,
|
|
3175
3618
|
listEl,
|
|
3176
3619
|
oldInstances,
|
|
3177
3620
|
newNodes,
|
|
3178
|
-
(node, parent) => mountNode(ctx, node, parent)
|
|
3621
|
+
(node, parent) => mountNode(ctx, node, parent),
|
|
3622
|
+
hooks
|
|
3179
3623
|
);
|
|
3180
3624
|
listInstance.children.length = 0;
|
|
3181
3625
|
for (const inst of result.instances) listInstance.children.push(inst);
|
|
@@ -3211,11 +3655,14 @@ function wireOverlayBehavior(ctx, graphNode, instance, target) {
|
|
|
3211
3655
|
const onOpenChange = (isOpen) => {
|
|
3212
3656
|
if (isOpen) {
|
|
3213
3657
|
if (desc.restoreFocus) saved = saveFocus(dom);
|
|
3214
|
-
if (desc.takesFocus) focusInitial(dom, target, desc.initialFocusId);
|
|
3215
3658
|
if (desc.modal) {
|
|
3216
3659
|
active.push(trapFocus(dom, target));
|
|
3217
3660
|
active.push(containFocus(dom, target));
|
|
3218
3661
|
}
|
|
3662
|
+
if (desc.menu) {
|
|
3663
|
+
active.push(rovingMenu(dom, target));
|
|
3664
|
+
}
|
|
3665
|
+
if (desc.takesFocus) focusInitial(dom, target, desc.initialFocusId);
|
|
3219
3666
|
if (desc.closeOnEscape && desc.onClose !== void 0) {
|
|
3220
3667
|
active.push(onEscape(dom, target, desc.onClose));
|
|
3221
3668
|
}
|
|
@@ -3232,6 +3679,11 @@ function wireOverlayBehavior(ctx, graphNode, instance, target) {
|
|
|
3232
3679
|
instance.trackCleanup(teardown);
|
|
3233
3680
|
if (openSig.peek() === true) onOpenChange(true);
|
|
3234
3681
|
}
|
|
3682
|
+
function wireComponentBehavior(ctx, graphNode, instance) {
|
|
3683
|
+
const fn = ctx.graph.getHandler(`__component__${graphNode.id}`);
|
|
3684
|
+
if (fn === void 0) return;
|
|
3685
|
+
for (const cleanup of fn()) instance.trackCleanup(cleanup);
|
|
3686
|
+
}
|
|
3235
3687
|
|
|
3236
3688
|
// ../renderer/src/hydration-diagnostics.ts
|
|
3237
3689
|
function formatHydrationDiagnostic(d) {
|
|
@@ -3363,6 +3815,7 @@ function hydrateNode(ctx, graphNode, domNode, path) {
|
|
|
3363
3815
|
return instance;
|
|
3364
3816
|
}
|
|
3365
3817
|
hydrateChildren(ctx, graphNode, instance, domNode, path);
|
|
3818
|
+
if (graphNode.type === "component") wireComponentBehavior(ctx, graphNode, instance);
|
|
3366
3819
|
return instance;
|
|
3367
3820
|
}
|
|
3368
3821
|
}
|
|
@@ -3597,7 +4050,8 @@ function analyzeGraph(graph) {
|
|
|
3597
4050
|
const isList = node.type === "reactive-list";
|
|
3598
4051
|
const isConditional = node.type === "conditional";
|
|
3599
4052
|
const isPortal = node.type === "portal";
|
|
3600
|
-
const
|
|
4053
|
+
const isComponent = node.type === "component";
|
|
4054
|
+
const isStatic = node.stateRefs.length === 0 && !hasEvents && !isList && !isConditional && !isPortal && !isComponent;
|
|
3601
4055
|
const isStaticSubtree = isStatic && allChildrenStatic;
|
|
3602
4056
|
nodes.set(node.id, {
|
|
3603
4057
|
isStatic,
|
|
@@ -3943,16 +4397,38 @@ function mountRouter(router, options) {
|
|
|
3943
4397
|
} else {
|
|
3944
4398
|
outlet = container;
|
|
3945
4399
|
}
|
|
4400
|
+
const routeTransition = options.transition !== void 0 ? resolveTransition(options.transition) : void 0;
|
|
4401
|
+
const txDom = routeTransition !== void 0 ? new BrowserDOMAdapter() : void 0;
|
|
3946
4402
|
let active = null;
|
|
4403
|
+
const pendingLeaves = /* @__PURE__ */ new Map();
|
|
3947
4404
|
let firstRender = hydrateMode;
|
|
4405
|
+
const teardownRoute = (route) => {
|
|
4406
|
+
route.registry.run();
|
|
4407
|
+
route.mounted.unmount();
|
|
4408
|
+
if (route.host !== null && txDom !== void 0) {
|
|
4409
|
+
const parent = txDom.parentNode(route.host);
|
|
4410
|
+
if (parent !== null) txDom.removeChild(parent, route.host);
|
|
4411
|
+
}
|
|
4412
|
+
};
|
|
3948
4413
|
const disposeActive = () => {
|
|
3949
4414
|
if (active === null) return;
|
|
3950
|
-
active
|
|
3951
|
-
active.mounted.unmount();
|
|
4415
|
+
teardownRoute(active);
|
|
3952
4416
|
active = null;
|
|
3953
4417
|
};
|
|
3954
|
-
const
|
|
3955
|
-
|
|
4418
|
+
const wrapOutletChildren = (dom) => {
|
|
4419
|
+
const host2 = dom.createElement("div");
|
|
4420
|
+
dom.setAttribute(host2, "data-streetui-route", "");
|
|
4421
|
+
const moved = [];
|
|
4422
|
+
let child = dom.firstChild(outlet);
|
|
4423
|
+
while (child !== null) {
|
|
4424
|
+
moved.push(child);
|
|
4425
|
+
child = dom.nextSibling(child);
|
|
4426
|
+
}
|
|
4427
|
+
for (const n of moved) dom.appendChild(host2, n);
|
|
4428
|
+
dom.appendChild(outlet, host2);
|
|
4429
|
+
return host2;
|
|
4430
|
+
};
|
|
4431
|
+
const buildRoute = (match, target, hydrate) => {
|
|
3956
4432
|
const registry = new CleanupRegistry();
|
|
3957
4433
|
const ctx = {
|
|
3958
4434
|
path: match.path,
|
|
@@ -3965,9 +4441,53 @@ function mountRouter(router, options) {
|
|
|
3965
4441
|
routeApp.page("route", (page) => match.route.builder(page, ctx));
|
|
3966
4442
|
const runtime = createRuntime({ renderer });
|
|
3967
4443
|
const routeCompiled = compile(routeApp);
|
|
3968
|
-
const mounted =
|
|
4444
|
+
const mounted = hydrate ? runtime.hydrate(routeCompiled, target) : runtime.mount(routeCompiled, target);
|
|
4445
|
+
return { registry, mounted, host: null, enterRun: null };
|
|
4446
|
+
};
|
|
4447
|
+
const renderRoute = (match) => {
|
|
4448
|
+
if (routeTransition === void 0 || txDom === void 0) {
|
|
4449
|
+
disposeActive();
|
|
4450
|
+
active = buildRoute(match, outlet, firstRender);
|
|
4451
|
+
firstRender = false;
|
|
4452
|
+
return;
|
|
4453
|
+
}
|
|
4454
|
+
if (active === null) {
|
|
4455
|
+
if (firstRender) {
|
|
4456
|
+
active = buildRoute(match, outlet, true);
|
|
4457
|
+
active.host = wrapOutletChildren(txDom);
|
|
4458
|
+
} else {
|
|
4459
|
+
const host2 = txDom.createElement("div");
|
|
4460
|
+
txDom.setAttribute(host2, "data-streetui-route", "");
|
|
4461
|
+
txDom.appendChild(outlet, host2);
|
|
4462
|
+
active = buildRoute(match, host2, false);
|
|
4463
|
+
active.host = host2;
|
|
4464
|
+
}
|
|
4465
|
+
firstRender = false;
|
|
4466
|
+
return;
|
|
4467
|
+
}
|
|
3969
4468
|
firstRender = false;
|
|
3970
|
-
|
|
4469
|
+
const leaving = active;
|
|
4470
|
+
leaving.enterRun?.cancel();
|
|
4471
|
+
leaving.enterRun = null;
|
|
4472
|
+
const leaveHost = leaving.host;
|
|
4473
|
+
const enterHost = txDom.createElement("div");
|
|
4474
|
+
txDom.setAttribute(enterHost, "data-streetui-route", "");
|
|
4475
|
+
txDom.appendChild(outlet, enterHost);
|
|
4476
|
+
const next = buildRoute(match, enterHost, false);
|
|
4477
|
+
next.host = enterHost;
|
|
4478
|
+
next.enterRun = runElementTransition(txDom, enterHost, routeTransition, "enter", () => {
|
|
4479
|
+
next.enterRun = null;
|
|
4480
|
+
});
|
|
4481
|
+
active = next;
|
|
4482
|
+
if (leaveHost !== null) {
|
|
4483
|
+
const run = runElementTransition(txDom, leaveHost, routeTransition, "leave", () => {
|
|
4484
|
+
pendingLeaves.delete(leaving);
|
|
4485
|
+
teardownRoute(leaving);
|
|
4486
|
+
});
|
|
4487
|
+
pendingLeaves.set(leaving, run);
|
|
4488
|
+
} else {
|
|
4489
|
+
teardownRoute(leaving);
|
|
4490
|
+
}
|
|
3971
4491
|
};
|
|
3972
4492
|
renderRoute(router.currentRoute.peek());
|
|
3973
4493
|
const stopRouteSub = router.currentRoute.subscribe((match) => renderRoute(match));
|
|
@@ -3995,6 +4515,11 @@ function mountRouter(router, options) {
|
|
|
3995
4515
|
unmount() {
|
|
3996
4516
|
if (interceptLinks) container.removeEventListener("click", onClick);
|
|
3997
4517
|
stopRouteSub();
|
|
4518
|
+
for (const [route, run] of pendingLeaves) {
|
|
4519
|
+
run.cancel();
|
|
4520
|
+
teardownRoute(route);
|
|
4521
|
+
}
|
|
4522
|
+
pendingLeaves.clear();
|
|
3998
4523
|
disposeActive();
|
|
3999
4524
|
shellMounted?.unmount();
|
|
4000
4525
|
router.destroy();
|
|
@@ -4304,6 +4829,55 @@ function nodeTypeStats(graph) {
|
|
|
4304
4829
|
});
|
|
4305
4830
|
return counts;
|
|
4306
4831
|
}
|
|
4832
|
+
function inspectComponents(graph) {
|
|
4833
|
+
const out = [];
|
|
4834
|
+
graph.walk((node, depth) => {
|
|
4835
|
+
if (node.type !== "component") return;
|
|
4836
|
+
const name = node.props["data-streetui-component"];
|
|
4837
|
+
out.push({
|
|
4838
|
+
id: node.id,
|
|
4839
|
+
key: node.key,
|
|
4840
|
+
name: typeof name === "string" ? name : "Component",
|
|
4841
|
+
depth,
|
|
4842
|
+
childCount: node.children.length
|
|
4843
|
+
});
|
|
4844
|
+
});
|
|
4845
|
+
return out;
|
|
4846
|
+
}
|
|
4847
|
+
function inspectInteractions(graph) {
|
|
4848
|
+
const overlays = [];
|
|
4849
|
+
const transitions = [];
|
|
4850
|
+
graph.walk((node, depth) => {
|
|
4851
|
+
const overlayFn = graph.getHandler(`__overlay__${node.id}`);
|
|
4852
|
+
if (overlayFn !== void 0) {
|
|
4853
|
+
const d = overlayFn();
|
|
4854
|
+
overlays.push({
|
|
4855
|
+
id: node.id,
|
|
4856
|
+
key: node.key,
|
|
4857
|
+
open: d.open.peek(),
|
|
4858
|
+
modal: d.modal,
|
|
4859
|
+
takesFocus: d.takesFocus,
|
|
4860
|
+
menu: d.menu,
|
|
4861
|
+
closeOnEscape: d.closeOnEscape,
|
|
4862
|
+
restoreFocus: d.restoreFocus,
|
|
4863
|
+
depth
|
|
4864
|
+
});
|
|
4865
|
+
}
|
|
4866
|
+
const transitionFn = graph.getHandler(`__transition__${node.id}`);
|
|
4867
|
+
if (transitionFn !== void 0) {
|
|
4868
|
+
const t = transitionFn();
|
|
4869
|
+
transitions.push({
|
|
4870
|
+
id: node.id,
|
|
4871
|
+
key: node.key,
|
|
4872
|
+
nodeType: node.type,
|
|
4873
|
+
duration: t.duration,
|
|
4874
|
+
appear: t.appear,
|
|
4875
|
+
depth
|
|
4876
|
+
});
|
|
4877
|
+
}
|
|
4878
|
+
});
|
|
4879
|
+
return { overlays, transitions };
|
|
4880
|
+
}
|
|
4307
4881
|
|
|
4308
4882
|
// ../devtools/src/application.ts
|
|
4309
4883
|
function collectPerf(node, distinctSignals, acc) {
|
|
@@ -4677,6 +5251,7 @@ export {
|
|
|
4677
5251
|
StreetFrameworkError,
|
|
4678
5252
|
StreetRenderHandle,
|
|
4679
5253
|
StreetRendererImpl,
|
|
5254
|
+
TransitionController,
|
|
4680
5255
|
VERSION,
|
|
4681
5256
|
a11yIds,
|
|
4682
5257
|
applyNodeProps,
|
|
@@ -4687,9 +5262,11 @@ export {
|
|
|
4687
5262
|
buttonUpdate,
|
|
4688
5263
|
compile,
|
|
4689
5264
|
compileGraph,
|
|
5265
|
+
component,
|
|
4690
5266
|
consoleDiagnosticSink,
|
|
4691
5267
|
consoleHydrationDiagnosticSink,
|
|
4692
5268
|
containFocus,
|
|
5269
|
+
createAnnouncer,
|
|
4693
5270
|
createApplication,
|
|
4694
5271
|
createBrowserHistory,
|
|
4695
5272
|
createContext,
|
|
@@ -4724,20 +5301,25 @@ export {
|
|
|
4724
5301
|
generateApplicationId,
|
|
4725
5302
|
generateNodeId,
|
|
4726
5303
|
getFocusable,
|
|
5304
|
+
getResolvedTransition,
|
|
4727
5305
|
globalEventBus,
|
|
4728
5306
|
headingUpdate,
|
|
4729
5307
|
hydrateGraph,
|
|
4730
5308
|
inputUpdate,
|
|
4731
5309
|
inspectApplication,
|
|
5310
|
+
inspectComponents,
|
|
4732
5311
|
inspectContext,
|
|
4733
5312
|
inspectForm,
|
|
4734
5313
|
inspectGraph,
|
|
4735
5314
|
inspectI18n,
|
|
5315
|
+
inspectInteractions,
|
|
4736
5316
|
inspectResource,
|
|
4737
5317
|
inspectRouter,
|
|
4738
5318
|
inspectSignal,
|
|
4739
5319
|
interpolate,
|
|
4740
5320
|
isBatching,
|
|
5321
|
+
isComponentDefinition,
|
|
5322
|
+
isTransitionConfig,
|
|
4741
5323
|
matchPattern,
|
|
4742
5324
|
matchRoutes,
|
|
4743
5325
|
maxLength,
|
|
@@ -4766,9 +5348,12 @@ export {
|
|
|
4766
5348
|
required,
|
|
4767
5349
|
resetIdCounter,
|
|
4768
5350
|
resolveTag,
|
|
5351
|
+
resolveTransition,
|
|
4769
5352
|
resource,
|
|
4770
5353
|
restoreFocus,
|
|
4771
5354
|
routerOutlet,
|
|
5355
|
+
rovingMenu,
|
|
5356
|
+
runElementTransition,
|
|
4772
5357
|
runValidators,
|
|
4773
5358
|
saveFocus,
|
|
4774
5359
|
scheduleImmediate,
|
|
@@ -4787,6 +5372,7 @@ export {
|
|
|
4787
5372
|
transformGraph,
|
|
4788
5373
|
trapFocus,
|
|
4789
5374
|
validateGraph,
|
|
5375
|
+
wireComponentBehavior,
|
|
4790
5376
|
wireEvents,
|
|
4791
5377
|
wireOverlayBehavior,
|
|
4792
5378
|
wireReactiveList,
|