streetui 1.6.1 → 1.8.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-CEom4hkp.d.cts → compile-CJJQYi71.d.cts} +215 -3
- package/dist/{compile-CEom4hkp.d.ts → compile-CJJQYi71.d.ts} +215 -3
- 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-D286jpZZ.d.cts → hydration-diagnostics-BQeehq3i.d.cts} +1 -1
- package/dist/{hydration-diagnostics-YrG-OJJe.d.ts → hydration-diagnostics-DNAJumYF.d.ts} +1 -1
- package/dist/index.cjs +402 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +94 -7
- package/dist/index.d.ts +94 -7
- package/dist/index.js +390 -3
- package/dist/index.js.map +1 -1
- package/dist/{server-ZBh5DO-z.d.cts → server-CnLEnlYU.d.cts} +17 -1
- package/dist/{server-jbB-7oN-.d.ts → server-DNlg7ou2.d.ts} +17 -1
- package/dist/server.cjs +1025 -865
- 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 +1025 -865
- package/dist/server.js.map +1 -1
- package/dist/testing.cjs +1273 -3
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +63 -4
- package/dist/testing.d.ts +63 -4
- package/dist/testing.js +1267 -3
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -70,8 +70,10 @@ __export(src_exports, {
|
|
|
70
70
|
buttonUpdate: () => buttonUpdate,
|
|
71
71
|
compile: () => compile,
|
|
72
72
|
compileGraph: () => compileGraph,
|
|
73
|
+
component: () => component,
|
|
73
74
|
consoleDiagnosticSink: () => consoleDiagnosticSink,
|
|
74
75
|
consoleHydrationDiagnosticSink: () => consoleHydrationDiagnosticSink,
|
|
76
|
+
containFocus: () => containFocus,
|
|
75
77
|
createApplication: () => createApplication,
|
|
76
78
|
createBrowserHistory: () => createBrowserHistory,
|
|
77
79
|
createContext: () => createContext,
|
|
@@ -98,17 +100,20 @@ __export(src_exports, {
|
|
|
98
100
|
flushSync: () => flushSync,
|
|
99
101
|
focusById: () => focusById,
|
|
100
102
|
focusFirst: () => focusFirst,
|
|
103
|
+
focusInitial: () => focusInitial,
|
|
101
104
|
formatDiagnostic: () => formatDiagnostic,
|
|
102
105
|
formatDiagnosticContext: () => formatDiagnosticContext,
|
|
103
106
|
formatHydrationDiagnostic: () => formatHydrationDiagnostic,
|
|
104
107
|
frameworkError: () => frameworkError,
|
|
105
108
|
generateApplicationId: () => generateApplicationId,
|
|
106
109
|
generateNodeId: () => generateNodeId,
|
|
110
|
+
getFocusable: () => getFocusable,
|
|
107
111
|
globalEventBus: () => globalEventBus,
|
|
108
112
|
headingUpdate: () => headingUpdate,
|
|
109
113
|
hydrateGraph: () => hydrateGraph,
|
|
110
114
|
inputUpdate: () => inputUpdate,
|
|
111
115
|
inspectApplication: () => inspectApplication,
|
|
116
|
+
inspectComponents: () => inspectComponents,
|
|
112
117
|
inspectContext: () => inspectContext,
|
|
113
118
|
inspectForm: () => inspectForm,
|
|
114
119
|
inspectGraph: () => inspectGraph,
|
|
@@ -118,6 +123,7 @@ __export(src_exports, {
|
|
|
118
123
|
inspectSignal: () => inspectSignal,
|
|
119
124
|
interpolate: () => interpolate,
|
|
120
125
|
isBatching: () => isBatching,
|
|
126
|
+
isComponentDefinition: () => isComponentDefinition,
|
|
121
127
|
matchPattern: () => matchPattern,
|
|
122
128
|
matchRoutes: () => matchRoutes,
|
|
123
129
|
maxLength: () => maxLength,
|
|
@@ -130,6 +136,7 @@ __export(src_exports, {
|
|
|
130
136
|
nodeTypeStats: () => nodeTypeStats,
|
|
131
137
|
normalizePath: () => normalizePath,
|
|
132
138
|
observerCount: () => observerCount,
|
|
139
|
+
onEscape: () => onEscape,
|
|
133
140
|
patchNode: () => patchNode,
|
|
134
141
|
patchProp: () => patchProp,
|
|
135
142
|
pattern: () => pattern,
|
|
@@ -146,8 +153,10 @@ __export(src_exports, {
|
|
|
146
153
|
resetIdCounter: () => resetIdCounter,
|
|
147
154
|
resolveTag: () => resolveTag,
|
|
148
155
|
resource: () => resource,
|
|
156
|
+
restoreFocus: () => restoreFocus,
|
|
149
157
|
routerOutlet: () => routerOutlet,
|
|
150
158
|
runValidators: () => runValidators,
|
|
159
|
+
saveFocus: () => saveFocus,
|
|
151
160
|
scheduleImmediate: () => scheduleImmediate,
|
|
152
161
|
scheduleUpdate: () => scheduleUpdate,
|
|
153
162
|
scheduler: () => scheduler,
|
|
@@ -162,15 +171,18 @@ __export(src_exports, {
|
|
|
162
171
|
textUpdate: () => textUpdate,
|
|
163
172
|
toIdToken: () => toIdToken,
|
|
164
173
|
transformGraph: () => transformGraph,
|
|
174
|
+
trapFocus: () => trapFocus,
|
|
165
175
|
validateGraph: () => validateGraph,
|
|
176
|
+
wireComponentBehavior: () => wireComponentBehavior,
|
|
166
177
|
wireEvents: () => wireEvents,
|
|
178
|
+
wireOverlayBehavior: () => wireOverlayBehavior,
|
|
167
179
|
wireReactiveList: () => wireReactiveList,
|
|
168
180
|
wireSignalBindings: () => wireSignalBindings
|
|
169
181
|
});
|
|
170
182
|
module.exports = __toCommonJS(src_exports);
|
|
171
183
|
|
|
172
184
|
// src/version.ts
|
|
173
|
-
var VERSION = "1.
|
|
185
|
+
var VERSION = "1.8.0";
|
|
174
186
|
|
|
175
187
|
// ../state/src/signal.ts
|
|
176
188
|
var _activeConsumer = null;
|
|
@@ -973,6 +985,8 @@ var ApplicationGraph = class {
|
|
|
973
985
|
}
|
|
974
986
|
this.handlers.delete(`__listbuild__${node.id}`);
|
|
975
987
|
this.handlers.delete(`__listplan__${node.id}`);
|
|
988
|
+
this.handlers.delete(`__overlay__${node.id}`);
|
|
989
|
+
this.handlers.delete(`__component__${node.id}`);
|
|
976
990
|
}
|
|
977
991
|
// ── Handler registry ──────────────────────────────────────────────────────
|
|
978
992
|
registerHandler(key, fn) {
|
|
@@ -1087,6 +1101,7 @@ function applyA11yProps(props, options) {
|
|
|
1087
1101
|
if (options.ariaCurrent !== void 0) props["aria-current"] = String(options.ariaCurrent);
|
|
1088
1102
|
if (options.ariaInvalid !== void 0) props["aria-invalid"] = String(options.ariaInvalid);
|
|
1089
1103
|
if (options.ariaRequired !== void 0) props["aria-required"] = String(options.ariaRequired);
|
|
1104
|
+
if (options.ariaModal !== void 0) props["aria-modal"] = String(options.ariaModal);
|
|
1090
1105
|
}
|
|
1091
1106
|
function containerProps(options) {
|
|
1092
1107
|
const props = {};
|
|
@@ -1118,6 +1133,49 @@ function reactiveListItemSignature(item) {
|
|
|
1118
1133
|
function reactiveListItemKey(item, index) {
|
|
1119
1134
|
return itemIdentity(item, index);
|
|
1120
1135
|
}
|
|
1136
|
+
var OVERLAY_KINDS = {
|
|
1137
|
+
dialog: {
|
|
1138
|
+
role: "dialog",
|
|
1139
|
+
modal: true,
|
|
1140
|
+
takesFocus: true,
|
|
1141
|
+
ariaModal: true,
|
|
1142
|
+
defaultCloseOnEscape: true,
|
|
1143
|
+
defaultRestoreFocus: true
|
|
1144
|
+
},
|
|
1145
|
+
popover: {
|
|
1146
|
+
role: "dialog",
|
|
1147
|
+
modal: false,
|
|
1148
|
+
takesFocus: true,
|
|
1149
|
+
ariaModal: false,
|
|
1150
|
+
defaultCloseOnEscape: true,
|
|
1151
|
+
defaultRestoreFocus: true
|
|
1152
|
+
},
|
|
1153
|
+
tooltip: {
|
|
1154
|
+
role: "tooltip",
|
|
1155
|
+
modal: false,
|
|
1156
|
+
takesFocus: false,
|
|
1157
|
+
ariaModal: false,
|
|
1158
|
+
defaultCloseOnEscape: false,
|
|
1159
|
+
defaultRestoreFocus: false
|
|
1160
|
+
},
|
|
1161
|
+
dropdown: {
|
|
1162
|
+
role: "menu",
|
|
1163
|
+
modal: false,
|
|
1164
|
+
takesFocus: true,
|
|
1165
|
+
ariaModal: false,
|
|
1166
|
+
defaultCloseOnEscape: true,
|
|
1167
|
+
defaultRestoreFocus: true
|
|
1168
|
+
},
|
|
1169
|
+
toast: {
|
|
1170
|
+
role: "status",
|
|
1171
|
+
modal: false,
|
|
1172
|
+
takesFocus: false,
|
|
1173
|
+
ariaModal: false,
|
|
1174
|
+
ariaLive: "polite",
|
|
1175
|
+
defaultCloseOnEscape: false,
|
|
1176
|
+
defaultRestoreFocus: false
|
|
1177
|
+
}
|
|
1178
|
+
};
|
|
1121
1179
|
var ContentBuilderBase = class {
|
|
1122
1180
|
constructor(_node, _graph) {
|
|
1123
1181
|
this._node = _node;
|
|
@@ -1390,6 +1448,131 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1390
1448
|
);
|
|
1391
1449
|
}, { id });
|
|
1392
1450
|
}
|
|
1451
|
+
// ── Portals & overlays ──────────────────────────────────────────────────────
|
|
1452
|
+
portal(key, builder, options = {}) {
|
|
1453
|
+
const node = this._graph.createNode("portal", {
|
|
1454
|
+
key,
|
|
1455
|
+
parent: this._node,
|
|
1456
|
+
props: containerProps(options)
|
|
1457
|
+
});
|
|
1458
|
+
builder(new ContainerBuilderImpl(node, this._graph));
|
|
1459
|
+
}
|
|
1460
|
+
/**
|
|
1461
|
+
* Shared assembly for every overlay kind: a `portal` node whose single child
|
|
1462
|
+
* is a `when(open, panel)` conditional. The panel container carries the
|
|
1463
|
+
* kind's ARIA semantics; `builder` fills it. An `__overlay__<portalId>`
|
|
1464
|
+
* descriptor is registered so the renderer wires focus/keyboard behavior to
|
|
1465
|
+
* the same `open` signal that drives the panel. Reuses existing primitives
|
|
1466
|
+
* (portal + when + container) — no new render path.
|
|
1467
|
+
*/
|
|
1468
|
+
_overlay(kind, key, options, builder) {
|
|
1469
|
+
const graph = this._graph;
|
|
1470
|
+
const portalNode = graph.createNode("portal", {
|
|
1471
|
+
key,
|
|
1472
|
+
parent: this._node,
|
|
1473
|
+
props: { key }
|
|
1474
|
+
});
|
|
1475
|
+
const openBindable = options.open;
|
|
1476
|
+
const openSignal = isSignal(openBindable) ? openBindable : signal(openBindable);
|
|
1477
|
+
const panelOptions = {
|
|
1478
|
+
role: options.role ?? kind.role,
|
|
1479
|
+
...kind.ariaModal ? { ariaModal: true } : {},
|
|
1480
|
+
...kind.ariaLive !== void 0 ? { ariaLive: kind.ariaLive } : {},
|
|
1481
|
+
...options.class !== void 0 ? { class: options.class } : {},
|
|
1482
|
+
...options.ariaLabel !== void 0 ? { ariaLabel: options.ariaLabel } : {},
|
|
1483
|
+
...options.ariaLabelledBy !== void 0 ? { ariaLabelledBy: options.ariaLabelledBy } : {},
|
|
1484
|
+
...options.ariaDescribedBy !== void 0 ? { ariaDescribedBy: options.ariaDescribedBy } : {}
|
|
1485
|
+
};
|
|
1486
|
+
const portalBuilder = new ContainerBuilderImpl(portalNode, graph);
|
|
1487
|
+
portalBuilder.when(openSignal, (panelHost) => {
|
|
1488
|
+
panelHost.container(`${key}__panel`, builder, panelOptions);
|
|
1489
|
+
});
|
|
1490
|
+
const descriptor = {
|
|
1491
|
+
open: openSignal,
|
|
1492
|
+
modal: kind.modal,
|
|
1493
|
+
takesFocus: kind.takesFocus,
|
|
1494
|
+
closeOnEscape: options.closeOnEscape ?? kind.defaultCloseOnEscape,
|
|
1495
|
+
restoreFocus: options.restoreFocus ?? kind.defaultRestoreFocus,
|
|
1496
|
+
...options.initialFocusId !== void 0 ? { initialFocusId: options.initialFocusId } : {},
|
|
1497
|
+
...options.onClose !== void 0 ? { onClose: options.onClose } : {}
|
|
1498
|
+
};
|
|
1499
|
+
graph.registerHandler(
|
|
1500
|
+
`__overlay__${portalNode.id}`,
|
|
1501
|
+
() => descriptor
|
|
1502
|
+
);
|
|
1503
|
+
}
|
|
1504
|
+
dialog(key, options, builder) {
|
|
1505
|
+
this._overlay(OVERLAY_KINDS.dialog, key, options, builder);
|
|
1506
|
+
}
|
|
1507
|
+
popover(key, options, builder) {
|
|
1508
|
+
this._overlay(OVERLAY_KINDS.popover, key, options, builder);
|
|
1509
|
+
}
|
|
1510
|
+
tooltip(key, options, builder) {
|
|
1511
|
+
this._overlay(OVERLAY_KINDS.tooltip, key, options, builder);
|
|
1512
|
+
}
|
|
1513
|
+
dropdown(key, options, builder) {
|
|
1514
|
+
this._overlay(OVERLAY_KINDS.dropdown, key, options, builder);
|
|
1515
|
+
}
|
|
1516
|
+
toast(key, options, builder) {
|
|
1517
|
+
this._overlay(OVERLAY_KINDS.toast, key, options, builder);
|
|
1518
|
+
}
|
|
1519
|
+
// ── Components ────────────────────────────────────────────────────────────
|
|
1520
|
+
/**
|
|
1521
|
+
* Instantiate a reusable component (§3–§9). Creates a `'component'` node
|
|
1522
|
+
* (rendered as a `<div>` wrapper — preserves the one-node/one-element
|
|
1523
|
+
* positional-hydration invariant), then runs `def.setup(props, ctx)`
|
|
1524
|
+
* synchronously to obtain the render function and fills the component's own
|
|
1525
|
+
* container scope with it — structurally identical to `container`/
|
|
1526
|
+
* `errorBoundary`. Cleanups the setup registers via `ctx.effect`/
|
|
1527
|
+
* `ctx.onCleanup` are collected into a closure and exposed to the renderer
|
|
1528
|
+
* through a `__component__<id>` handler (mirroring `__overlay__`); the mount/
|
|
1529
|
+
* hydrate paths read it and route each into `NodeInstance.trackCleanup`, so
|
|
1530
|
+
* teardown runs (children-first) when the component leaves the graph.
|
|
1531
|
+
*
|
|
1532
|
+
* `setup` runs once per instance here at build time. When this component sits
|
|
1533
|
+
* inside a keyed list / conditional, a rebuild disposes the old instance
|
|
1534
|
+
* (running its cleanups + pruning its `__component__` entry) and re-runs this
|
|
1535
|
+
* method for the new node — so re-invocation is safe and leak-free.
|
|
1536
|
+
*/
|
|
1537
|
+
component(key, def, props, children) {
|
|
1538
|
+
const graph = this._graph;
|
|
1539
|
+
const node = graph.createNode("component", {
|
|
1540
|
+
key,
|
|
1541
|
+
parent: this._node,
|
|
1542
|
+
// `data-streetui-component` is a non-underscore prop, so it reaches the
|
|
1543
|
+
// DOM as an attribute and is visible to DevTools (§21) — unlike the
|
|
1544
|
+
// internal `_`-prefixed metadata the renderer hides.
|
|
1545
|
+
props: { key, "data-streetui-component": def.name }
|
|
1546
|
+
});
|
|
1547
|
+
const cleanups = [];
|
|
1548
|
+
const ctx = {
|
|
1549
|
+
key,
|
|
1550
|
+
onCleanup(fn) {
|
|
1551
|
+
cleanups.push(fn);
|
|
1552
|
+
},
|
|
1553
|
+
effect(fn) {
|
|
1554
|
+
cleanups.push(effect(fn));
|
|
1555
|
+
},
|
|
1556
|
+
renderChildren(content) {
|
|
1557
|
+
if (children !== void 0) children(content);
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1560
|
+
let render;
|
|
1561
|
+
try {
|
|
1562
|
+
render = def.setup(props, ctx);
|
|
1563
|
+
render(new ContainerBuilderImpl(node, graph));
|
|
1564
|
+
} catch (err) {
|
|
1565
|
+
queueMicrotask(() => {
|
|
1566
|
+
throw err;
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
if (cleanups.length > 0) {
|
|
1570
|
+
graph.registerHandler(
|
|
1571
|
+
`__component__${node.id}`,
|
|
1572
|
+
() => cleanups
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1393
1576
|
};
|
|
1394
1577
|
var SectionBuilderImpl = class extends ContainerBuilderBase {
|
|
1395
1578
|
};
|
|
@@ -1423,6 +1606,18 @@ var AppBuilder = class {
|
|
|
1423
1606
|
}
|
|
1424
1607
|
};
|
|
1425
1608
|
|
|
1609
|
+
// ../dsl/src/component.ts
|
|
1610
|
+
function component(setup, options = {}) {
|
|
1611
|
+
return {
|
|
1612
|
+
__streetui_component: true,
|
|
1613
|
+
name: options.name ?? setup.name ?? "Component",
|
|
1614
|
+
setup
|
|
1615
|
+
};
|
|
1616
|
+
}
|
|
1617
|
+
function isComponentDefinition(value) {
|
|
1618
|
+
return value !== null && typeof value === "object" && value.__streetui_component === true;
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1426
1621
|
// ../dsl/src/dsl.ts
|
|
1427
1622
|
var StreetApp = class {
|
|
1428
1623
|
_graph;
|
|
@@ -1990,6 +2185,18 @@ var BrowserDOMAdapter = class {
|
|
|
1990
2185
|
focus(element) {
|
|
1991
2186
|
element.focus?.();
|
|
1992
2187
|
}
|
|
2188
|
+
body() {
|
|
2189
|
+
return document.body ?? null;
|
|
2190
|
+
}
|
|
2191
|
+
activeElement() {
|
|
2192
|
+
return document.activeElement ?? null;
|
|
2193
|
+
}
|
|
2194
|
+
contains(ancestor, node) {
|
|
2195
|
+
return ancestor.contains(node);
|
|
2196
|
+
}
|
|
2197
|
+
matches(element, selector) {
|
|
2198
|
+
return typeof element.matches === "function" && element.matches(selector);
|
|
2199
|
+
}
|
|
1993
2200
|
isElement(node) {
|
|
1994
2201
|
return node.nodeType === Node.ELEMENT_NODE;
|
|
1995
2202
|
}
|
|
@@ -2357,6 +2564,18 @@ var ServerDOMAdapter = class {
|
|
|
2357
2564
|
}
|
|
2358
2565
|
focus() {
|
|
2359
2566
|
}
|
|
2567
|
+
body() {
|
|
2568
|
+
return null;
|
|
2569
|
+
}
|
|
2570
|
+
activeElement() {
|
|
2571
|
+
return null;
|
|
2572
|
+
}
|
|
2573
|
+
contains(_ancestor, _node) {
|
|
2574
|
+
return false;
|
|
2575
|
+
}
|
|
2576
|
+
matches(_element, _selector) {
|
|
2577
|
+
return false;
|
|
2578
|
+
}
|
|
2360
2579
|
isElement(node) {
|
|
2361
2580
|
return asServer(node).kind === "element";
|
|
2362
2581
|
}
|
|
@@ -2422,6 +2641,66 @@ function focusFirst(dom, container, selector = FOCUSABLE_SELECTOR) {
|
|
|
2422
2641
|
dom.focus(el);
|
|
2423
2642
|
return true;
|
|
2424
2643
|
}
|
|
2644
|
+
function getFocusable(dom, container, selector = FOCUSABLE_SELECTOR) {
|
|
2645
|
+
return Array.from(dom.querySelectorAll(container, selector)).filter(
|
|
2646
|
+
(el) => dom.matches(el, selector)
|
|
2647
|
+
);
|
|
2648
|
+
}
|
|
2649
|
+
function saveFocus(dom) {
|
|
2650
|
+
return dom.activeElement();
|
|
2651
|
+
}
|
|
2652
|
+
function restoreFocus(dom, saved) {
|
|
2653
|
+
if (saved !== null) dom.focus(saved);
|
|
2654
|
+
}
|
|
2655
|
+
function focusInitial(dom, container, initialFocusId) {
|
|
2656
|
+
if (initialFocusId !== void 0 && focusById(dom, container, initialFocusId)) return;
|
|
2657
|
+
focusFirst(dom, container);
|
|
2658
|
+
}
|
|
2659
|
+
function trapFocus(dom, container) {
|
|
2660
|
+
const onKeydown = (event) => {
|
|
2661
|
+
if (event.key !== "Tab") return;
|
|
2662
|
+
const items = getFocusable(dom, container);
|
|
2663
|
+
if (items.length === 0) {
|
|
2664
|
+
event.preventDefault();
|
|
2665
|
+
return;
|
|
2666
|
+
}
|
|
2667
|
+
const first = items[0];
|
|
2668
|
+
const last = items[items.length - 1];
|
|
2669
|
+
const active = dom.activeElement();
|
|
2670
|
+
if (active === null || !dom.contains(container, active)) {
|
|
2671
|
+
event.preventDefault();
|
|
2672
|
+
dom.focus(first);
|
|
2673
|
+
} else if (event.shiftKey && active === first) {
|
|
2674
|
+
event.preventDefault();
|
|
2675
|
+
dom.focus(last);
|
|
2676
|
+
} else if (!event.shiftKey && active === last) {
|
|
2677
|
+
event.preventDefault();
|
|
2678
|
+
dom.focus(first);
|
|
2679
|
+
}
|
|
2680
|
+
};
|
|
2681
|
+
dom.addEventListener(container, "keydown", onKeydown);
|
|
2682
|
+
return () => dom.removeEventListener(container, "keydown", onKeydown);
|
|
2683
|
+
}
|
|
2684
|
+
function containFocus(dom, container) {
|
|
2685
|
+
const body = dom.body();
|
|
2686
|
+
if (body === null) return () => {
|
|
2687
|
+
};
|
|
2688
|
+
const onFocusIn = (event) => {
|
|
2689
|
+
const target = event.target;
|
|
2690
|
+
if (target !== null && !dom.contains(container, target)) {
|
|
2691
|
+
focusFirst(dom, container);
|
|
2692
|
+
}
|
|
2693
|
+
};
|
|
2694
|
+
dom.addEventListener(body, "focusin", onFocusIn);
|
|
2695
|
+
return () => dom.removeEventListener(body, "focusin", onFocusIn);
|
|
2696
|
+
}
|
|
2697
|
+
function onEscape(dom, target, handler) {
|
|
2698
|
+
const onKeydown = (event) => {
|
|
2699
|
+
if (event.key === "Escape") handler();
|
|
2700
|
+
};
|
|
2701
|
+
dom.addEventListener(target, "keydown", onKeydown);
|
|
2702
|
+
return () => dom.removeEventListener(target, "keydown", onKeydown);
|
|
2703
|
+
}
|
|
2425
2704
|
|
|
2426
2705
|
// ../renderer/src/render-context.ts
|
|
2427
2706
|
function createRenderContext(dom, graph, container, hydrationDiagnostics, staticHTML) {
|
|
@@ -2580,7 +2859,12 @@ var TAG_MAP = {
|
|
|
2580
2859
|
component: "div",
|
|
2581
2860
|
slot: "div",
|
|
2582
2861
|
fragment: "div",
|
|
2583
|
-
"reactive-list": "ul"
|
|
2862
|
+
"reactive-list": "ul",
|
|
2863
|
+
// A portal renders as a neutral inline anchor <div> at its declaration site;
|
|
2864
|
+
// its children are relocated to a document.body container on the browser
|
|
2865
|
+
// (see the portal branch in mount.ts). On the server (no body) it renders
|
|
2866
|
+
// inline, so the anchor tag is what SSR/hydration positionally match on.
|
|
2867
|
+
portal: "div"
|
|
2584
2868
|
};
|
|
2585
2869
|
function resolveTag(type) {
|
|
2586
2870
|
return TAG_MAP[type] ?? "div";
|
|
@@ -2992,6 +3276,28 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
2992
3276
|
wireReactiveList(ctx, graphNode, instance2, el2);
|
|
2993
3277
|
return instance2;
|
|
2994
3278
|
}
|
|
3279
|
+
if (graphNode.type === "portal") {
|
|
3280
|
+
const anchor = dom.createElement(resolveTag("portal"));
|
|
3281
|
+
dom.setAttribute(anchor, "data-streetui-portal", "");
|
|
3282
|
+
applyNodeProps(ctx, graphNode, anchor);
|
|
3283
|
+
const instance2 = new NodeInstance(graphNode, anchor);
|
|
3284
|
+
ctx.instances.set(graphNode.id, instance2);
|
|
3285
|
+
const body = dom.body();
|
|
3286
|
+
let target = anchor;
|
|
3287
|
+
if (body !== null) {
|
|
3288
|
+
const portalContainer = dom.createElement("div");
|
|
3289
|
+
dom.setAttribute(portalContainer, "data-streetui-portal-container", "");
|
|
3290
|
+
dom.appendChild(body, portalContainer);
|
|
3291
|
+
instance2.trackCleanup(() => dom.removeChild(body, portalContainer));
|
|
3292
|
+
target = portalContainer;
|
|
3293
|
+
}
|
|
3294
|
+
for (const child of graphNode.children) {
|
|
3295
|
+
instance2.addChild(mountNode(ctx, child, target));
|
|
3296
|
+
}
|
|
3297
|
+
dom.appendChild(parentDom, anchor);
|
|
3298
|
+
wireOverlayBehavior(ctx, graphNode, instance2, target);
|
|
3299
|
+
return instance2;
|
|
3300
|
+
}
|
|
2995
3301
|
const tag = resolveTag(graphNode.type);
|
|
2996
3302
|
const el = dom.createElement(tag);
|
|
2997
3303
|
applyNodeProps(ctx, graphNode, el);
|
|
@@ -3011,6 +3317,7 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
3011
3317
|
instance.addChild(childInstance);
|
|
3012
3318
|
}
|
|
3013
3319
|
dom.appendChild(parentDom, el);
|
|
3320
|
+
if (graphNode.type === "component") wireComponentBehavior(ctx, graphNode, instance);
|
|
3014
3321
|
return instance;
|
|
3015
3322
|
}
|
|
3016
3323
|
function textUpdate(dom, el, textNode) {
|
|
@@ -3141,6 +3448,49 @@ function forgetInstance(ctx, instance) {
|
|
|
3141
3448
|
ctx.instances.delete(instance.graphNode.id);
|
|
3142
3449
|
for (const child of instance.children) forgetInstance(ctx, child);
|
|
3143
3450
|
}
|
|
3451
|
+
function wireOverlayBehavior(ctx, graphNode, instance, target) {
|
|
3452
|
+
const { dom, graph } = ctx;
|
|
3453
|
+
if (dom.body() === null) return;
|
|
3454
|
+
const descFn = graph.getHandler(`__overlay__${graphNode.id}`);
|
|
3455
|
+
if (descFn === void 0) return;
|
|
3456
|
+
const desc = descFn();
|
|
3457
|
+
const openSig = desc.open;
|
|
3458
|
+
if (openSig === void 0 || typeof openSig.subscribe !== "function") return;
|
|
3459
|
+
let active = [];
|
|
3460
|
+
let saved = null;
|
|
3461
|
+
const teardown = () => {
|
|
3462
|
+
for (const fn of active) fn();
|
|
3463
|
+
active = [];
|
|
3464
|
+
};
|
|
3465
|
+
const onOpenChange = (isOpen) => {
|
|
3466
|
+
if (isOpen) {
|
|
3467
|
+
if (desc.restoreFocus) saved = saveFocus(dom);
|
|
3468
|
+
if (desc.takesFocus) focusInitial(dom, target, desc.initialFocusId);
|
|
3469
|
+
if (desc.modal) {
|
|
3470
|
+
active.push(trapFocus(dom, target));
|
|
3471
|
+
active.push(containFocus(dom, target));
|
|
3472
|
+
}
|
|
3473
|
+
if (desc.closeOnEscape && desc.onClose !== void 0) {
|
|
3474
|
+
active.push(onEscape(dom, target, desc.onClose));
|
|
3475
|
+
}
|
|
3476
|
+
} else {
|
|
3477
|
+
teardown();
|
|
3478
|
+
if (desc.restoreFocus && saved !== null) {
|
|
3479
|
+
restoreFocus(dom, saved);
|
|
3480
|
+
saved = null;
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3483
|
+
};
|
|
3484
|
+
const unsub = openSig.subscribe(onOpenChange);
|
|
3485
|
+
instance.trackCleanup(unsub);
|
|
3486
|
+
instance.trackCleanup(teardown);
|
|
3487
|
+
if (openSig.peek() === true) onOpenChange(true);
|
|
3488
|
+
}
|
|
3489
|
+
function wireComponentBehavior(ctx, graphNode, instance) {
|
|
3490
|
+
const fn = ctx.graph.getHandler(`__component__${graphNode.id}`);
|
|
3491
|
+
if (fn === void 0) return;
|
|
3492
|
+
for (const cleanup of fn()) instance.trackCleanup(cleanup);
|
|
3493
|
+
}
|
|
3144
3494
|
|
|
3145
3495
|
// ../renderer/src/hydration-diagnostics.ts
|
|
3146
3496
|
function formatHydrationDiagnostic(d) {
|
|
@@ -3243,6 +3593,25 @@ function hydrateNode(ctx, graphNode, domNode, path) {
|
|
|
3243
3593
|
wireReactiveList(ctx, graphNode, instance, domNode);
|
|
3244
3594
|
return instance;
|
|
3245
3595
|
}
|
|
3596
|
+
case "portal": {
|
|
3597
|
+
const instance = new NodeInstance(graphNode, domNode);
|
|
3598
|
+
ctx.instances.set(graphNode.id, instance);
|
|
3599
|
+
const body = dom.body();
|
|
3600
|
+
let target = domNode;
|
|
3601
|
+
if (body !== null) {
|
|
3602
|
+
const portalContainer = dom.createElement("div");
|
|
3603
|
+
dom.setAttribute(portalContainer, "data-streetui-portal-container", "");
|
|
3604
|
+
for (const child of dom.childNodes(domNode)) {
|
|
3605
|
+
dom.appendChild(portalContainer, child);
|
|
3606
|
+
}
|
|
3607
|
+
dom.appendChild(body, portalContainer);
|
|
3608
|
+
instance.trackCleanup(() => dom.removeChild(body, portalContainer));
|
|
3609
|
+
target = portalContainer;
|
|
3610
|
+
}
|
|
3611
|
+
hydrateChildren(ctx, graphNode, instance, target, path);
|
|
3612
|
+
wireOverlayBehavior(ctx, graphNode, instance, target);
|
|
3613
|
+
return instance;
|
|
3614
|
+
}
|
|
3246
3615
|
default: {
|
|
3247
3616
|
const instance = new NodeInstance(graphNode, domNode);
|
|
3248
3617
|
ctx.instances.set(graphNode.id, instance);
|
|
@@ -3253,6 +3622,7 @@ function hydrateNode(ctx, graphNode, domNode, path) {
|
|
|
3253
3622
|
return instance;
|
|
3254
3623
|
}
|
|
3255
3624
|
hydrateChildren(ctx, graphNode, instance, domNode, path);
|
|
3625
|
+
if (graphNode.type === "component") wireComponentBehavior(ctx, graphNode, instance);
|
|
3256
3626
|
return instance;
|
|
3257
3627
|
}
|
|
3258
3628
|
}
|
|
@@ -3486,7 +3856,9 @@ function analyzeGraph(graph) {
|
|
|
3486
3856
|
const hasEvents = node.events.length > 0;
|
|
3487
3857
|
const isList = node.type === "reactive-list";
|
|
3488
3858
|
const isConditional = node.type === "conditional";
|
|
3489
|
-
const
|
|
3859
|
+
const isPortal = node.type === "portal";
|
|
3860
|
+
const isComponent = node.type === "component";
|
|
3861
|
+
const isStatic = node.stateRefs.length === 0 && !hasEvents && !isList && !isConditional && !isPortal && !isComponent;
|
|
3490
3862
|
const isStaticSubtree = isStatic && allChildrenStatic;
|
|
3491
3863
|
nodes.set(node.id, {
|
|
3492
3864
|
isStatic,
|
|
@@ -4193,6 +4565,21 @@ function nodeTypeStats(graph) {
|
|
|
4193
4565
|
});
|
|
4194
4566
|
return counts;
|
|
4195
4567
|
}
|
|
4568
|
+
function inspectComponents(graph) {
|
|
4569
|
+
const out = [];
|
|
4570
|
+
graph.walk((node, depth) => {
|
|
4571
|
+
if (node.type !== "component") return;
|
|
4572
|
+
const name = node.props["data-streetui-component"];
|
|
4573
|
+
out.push({
|
|
4574
|
+
id: node.id,
|
|
4575
|
+
key: node.key,
|
|
4576
|
+
name: typeof name === "string" ? name : "Component",
|
|
4577
|
+
depth,
|
|
4578
|
+
childCount: node.children.length
|
|
4579
|
+
});
|
|
4580
|
+
});
|
|
4581
|
+
return out;
|
|
4582
|
+
}
|
|
4196
4583
|
|
|
4197
4584
|
// ../devtools/src/application.ts
|
|
4198
4585
|
function collectPerf(node, distinctSignals, acc) {
|
|
@@ -4577,8 +4964,10 @@ function defineConfig(config) {
|
|
|
4577
4964
|
buttonUpdate,
|
|
4578
4965
|
compile,
|
|
4579
4966
|
compileGraph,
|
|
4967
|
+
component,
|
|
4580
4968
|
consoleDiagnosticSink,
|
|
4581
4969
|
consoleHydrationDiagnosticSink,
|
|
4970
|
+
containFocus,
|
|
4582
4971
|
createApplication,
|
|
4583
4972
|
createBrowserHistory,
|
|
4584
4973
|
createContext,
|
|
@@ -4605,17 +4994,20 @@ function defineConfig(config) {
|
|
|
4605
4994
|
flushSync,
|
|
4606
4995
|
focusById,
|
|
4607
4996
|
focusFirst,
|
|
4997
|
+
focusInitial,
|
|
4608
4998
|
formatDiagnostic,
|
|
4609
4999
|
formatDiagnosticContext,
|
|
4610
5000
|
formatHydrationDiagnostic,
|
|
4611
5001
|
frameworkError,
|
|
4612
5002
|
generateApplicationId,
|
|
4613
5003
|
generateNodeId,
|
|
5004
|
+
getFocusable,
|
|
4614
5005
|
globalEventBus,
|
|
4615
5006
|
headingUpdate,
|
|
4616
5007
|
hydrateGraph,
|
|
4617
5008
|
inputUpdate,
|
|
4618
5009
|
inspectApplication,
|
|
5010
|
+
inspectComponents,
|
|
4619
5011
|
inspectContext,
|
|
4620
5012
|
inspectForm,
|
|
4621
5013
|
inspectGraph,
|
|
@@ -4625,6 +5017,7 @@ function defineConfig(config) {
|
|
|
4625
5017
|
inspectSignal,
|
|
4626
5018
|
interpolate,
|
|
4627
5019
|
isBatching,
|
|
5020
|
+
isComponentDefinition,
|
|
4628
5021
|
matchPattern,
|
|
4629
5022
|
matchRoutes,
|
|
4630
5023
|
maxLength,
|
|
@@ -4637,6 +5030,7 @@ function defineConfig(config) {
|
|
|
4637
5030
|
nodeTypeStats,
|
|
4638
5031
|
normalizePath,
|
|
4639
5032
|
observerCount,
|
|
5033
|
+
onEscape,
|
|
4640
5034
|
patchNode,
|
|
4641
5035
|
patchProp,
|
|
4642
5036
|
pattern,
|
|
@@ -4653,8 +5047,10 @@ function defineConfig(config) {
|
|
|
4653
5047
|
resetIdCounter,
|
|
4654
5048
|
resolveTag,
|
|
4655
5049
|
resource,
|
|
5050
|
+
restoreFocus,
|
|
4656
5051
|
routerOutlet,
|
|
4657
5052
|
runValidators,
|
|
5053
|
+
saveFocus,
|
|
4658
5054
|
scheduleImmediate,
|
|
4659
5055
|
scheduleUpdate,
|
|
4660
5056
|
scheduler,
|
|
@@ -4669,8 +5065,11 @@ function defineConfig(config) {
|
|
|
4669
5065
|
textUpdate,
|
|
4670
5066
|
toIdToken,
|
|
4671
5067
|
transformGraph,
|
|
5068
|
+
trapFocus,
|
|
4672
5069
|
validateGraph,
|
|
5070
|
+
wireComponentBehavior,
|
|
4673
5071
|
wireEvents,
|
|
5072
|
+
wireOverlayBehavior,
|
|
4674
5073
|
wireReactiveList,
|
|
4675
5074
|
wireSignalBindings
|
|
4676
5075
|
});
|