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/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var VERSION = "1.6.0";
2
+ var VERSION = "1.8.0";
3
3
 
4
4
  // ../state/src/signal.ts
5
5
  var _activeConsumer = null;
@@ -802,6 +802,8 @@ var ApplicationGraph = class {
802
802
  }
803
803
  this.handlers.delete(`__listbuild__${node.id}`);
804
804
  this.handlers.delete(`__listplan__${node.id}`);
805
+ this.handlers.delete(`__overlay__${node.id}`);
806
+ this.handlers.delete(`__component__${node.id}`);
805
807
  }
806
808
  // ── Handler registry ──────────────────────────────────────────────────────
807
809
  registerHandler(key, fn) {
@@ -916,6 +918,7 @@ function applyA11yProps(props, options) {
916
918
  if (options.ariaCurrent !== void 0) props["aria-current"] = String(options.ariaCurrent);
917
919
  if (options.ariaInvalid !== void 0) props["aria-invalid"] = String(options.ariaInvalid);
918
920
  if (options.ariaRequired !== void 0) props["aria-required"] = String(options.ariaRequired);
921
+ if (options.ariaModal !== void 0) props["aria-modal"] = String(options.ariaModal);
919
922
  }
920
923
  function containerProps(options) {
921
924
  const props = {};
@@ -947,6 +950,49 @@ function reactiveListItemSignature(item) {
947
950
  function reactiveListItemKey(item, index) {
948
951
  return itemIdentity(item, index);
949
952
  }
953
+ var OVERLAY_KINDS = {
954
+ dialog: {
955
+ role: "dialog",
956
+ modal: true,
957
+ takesFocus: true,
958
+ ariaModal: true,
959
+ defaultCloseOnEscape: true,
960
+ defaultRestoreFocus: true
961
+ },
962
+ popover: {
963
+ role: "dialog",
964
+ modal: false,
965
+ takesFocus: true,
966
+ ariaModal: false,
967
+ defaultCloseOnEscape: true,
968
+ defaultRestoreFocus: true
969
+ },
970
+ tooltip: {
971
+ role: "tooltip",
972
+ modal: false,
973
+ takesFocus: false,
974
+ ariaModal: false,
975
+ defaultCloseOnEscape: false,
976
+ defaultRestoreFocus: false
977
+ },
978
+ dropdown: {
979
+ role: "menu",
980
+ modal: false,
981
+ takesFocus: true,
982
+ ariaModal: false,
983
+ defaultCloseOnEscape: true,
984
+ defaultRestoreFocus: true
985
+ },
986
+ toast: {
987
+ role: "status",
988
+ modal: false,
989
+ takesFocus: false,
990
+ ariaModal: false,
991
+ ariaLive: "polite",
992
+ defaultCloseOnEscape: false,
993
+ defaultRestoreFocus: false
994
+ }
995
+ };
950
996
  var ContentBuilderBase = class {
951
997
  constructor(_node, _graph) {
952
998
  this._node = _node;
@@ -1219,6 +1265,131 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
1219
1265
  );
1220
1266
  }, { id });
1221
1267
  }
1268
+ // ── Portals & overlays ──────────────────────────────────────────────────────
1269
+ portal(key, builder, options = {}) {
1270
+ const node = this._graph.createNode("portal", {
1271
+ key,
1272
+ parent: this._node,
1273
+ props: containerProps(options)
1274
+ });
1275
+ builder(new ContainerBuilderImpl(node, this._graph));
1276
+ }
1277
+ /**
1278
+ * Shared assembly for every overlay kind: a `portal` node whose single child
1279
+ * is a `when(open, panel)` conditional. The panel container carries the
1280
+ * kind's ARIA semantics; `builder` fills it. An `__overlay__<portalId>`
1281
+ * descriptor is registered so the renderer wires focus/keyboard behavior to
1282
+ * the same `open` signal that drives the panel. Reuses existing primitives
1283
+ * (portal + when + container) — no new render path.
1284
+ */
1285
+ _overlay(kind, key, options, builder) {
1286
+ const graph = this._graph;
1287
+ const portalNode = graph.createNode("portal", {
1288
+ key,
1289
+ parent: this._node,
1290
+ props: { key }
1291
+ });
1292
+ const openBindable = options.open;
1293
+ const openSignal = isSignal(openBindable) ? openBindable : signal(openBindable);
1294
+ const panelOptions = {
1295
+ role: options.role ?? kind.role,
1296
+ ...kind.ariaModal ? { ariaModal: true } : {},
1297
+ ...kind.ariaLive !== void 0 ? { ariaLive: kind.ariaLive } : {},
1298
+ ...options.class !== void 0 ? { class: options.class } : {},
1299
+ ...options.ariaLabel !== void 0 ? { ariaLabel: options.ariaLabel } : {},
1300
+ ...options.ariaLabelledBy !== void 0 ? { ariaLabelledBy: options.ariaLabelledBy } : {},
1301
+ ...options.ariaDescribedBy !== void 0 ? { ariaDescribedBy: options.ariaDescribedBy } : {}
1302
+ };
1303
+ const portalBuilder = new ContainerBuilderImpl(portalNode, graph);
1304
+ portalBuilder.when(openSignal, (panelHost) => {
1305
+ panelHost.container(`${key}__panel`, builder, panelOptions);
1306
+ });
1307
+ const descriptor = {
1308
+ open: openSignal,
1309
+ modal: kind.modal,
1310
+ takesFocus: kind.takesFocus,
1311
+ closeOnEscape: options.closeOnEscape ?? kind.defaultCloseOnEscape,
1312
+ restoreFocus: options.restoreFocus ?? kind.defaultRestoreFocus,
1313
+ ...options.initialFocusId !== void 0 ? { initialFocusId: options.initialFocusId } : {},
1314
+ ...options.onClose !== void 0 ? { onClose: options.onClose } : {}
1315
+ };
1316
+ graph.registerHandler(
1317
+ `__overlay__${portalNode.id}`,
1318
+ () => descriptor
1319
+ );
1320
+ }
1321
+ dialog(key, options, builder) {
1322
+ this._overlay(OVERLAY_KINDS.dialog, key, options, builder);
1323
+ }
1324
+ popover(key, options, builder) {
1325
+ this._overlay(OVERLAY_KINDS.popover, key, options, builder);
1326
+ }
1327
+ tooltip(key, options, builder) {
1328
+ this._overlay(OVERLAY_KINDS.tooltip, key, options, builder);
1329
+ }
1330
+ dropdown(key, options, builder) {
1331
+ this._overlay(OVERLAY_KINDS.dropdown, key, options, builder);
1332
+ }
1333
+ toast(key, options, builder) {
1334
+ this._overlay(OVERLAY_KINDS.toast, key, options, builder);
1335
+ }
1336
+ // ── Components ────────────────────────────────────────────────────────────
1337
+ /**
1338
+ * Instantiate a reusable component (§3–§9). Creates a `'component'` node
1339
+ * (rendered as a `<div>` wrapper — preserves the one-node/one-element
1340
+ * positional-hydration invariant), then runs `def.setup(props, ctx)`
1341
+ * synchronously to obtain the render function and fills the component's own
1342
+ * container scope with it — structurally identical to `container`/
1343
+ * `errorBoundary`. Cleanups the setup registers via `ctx.effect`/
1344
+ * `ctx.onCleanup` are collected into a closure and exposed to the renderer
1345
+ * through a `__component__<id>` handler (mirroring `__overlay__`); the mount/
1346
+ * hydrate paths read it and route each into `NodeInstance.trackCleanup`, so
1347
+ * teardown runs (children-first) when the component leaves the graph.
1348
+ *
1349
+ * `setup` runs once per instance here at build time. When this component sits
1350
+ * inside a keyed list / conditional, a rebuild disposes the old instance
1351
+ * (running its cleanups + pruning its `__component__` entry) and re-runs this
1352
+ * method for the new node — so re-invocation is safe and leak-free.
1353
+ */
1354
+ component(key, def, props, children) {
1355
+ const graph = this._graph;
1356
+ const node = graph.createNode("component", {
1357
+ key,
1358
+ parent: this._node,
1359
+ // `data-streetui-component` is a non-underscore prop, so it reaches the
1360
+ // DOM as an attribute and is visible to DevTools (§21) — unlike the
1361
+ // internal `_`-prefixed metadata the renderer hides.
1362
+ props: { key, "data-streetui-component": def.name }
1363
+ });
1364
+ const cleanups = [];
1365
+ const ctx = {
1366
+ key,
1367
+ onCleanup(fn) {
1368
+ cleanups.push(fn);
1369
+ },
1370
+ effect(fn) {
1371
+ cleanups.push(effect(fn));
1372
+ },
1373
+ renderChildren(content) {
1374
+ if (children !== void 0) children(content);
1375
+ }
1376
+ };
1377
+ let render;
1378
+ try {
1379
+ render = def.setup(props, ctx);
1380
+ render(new ContainerBuilderImpl(node, graph));
1381
+ } catch (err) {
1382
+ queueMicrotask(() => {
1383
+ throw err;
1384
+ });
1385
+ }
1386
+ if (cleanups.length > 0) {
1387
+ graph.registerHandler(
1388
+ `__component__${node.id}`,
1389
+ () => cleanups
1390
+ );
1391
+ }
1392
+ }
1222
1393
  };
