streetui 1.6.1 → 1.7.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.
@@ -1,6 +1,6 @@
1
- import { S as StreetApp } from './compile-CEom4hkp.cjs';
2
- export { V as VERSION } from './compile-CEom4hkp.cjs';
3
- import { R as RenderHandle, H as HydrationDiagnostic } from './hydration-diagnostics-D286jpZZ.cjs';
1
+ import { S as StreetApp } from './compile-Cte0nf06.cjs';
2
+ export { V as VERSION } from './compile-Cte0nf06.cjs';
3
+ import { R as RenderHandle, H as HydrationDiagnostic } from './hydration-diagnostics-zK-fihvd.cjs';
4
4
  export * from 'streetui/diagnostics';
5
5
 
6
6
  /**
package/dist/testing.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { S as StreetApp } from './compile-CEom4hkp.js';
2
- export { V as VERSION } from './compile-CEom4hkp.js';
3
- import { R as RenderHandle, H as HydrationDiagnostic } from './hydration-diagnostics-YrG-OJJe.js';
1
+ import { S as StreetApp } from './compile-Cte0nf06.js';
2
+ export { V as VERSION } from './compile-Cte0nf06.js';
3
+ import { R as RenderHandle, H as HydrationDiagnostic } from './hydration-diagnostics-Ddgs3Puu.js';
4
4
  export * from 'streetui/diagnostics';
5
5
 
6
6
  /**
package/dist/testing.js CHANGED
@@ -253,6 +253,18 @@ var BrowserDOMAdapter = class {
253
253
  focus(element) {
254
254
  element.focus?.();
255
255
  }
256
+ body() {
257
+ return document.body ?? null;
258
+ }
259
+ activeElement() {
260
+ return document.activeElement ?? null;
261
+ }
262
+ contains(ancestor, node) {
263
+ return ancestor.contains(node);
264
+ }
265
+ matches(element, selector) {
266
+ return typeof element.matches === "function" && element.matches(selector);
267
+ }
256
268
  isElement(node) {
257
269
  return node.nodeType === Node.ELEMENT_NODE;
258
270
  }
@@ -619,6 +631,18 @@ var ServerDOMAdapter = class {
619
631
  }
620
632
  focus() {
621
633
  }
634
+ body() {
635
+ return null;
636
+ }
637
+ activeElement() {
638
+ return null;
639
+ }
640
+ contains(_ancestor, _node) {
641
+ return false;
642
+ }
643
+ matches(_element, _selector) {
644
+ return false;
645
+ }
622
646
  isElement(node) {
623
647
  return asServer(node).kind === "element";
624
648
  }
@@ -669,6 +693,81 @@ var ServerDOMAdapter = class {
669
693
  }
670
694
  };
671
695
 
696
+ // ../dom/src/focus.ts
697
+ var FOCUSABLE_SELECTOR = 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])';
698
+ function focusById(dom, root, id) {
699
+ const el = dom.querySelector(root, `[id="${id}"]`);
700
+ if (el === null) return false;
701
+ dom.focus(el);
702
+ return true;
703
+ }
704
+ function focusFirst(dom, container, selector = FOCUSABLE_SELECTOR) {
705
+ const el = dom.querySelector(container, selector);
706
+ if (el === null) return false;
707
+ dom.focus(el);
708
+ return true;
709
+ }
710
+ function getFocusable(dom, container, selector = FOCUSABLE_SELECTOR) {
711
+ return Array.from(dom.querySelectorAll(container, selector)).filter(
712
+ (el) => dom.matches(el, selector)
713
+ );
714
+ }
715
+ function saveFocus(dom) {
716
+ return dom.activeElement();
717
+ }
718
+ function restoreFocus(dom, saved) {
719
+ if (saved !== null) dom.focus(saved);
720
+ }
721
+ function focusInitial(dom, container, initialFocusId) {
722
+ if (initialFocusId !== void 0 && focusById(dom, container, initialFocusId)) return;
723
+ focusFirst(dom, container);
724
+ }
725
+ function trapFocus(dom, container) {
726
+ const onKeydown = (event) => {
727
+ if (event.key !== "Tab") return;
728
+ const items = getFocusable(dom, container);
729
+ if (items.length === 0) {
730
+ event.preventDefault();
731
+ return;
732
+ }
733
+ const first = items[0];
734
+ const last = items[items.length - 1];
735
+ const active = dom.activeElement();
736
+ if (active === null || !dom.contains(container, active)) {
737
+ event.preventDefault();
738
+ dom.focus(first);
739
+ } else if (event.shiftKey && active === first) {
740
+ event.preventDefault();
741
+ dom.focus(last);
742
+ } else if (!event.shiftKey && active === last) {
743
+ event.preventDefault();
744
+ dom.focus(first);
745
+ }
746
+ };
747
+ dom.addEventListener(container, "keydown", onKeydown);
748
+ return () => dom.removeEventListener(container, "keydown", onKeydown);
749
+ }
750
+ function containFocus(dom, container) {
751
+ const body = dom.body();
752
+ if (body === null) return () => {
753
+ };
754
+ const onFocusIn = (event) => {
755
+ const target = event.target;
756
+ if (target !== null && !dom.contains(container, target)) {
757
+ focusFirst(dom, container);
758
+ }
759
+ };
760
+ dom.addEventListener(body, "focusin", onFocusIn);
761
+ return () => dom.removeEventListener(body, "focusin", onFocusIn);
762
+ }
763
+ function onEscape(dom, target, handler) {
764
+ const onKeydown = (event) => {
765
+ if (event.key === "Escape") handler();
766
+ };
767
+ dom.addEventListener(target, "keydown", onKeydown);
768
+ return () => dom.removeEventListener(target, "keydown", onKeydown);
769
+ }
770
+
672
771
  // ../renderer/src/render-context.ts
673
772
  function createRenderContext(dom, graph, container, hydrationDiagnostics, staticHTML) {
674
773
  return {
@@ -826,7 +925,12 @@ var TAG_MAP = {
826
925
  component: "div",
827
926
  slot: "div",
828
927
  fragment: "div",
829
- "reactive-list": "ul"
928
+ "reactive-list": "ul",
929
+ // A portal renders as a neutral inline anchor <div> at its declaration site;
930
+ // its children are relocated to a document.body container on the browser
931
+ // (see the portal branch in mount.ts). On the server (no body) it renders
932
+ // inline, so the anchor tag is what SSR/hydration positionally match on.
933
+ portal: "div"
830
934
  };
831
935
  function resolveTag(type) {
832
936
  return TAG_MAP[type] ?? "div";
@@ -1238,6 +1342,28 @@ function mountNode(ctx, graphNode, parentDom) {
1238
1342
  wireReactiveList(ctx, graphNode, instance2, el2);
1239
1343
  return instance2;
1240
1344
  }
1345
+ if (graphNode.type === "portal") {
1346
+ const anchor = dom.createElement(resolveTag("portal"));
1347
+ dom.setAttribute(anchor, "data-streetui-portal", "");
1348
+ applyNodeProps(ctx, graphNode, anchor);
1349
+ const instance2 = new NodeInstance(graphNode, anchor);
1350
+ ctx.instances.set(graphNode.id, instance2);
1351
+ const body = dom.body();
1352
+ let target = anchor;
1353
+ if (body !== null) {
1354
+ const portalContainer = dom.createElement("div");
1355
+ dom.setAttribute(portalContainer, "data-streetui-portal-container", "");
1356
+ dom.appendChild(body, portalContainer);
1357
+ instance2.trackCleanup(() => dom.removeChild(body, portalContainer));
1358
+ target = portalContainer;
1359
+ }
1360
+ for (const child of graphNode.children) {
1361
+ instance2.addChild(mountNode(ctx, child, target));
1362
+ }
1363
+ dom.appendChild(parentDom, anchor);
1364
+ wireOverlayBehavior(ctx, graphNode, instance2, target);
1365
+ return instance2;
1366
+ }
1241
1367
  const tag = resolveTag(graphNode.type);
1242
1368
  const el = dom.createElement(tag);
1243
1369
  applyNodeProps(ctx, graphNode, el);
@@ -1387,6 +1513,44 @@ function forgetInstance(ctx, instance) {
1387
1513
  ctx.instances.delete(instance.graphNode.id);
1388
1514
  for (const child of instance.children) forgetInstance(ctx, child);
1389
1515
  }
1516
+ function wireOverlayBehavior(ctx, graphNode, instance, target) {
1517
+ const { dom, graph } = ctx;
1518
+ if (dom.body() === null) return;
1519
+ const descFn = graph.getHandler(`__overlay__${graphNode.id}`);
1520
+ if (descFn === void 0) return;
1521
+ const desc = descFn();
1522
+ const openSig = desc.open;
1523
+ if (openSig === void 0 || typeof openSig.subscribe !== "function") return;
1524
+ let active = [];
1525
+ let saved = null;
1526
+ const teardown = () => {
1527
+ for (const fn of active) fn();
1528
+ active = [];
1529
+ };
1530
+ const onOpenChange = (isOpen) => {
1531
+ if (isOpen) {
1532
+ if (desc.restoreFocus) saved = saveFocus(dom);
1533
+ if (desc.takesFocus) focusInitial(dom, target, desc.initialFocusId);
1534
+ if (desc.modal) {
1535
+ active.push(trapFocus(dom, target));
1536
+ active.push(containFocus(dom, target));
1537
+ }
1538
+ if (desc.closeOnEscape && desc.onClose !== void 0) {
1539
+ active.push(onEscape(dom, target, desc.onClose));
1540
+ }
1541
+ } else {
1542
+ teardown();
1543
+ if (desc.restoreFocus && saved !== null) {
1544
+ restoreFocus(dom, saved);
1545
+ saved = null;
1546
+ }
1547
+ }
1548
+ };
1549
+ const unsub = openSig.subscribe(onOpenChange);
1550
+ instance.trackCleanup(unsub);
1551
+ instance.trackCleanup(teardown);
1552
+ if (openSig.peek() === true) onOpenChange(true);
1553
+ }
1390
1554
 
1391
1555
  // ../renderer/src/hydration-diagnostics.ts
1392
1556
  function formatHydrationDiagnostic(d) {
@@ -1482,6 +1646,25 @@ function hydrateNode(ctx, graphNode, domNode, path) {
1482
1646
  wireReactiveList(ctx, graphNode, instance, domNode);
1483
1647
  return instance;
1484
1648
  }
1649
+ case "portal": {
1650
+ const instance = new NodeInstance(graphNode, domNode);
1651
+ ctx.instances.set(graphNode.id, instance);
1652
+ const body = dom.body();
1653
+ let target = domNode;
1654
+ if (body !== null) {
1655
+ const portalContainer = dom.createElement("div");
1656
+ dom.setAttribute(portalContainer, "data-streetui-portal-container", "");
1657
+ for (const child of dom.childNodes(domNode)) {
1658
+ dom.appendChild(portalContainer, child);
1659
+ }
1660
+ dom.appendChild(body, portalContainer);
1661
+ instance.trackCleanup(() => dom.removeChild(body, portalContainer));
1662
+ target = portalContainer;
1663
+ }
1664
+ hydrateChildren(ctx, graphNode, instance, target, path);
1665
+ wireOverlayBehavior(ctx, graphNode, instance, target);
1666
+ return instance;
1667
+ }
1485
1668
  default: {
1486
1669
  const instance = new NodeInstance(graphNode, domNode);
1487
1670
  ctx.instances.set(graphNode.id, instance);
@@ -1689,7 +1872,8 @@ function analyzeGraph(graph) {
1689
1872
  const hasEvents = node.events.length > 0;
1690
1873
  const isList = node.type === "reactive-list";
1691
1874
  const isConditional = node.type === "conditional";
1692
- const isStatic = node.stateRefs.length === 0 && !hasEvents && !isList && !isConditional;
1875
+ const isPortal = node.type === "portal";
1876
+ const isStatic = node.stateRefs.length === 0 && !hasEvents && !isList && !isConditional && !isPortal;
1693
1877
  const isStaticSubtree = isStatic && allChildrenStatic;
1694
1878
  nodes.set(node.id, {
1695
1879
  isStatic,
@@ -2115,7 +2299,7 @@ function renderServerThenHydrate(build, options = {}) {
2115
2299
  }
2116
2300
 
2117
2301
  // src/version.ts
2118
- var VERSION = "1.6.0";
2302
+ var VERSION = "1.7.0";
2119
2303
  export {
2120
2304
  VERSION,
2121
2305
  analyzeGraph,