1223
1394
  var SectionBuilderImpl = class extends ContainerBuilderBase {
1224
1395
  };
@@ -1252,6 +1423,18 @@ var AppBuilder = class {
1252
1423
  }
1253
1424
  };
1254
1425
 
1426
+ // ../dsl/src/component.ts
1427
+ function component(setup, options = {}) {
1428
+ return {
1429
+ __streetui_component: true,
1430
+ name: options.name ?? setup.name ?? "Component",
1431
+ setup
1432
+ };
1433
+ }
1434
+ function isComponentDefinition(value) {
1435
+ return value !== null && typeof value === "object" && value.__streetui_component === true;
1436
+ }
1437
+
1255
1438
  // ../dsl/src/dsl.ts
1256
1439
  var StreetApp = class {
1257
1440
  _graph;
@@ -1819,6 +2002,18 @@ var BrowserDOMAdapter = class {
1819
2002
  focus(element) {
1820
2003
  element.focus?.();
1821
2004
  }
2005
+ body() {
2006
+ return document.body ?? null;
2007
+ }
2008
+ activeElement() {
2009
+ return document.activeElement ?? null;
2010
+ }
2011
+ contains(ancestor, node) {
2012
+ return ancestor.contains(node);
2013
+ }
2014
+ matches(element, selector) {
2015
+ return typeof element.matches === "function" && element.matches(selector);
2016
+ }
1822
2017
  isElement(node) {
1823
2018
  return node.nodeType === Node.ELEMENT_NODE;
1824
2019
  }
@@ -2186,6 +2381,18 @@ var ServerDOMAdapter = class {
2186
2381
  }
2187
2382
  focus() {
2188
2383
  }
2384
+ body() {
2385
+ return null;
2386
+ }
2387
+ activeElement() {
2388
+ return null;
2389
+ }
2390
+ contains(_ancestor, _node) {
2391
+ return false;
2392
+ }
2393
+ matches(_element, _selector) {
2394
+ return false;
2395
+ }
2189
2396
  isElement(node) {
2190
2397
  return asServer(node).kind === "element";
2191
2398
  }
@@ -2251,6 +2458,66 @@ function focusFirst(dom, container, selector = FOCUSABLE_SELECTOR) {
2251
2458
  dom.focus(el);
2252
2459
  return true;
2253
2460
  }
2461
+ function getFocusable(dom, container, selector = FOCUSABLE_SELECTOR) {
2462
+ return Array.from(dom.querySelectorAll(container, selector)).filter(
2463
+ (el) => dom.matches(el, selector)
2464
+ );
2465
+ }
2466
+ function saveFocus(dom) {
2467
+ return dom.activeElement();
2468
+ }
2469
+ function restoreFocus(dom, saved) {
2470
+ if (saved !== null) dom.focus(saved);
2471
+ }
2472
+ function focusInitial(dom, container, initialFocusId) {
2473
+ if (initialFocusId !== void 0 && focusById(dom, container, initialFocusId)) return;
2474
+ focusFirst(dom, container);
2475
+ }
2476
+ function trapFocus(dom, container) {
2477
+ const onKeydown = (event) => {
2478
+ if (event.key !== "Tab") return;
2479
+ const items = getFocusable(dom, container);
2480
+ if (items.length === 0) {
2481
+ event.preventDefault();
2482
+ return;
2483
+ }
2484
+ const first = items[0];
2485
+ const last = items[items.length - 1];
2486
+ const active = dom.activeElement();
2487
+ if (active === null || !dom.contains(container, active)) {
2488
+ event.preventDefault();
2489
+ dom.focus(first);
2490
+ } else if (event.shiftKey && active === first) {
2491
+ event.preventDefault();
2492
+ dom.focus(last);
2493
+ } else if (!event.shiftKey && active === last) {
2494
+ event.preventDefault();
2495
+ dom.focus(first);
2496
+ }
2497
+ };
2498
+ dom.addEventListener(container, "keydown", onKeydown);
2499
+ return () => dom.removeEventListener(container, "keydown", onKeydown);
2500
+ }
2501
+ function containFocus(dom, container) {
2502
+ const body = dom.body();
2503
+ if (body === null) return () => {
2504
+ };
2505
+ const onFocusIn = (event) => {
2506
+ const target = event.target;
2507
+ if (target !== null && !dom.contains(container, target)) {
2508
+ focusFirst(dom, container);
2509
+ }
2510
+ };
2511
+ dom.addEventListener(body, "focusin", onFocusIn);
2512
+ return () => dom.removeEventListener(body, "focusin", onFocusIn);
2513
+ }
2514
+ function onEscape(dom, target, handler) {
2515
+ const onKeydown = (event) => {
2516
+ if (event.key === "Escape") handler();
2517
+ };
2518
+ dom.addEventListener(target, "keydown", onKeydown);
2519
+ return () => dom.removeEventListener(target, "keydown", onKeydown);
2520
+ }
2254
2521
 
2255
2522
  // ../renderer/src/render-context.ts
2256
2523
  function createRenderContext(dom, graph, container, hydrationDiagnostics, staticHTML) {
@@ -2409,7 +2676,12 @@ var TAG_MAP = {
2409
2676
  component: "div",
2410
2677
  slot: "div",
2411
2678
  fragment: "div",
2412
- "reactive-list": "ul"
2679
+ "reactive-list": "ul",
2680
+ // A portal renders as a neutral inline anchor <div> at its declaration site;
2681
+ // its children are relocated to a document.body container on the browser
2682
+ // (see the portal branch in mount.ts). On the server (no body) it renders
2683
+ // inline, so the anchor tag is what SSR/hydration positionally match on.
2684
+ portal: "div"
2413
2685
  };
2414
2686
  function resolveTag(type) {
2415
2687
  return TAG_MAP[type] ?? "div";
@@ -2821,6 +3093,28 @@ function mountNode(ctx, graphNode, parentDom) {
2821
3093
  wireReactiveList(ctx, graphNode, instance2, el2);
2822
3094
  return instance2;
2823
3095
  }
3096
+ if (graphNode.type === "portal") {
3097
+ const anchor = dom.createElement(resolveTag("portal"));
3098
+ dom.setAttribute(anchor, "data-streetui-portal", "");
3099
+ applyNodeProps(ctx, graphNode, anchor);
3100
+ const instance2 = new NodeInstance(graphNode, anchor);
3101
+ ctx.instances.set(graphNode.id, instance2);
3102
+ const body = dom.body();
3103
+ let target = anchor;
3104
+ if (body !== null) {
3105
+ const portalContainer = dom.createElement("div");
3106
+ dom.setAttribute(portalContainer, "data-streetui-portal-container", "");
3107
+ dom.appendChild(body, portalContainer);
3108
+ instance2.trackCleanup(() => dom.removeChild(body, portalContainer));
3109
+ target = portalContainer;
3110
+ }
3111
+ for (const child of graphNode.children) {
3112
+ instance2.addChild(mountNode(ctx, child, target));
3113
+ }
3114
+ dom.appendChild(parentDom, anchor);
3115
+ wireOverlayBehavior(ctx, graphNode, instance2, target);
3116
+ return instance2;
3117
+ }
2824
3118
  const tag = resolveTag(graphNode.type);
2825
3119
  const el = dom.createElement(tag);
2826
3120
  applyNodeProps(ctx, graphNode, el);
@@ -2840,6 +3134,7 @@ function mountNode(ctx, graphNode, parentDom) {
2840
3134
  instance.addChild(childInstance);
2841
3135
  }
2842
3136
  dom.appendChild(parentDom, el);
3137
+ if (graphNode.type === "component") wireComponentBehavior(ctx, graphNode, instance);
2843
3138
  return instance;
2844
3139
  }
2845
3140
  function textUpdate(dom, el, textNode) {
@@ -2970,6 +3265,49 @@ function forgetInstance(ctx, instance) {
2970
3265
  ctx.instances.delete(instance.graphNode.id);
2971
3266
  for (const child of instance.children) forgetInstance(ctx, child);
2972
3267
  }
3268
+ function wireOverlayBehavior(ctx, graphNode, instance, target) {
3269
+ const { dom, graph } = ctx;
3270
+ if (dom.body() === null) return;
3271
+ const descFn = graph.getHandler(`__overlay__${graphNode.id}`);
3272
+ if (descFn === void 0) return;
3273
+ const desc = descFn();
3274
+ const openSig = desc.open;
3275
+ if (openSig === void 0 || typeof openSig.subscribe !== "function") return;
3276
+ let active = [];
3277
+ let saved = null;
3278
+ const teardown = () => {
3279
+ for (const fn of active) fn();
3280
+ active = [];
3281
+ };
3282
+ const onOpenChange = (isOpen) => {
3283
+ if (isOpen) {
3284
+ if (desc.restoreFocus) saved = saveFocus(dom);
3285
+ if (desc.takesFocus) focusInitial(dom, target, desc.initialFocusId);
3286
+ if (desc.modal) {
3287
+ active.push(trapFocus(dom, target));
3288
+ active.push(containFocus(dom, target));
3289
+ }
3290
+ if (desc.closeOnEscape && desc.onClose !== void 0) {
3291
+ active.push(onEscape(dom, target, desc.onClose));
3292
+ }
3293
+ } else {
3294
+ teardown();
3295
+ if (desc.restoreFocus && saved !== null) {
3296
+ restoreFocus(dom, saved);
3297
+ saved = null;
3298
+ }
3299
+ }
3300
+ };
3301
+ const unsub = openSig.subscribe(onOpenChange);
3302
+ instance.trackCleanup(unsub);
3303
+ instance.trackCleanup(teardown);
3304
+ if (openSig.peek() === true) onOpenChange(true);
3305
+ }
3306
+ function wireComponentBehavior(ctx, graphNode, instance) {
3307
+ const fn = ctx.graph.getHandler(`__component__${graphNode.id}`);
3308
+ if (fn === void 0) return;
3309
+ for (const cleanup of fn()) instance.trackCleanup(cleanup);
3310
+ }
2973
3311
 
2974
3312
  // ../renderer/src/hydration-diagnostics.ts
2975
3313
  function formatHydrationDiagnostic(d) {
@@ -3072,6 +3410,25 @@ function hydrateNode(ctx, graphNode, domNode, path) {
3072
3410
  wireReactiveList(ctx, graphNode, instance, domNode);
3073
3411
  return instance;
3074
3412
  }
3413
+ case "portal": {
3414
+ const instance = new NodeInstance(graphNode, domNode);
3415
+ ctx.instances.set(graphNode.id, instance);
3416
+ const body = dom.body();
3417
+ let target = domNode;
3418
+ if (body !== null) {
3419
+ const portalContainer = dom.createElement("div");
3420
+ dom.setAttribute(portalContainer, "data-streetui-portal-container", "");
3421
+ for (const child of dom.childNodes(domNode)) {
3422
+ dom.appendChild(portalContainer, child);
3423
+ }
3424
+ dom.appendChild(body, portalContainer);
3425
+ instance.trackCleanup(() => dom.removeChild(body, portalContainer));
3426
+ target = portalContainer;
3427
+ }
3428
+ hydrateChildren(ctx, graphNode, instance, target, path);
3429
+ wireOverlayBehavior(ctx, graphNode, instance, target);
3430
+ return instance;
3431
+ }
3075
3432
  default: {
3076
3433
  const instance = new NodeInstance(graphNode, domNode);
3077
3434
  ctx.instances.set(graphNode.id, instance);
@@ -3082,6 +3439,7 @@ function hydrateNode(ctx, graphNode, domNode, path) {
3082
3439
  return instance;
3083
3440
  }
3084
3441
  hydrateChildren(ctx, graphNode, instance, domNode, path);
3442
+ if (graphNode.type === "component") wireComponentBehavior(ctx, graphNode, instance);
3085
3443
  return instance;
3086
3444
  }
3087
3445
  }
@@ -3315,7 +3673,9 @@ function analyzeGraph(graph) {
3315
3673
  const hasEvents = node.events.length > 0;
3316
3674
  const isList = node.type === "reactive-list";
3317
3675
  const isConditional = node.type === "conditional";
3318
- const isStatic = node.stateRefs.length === 0 && !hasEvents && !isList && !isConditional;
3676
+ const isPortal = node.type === "portal";
3677
+ const isComponent = node.type === "component";
3678
+ const isStatic = node.stateRefs.length === 0 && !hasEvents && !isList && !isConditional && !isPortal && !isComponent;
3319
3679
  const isStaticSubtree = isStatic && allChildrenStatic;
3320
3680
  nodes.set(node.id, {
3321
3681
  isStatic,
@@ -4022,6 +4382,21 @@ function nodeTypeStats(graph) {
4022
4382
  });
4023
4383
  return counts;
4024
4384
  }
4385
+ function inspectComponents(graph) {
4386
+ const out = [];
4387
+ graph.walk((node, depth) => {
4388
+ if (node.type !== "component") return;
4389
+ const name = node.props["data-streetui-component"];
4390
+ out.push({
4391
+ id: node.id,
4392
+ key: node.key,
4393
+ name: typeof name === "string" ? name : "Component",
4394
+ depth,
4395
+ childCount: node.children.length
4396
+ });
4397
+ });
4398
+ return out;
4399
+ }
4025
4400
 
4026
4401
  // ../devtools/src/application.ts
4027
4402
  function collectPerf(node, distinctSignals, acc) {
@@ -4405,8 +4780,10 @@ export {
4405
4780
  buttonUpdate,
4406
4781
  compile,
4407
4782
  compileGraph,
4783
+ component,
4408
4784
  consoleDiagnosticSink,
4409
4785
  consoleHydrationDiagnosticSink,
4786
+ containFocus,
4410
4787
  createApplication,
4411
4788
  createBrowserHistory,
4412
4789
  createContext,
@@ -4433,17 +4810,20 @@ export {
4433
4810
  flushSync,
4434
4811
  focusById,
4435
4812
  focusFirst,
4813
+ focusInitial,
4436
4814
  formatDiagnostic,
4437
4815
  formatDiagnosticContext,
4438
4816
  formatHydrationDiagnostic,
4439
4817
  frameworkError,
4440
4818
  generateApplicationId,
4441
4819
  generateNodeId,
4820
+ getFocusable,
4442
4821
  globalEventBus,
4443
4822
  headingUpdate,
4444
4823
  hydrateGraph,
4445
4824
  inputUpdate,
4446
4825
  inspectApplication,
4826
+ inspectComponents,
4447
4827
  inspectContext,
4448
4828
  inspectForm,
4449
4829
  inspectGraph,
@@ -4453,6 +4833,7 @@ export {
4453
4833
  inspectSignal,
4454
4834
  interpolate,
4455
4835
  isBatching,
4836
+ isComponentDefinition,
4456
4837
  matchPattern,
4457
4838
  matchRoutes,
4458
4839
  maxLength,
@@ -4465,6 +4846,7 @@ export {
4465
4846
  nodeTypeStats,
4466
4847
  normalizePath,
4467
4848
  observerCount,
4849
+ onEscape,
4468
4850
  patchNode,
4469
4851
  patchProp,
4470
4852
  pattern,
@@ -4481,8 +4863,10 @@ export {
4481
4863
  resetIdCounter,
4482
4864
  resolveTag,
4483
4865
  resource,
4866
+ restoreFocus,
4484
4867
  routerOutlet,
4485
4868
  runValidators,
4869
+ saveFocus,
4486
4870
  scheduleImmediate,
4487
4871
  scheduleUpdate,
4488
4872
  scheduler,
@@ -4497,8 +4881,11 @@ export {
4497
4881
  textUpdate,
4498
4882
  toIdToken,
4499
4883
  transformGraph,
4884
+ trapFocus,
4500
4885
  validateGraph,
4886
+ wireComponentBehavior,
4501
4887
  wireEvents,
4888
+ wireOverlayBehavior,
4502
4889
  wireReactiveList,
4503
4890
  wireSignalBindings
4504
4891
  };