@almadar/ui 5.15.0 → 5.16.1

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.
@@ -14626,13 +14626,13 @@ var init_MapView = __esm({
14626
14626
  shadowSize: [41, 41]
14627
14627
  });
14628
14628
  L.Marker.prototype.options.icon = defaultIcon;
14629
- const { useEffect: useEffect89, useRef: useRef88, useCallback: useCallback130, useState: useState125 } = React98__namespace.default;
14629
+ const { useEffect: useEffect90, useRef: useRef89, useCallback: useCallback130, useState: useState125 } = React98__namespace.default;
14630
14630
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
14631
14631
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
14632
14632
  function MapUpdater({ centerLat, centerLng, zoom }) {
14633
14633
  const map = useMap();
14634
- const prevRef = useRef88({ centerLat, centerLng, zoom });
14635
- useEffect89(() => {
14634
+ const prevRef = useRef89({ centerLat, centerLng, zoom });
14635
+ useEffect90(() => {
14636
14636
  const prev = prevRef.current;
14637
14637
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
14638
14638
  map.setView([centerLat, centerLng], zoom);
@@ -14643,7 +14643,7 @@ var init_MapView = __esm({
14643
14643
  }
14644
14644
  function MapClickHandler({ onMapClick }) {
14645
14645
  const map = useMap();
14646
- useEffect89(() => {
14646
+ useEffect90(() => {
14647
14647
  if (!onMapClick) return;
14648
14648
  const handler = (e) => {
14649
14649
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -26158,6 +26158,21 @@ var init_DashboardLayout = __esm({
26158
26158
  };
26159
26159
  const [sidebarOpen, setSidebarOpen] = React98.useState(false);
26160
26160
  const [userMenuOpen, setUserMenuOpen] = React98.useState(false);
26161
+ const layoutRef = React98.useRef(null);
26162
+ const [isMobile, setIsMobile] = React98.useState(false);
26163
+ React98.useEffect(() => {
26164
+ const el = layoutRef.current;
26165
+ if (!el || typeof ResizeObserver === "undefined") return;
26166
+ const ro = new ResizeObserver((entries) => {
26167
+ const w = entries[0]?.contentRect.width ?? el.clientWidth;
26168
+ setIsMobile(w < 1024);
26169
+ });
26170
+ ro.observe(el);
26171
+ return () => ro.disconnect();
26172
+ }, []);
26173
+ React98.useEffect(() => {
26174
+ if (!isMobile && sidebarOpen) setSidebarOpen(false);
26175
+ }, [isMobile, sidebarOpen]);
26161
26176
  const location = reactRouterDom.useLocation();
26162
26177
  const ctxPagePath = useCurrentPagePath();
26163
26178
  const activePath = currentPath ?? ctxPagePath ?? location.pathname;
@@ -26170,15 +26185,15 @@ var init_DashboardLayout = __esm({
26170
26185
  const showBottomNav = layoutMode === "bottomnav";
26171
26186
  const isTopNav = layoutMode === "topnav";
26172
26187
  return /* @__PURE__ */ jsxRuntime.jsxs(
26173
- HStack,
26188
+ Box,
26174
26189
  {
26175
- gap: "none",
26176
- className: "@container/dashboard min-h-screen w-full bg-background dark:bg-background items-stretch",
26190
+ ref: layoutRef,
26191
+ className: "@container/dashboard min-h-screen w-full bg-background dark:bg-background flex flex-row items-stretch",
26177
26192
  children: [
26178
- showSidebar && sidebarOpen && /* @__PURE__ */ jsxRuntime.jsx(
26193
+ showSidebar && isMobile && sidebarOpen && /* @__PURE__ */ jsxRuntime.jsx(
26179
26194
  Box,
26180
26195
  {
26181
- className: "fixed inset-0 bg-foreground/50 dark:bg-foreground/70 z-20 @lg/dashboard:hidden",
26196
+ className: "fixed inset-0 bg-foreground/50 dark:bg-foreground/70 z-20",
26182
26197
  onClick: () => setSidebarOpen(false)
26183
26198
  }
26184
26199
  ),
@@ -26188,11 +26203,16 @@ var init_DashboardLayout = __esm({
26188
26203
  as: "aside",
26189
26204
  className: cn(
26190
26205
  "z-30 w-64 flex-shrink-0 bg-card dark:bg-card border-r border-border dark:border-border",
26191
- "fixed inset-y-0 left-0 @lg/dashboard:static @lg/dashboard:translate-x-0 @lg/dashboard:h-auto",
26192
- "transform transition-transform duration-200 ease-in-out",
26193
- "flex flex-col",
26194
- sidebarOpen ? "translate-x-0" : "-translate-x-full"
26206
+ "flex flex-col"
26195
26207
  ),
26208
+ style: isMobile ? {
26209
+ position: "fixed",
26210
+ top: 0,
26211
+ bottom: 0,
26212
+ left: 0,
26213
+ transform: sidebarOpen ? "translateX(0)" : "translateX(-100%)",
26214
+ transition: "transform 200ms ease-in-out"
26215
+ } : { position: "static", transform: "none" },
26196
26216
  children: [
26197
26217
  /* @__PURE__ */ jsxRuntime.jsxs(
26198
26218
  HStack,
@@ -26221,11 +26241,11 @@ var init_DashboardLayout = __esm({
26221
26241
  }
26222
26242
  )
26223
26243
  ] }),
26224
- /* @__PURE__ */ jsxRuntime.jsx(
26244
+ isMobile && /* @__PURE__ */ jsxRuntime.jsx(
26225
26245
  Button,
26226
26246
  {
26227
26247
  variant: "ghost",
26228
- className: "@lg/dashboard:hidden p-2 rounded-md hover:bg-muted dark:hover:bg-muted text-muted-foreground dark:text-muted-foreground",
26248
+ className: "p-2 rounded-md hover:bg-muted dark:hover:bg-muted text-muted-foreground dark:text-muted-foreground",
26229
26249
  onClick: () => setSidebarOpen(false),
26230
26250
  children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "x", className: "h-5 w-5" })
26231
26251
  }
@@ -26266,11 +26286,11 @@ var init_DashboardLayout = __esm({
26266
26286
  justify: "between",
26267
26287
  className: "h-full px-3 @sm/dashboard:px-4 gap-2 @sm/dashboard:gap-4",
26268
26288
  children: [
26269
- showSidebar && /* @__PURE__ */ jsxRuntime.jsx(
26289
+ showSidebar && isMobile && /* @__PURE__ */ jsxRuntime.jsx(
26270
26290
  Button,
26271
26291
  {
26272
26292
  variant: "ghost",
26273
- className: "@lg/dashboard:hidden p-2 rounded-md hover:bg-muted dark:hover:bg-muted text-muted-foreground dark:text-muted-foreground touch-manipulation min-h-[44px] min-w-[44px] flex items-center justify-center",
26293
+ className: "p-2 rounded-md hover:bg-muted dark:hover:bg-muted text-muted-foreground dark:text-muted-foreground touch-manipulation min-h-[44px] min-w-[44px] flex items-center justify-center",
26274
26294
  onClick: () => setSidebarOpen(true),
26275
26295
  "aria-label": "Open sidebar",
26276
26296
  children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "menu", className: "h-5 w-5" })
@@ -62653,15 +62673,9 @@ function buildTransitionSchema(fullSchema, orbitalName, traitName, transitionEve
62653
62673
  return { ...fullSchema, name: `${fullSchema.name}__${orbitalName}__${traitName}__${transitionEvent}`, orbitals: [clonedOrbital] };
62654
62674
  }
62655
62675
  var SELECTION_STYLES = `
62656
- .orb-preview-live [data-pattern]:hover {
62657
- outline: 2px dashed var(--color-primary);
62658
- outline-offset: 1px;
62676
+ .orb-preview-live [data-pattern] {
62659
62677
  cursor: pointer;
62660
62678
  }
62661
- .orb-preview-live [data-pattern].pattern-selected {
62662
- outline: 2px solid var(--color-primary);
62663
- outline-offset: 1px;
62664
- }
62665
62679
  .orb-preview-live.drag-active [data-accepts-children="true"] {
62666
62680
  outline: 2px dashed var(--color-primary);
62667
62681
  outline-offset: -2px;
@@ -62678,15 +62692,66 @@ var SELECTION_STYLES = `
62678
62692
  pointer-events: none;
62679
62693
  }
62680
62694
  `;
62695
+ function absUnion(el) {
62696
+ const own = el.getBoundingClientRect();
62697
+ if (own.width > 0 || own.height > 0) {
62698
+ return { top: own.top, left: own.left, right: own.right, bottom: own.bottom };
62699
+ }
62700
+ let r2 = null;
62701
+ for (const child of Array.from(el.children)) {
62702
+ const box = absUnion(child);
62703
+ if (!box) continue;
62704
+ r2 = r2 ? {
62705
+ top: Math.min(r2.top, box.top),
62706
+ left: Math.min(r2.left, box.left),
62707
+ right: Math.max(r2.right, box.right),
62708
+ bottom: Math.max(r2.bottom, box.bottom)
62709
+ } : box;
62710
+ }
62711
+ return r2;
62712
+ }
62713
+ function rectRelativeTo(el, container) {
62714
+ const u = absUnion(el);
62715
+ if (!u) return null;
62716
+ const base = container.getBoundingClientRect();
62717
+ return { top: u.top - base.top, left: u.left - base.left, width: u.right - u.left, height: u.bottom - u.top };
62718
+ }
62719
+ function buildFocus(el, orbitalName) {
62720
+ if (!orbitalName) return null;
62721
+ const path = el.getAttribute("data-orb-path") ?? el.getAttribute("data-pattern-path");
62722
+ const patternType = el.getAttribute("data-orb-pattern") ?? el.getAttribute("data-pattern");
62723
+ const trait = el.getAttribute("data-orb-trait") ?? el.getAttribute("data-source-trait");
62724
+ const focus = {
62725
+ level: "node",
62726
+ orbital: orbitalName,
62727
+ label: patternType ?? trait ?? "element"
62728
+ };
62729
+ if (path !== null) focus.path = path;
62730
+ if (trait !== null) focus.trait = trait;
62731
+ if (patternType !== null) focus.patternType = patternType;
62732
+ const transition = el.getAttribute("data-orb-transition");
62733
+ if (transition !== null) focus.transition = transition;
62734
+ const state = el.getAttribute("data-orb-state");
62735
+ if (state !== null) focus.state = state;
62736
+ const slot = el.getAttribute("data-orb-slot");
62737
+ if (slot !== null) focus.slot = slot;
62738
+ const entity = el.getAttribute("data-orb-entity");
62739
+ if (entity !== null) focus.entity = entity;
62740
+ return focus;
62741
+ }
62681
62742
  var OrbPreviewNodeInner = (props) => {
62682
62743
  const data = props.data;
62683
62744
  const screenSize = React98.useContext(ScreenSizeContext);
62684
62745
  const preset = SCREEN_SIZE_PRESETS[screenSize];
62685
62746
  const { select } = React98.useContext(PatternSelectionContext);
62747
+ const eventBus = useEventBus();
62686
62748
  const contentRef = React98.useRef(null);
62687
62749
  const [hovered, setHovered] = React98.useState(false);
62688
62750
  const handleMouseEnter = React98.useCallback(() => setHovered(true), []);
62689
62751
  const handleMouseLeave = React98.useCallback(() => setHovered(false), []);
62752
+ const [selectedRect, setSelectedRect] = React98.useState(null);
62753
+ const [hoverRect, setHoverRect] = React98.useState(null);
62754
+ const lastHoverElRef = React98.useRef(null);
62690
62755
  const role = data.stateRole ?? "default";
62691
62756
  const colors = ROLE_COLORS[role] ?? ROLE_COLORS.default;
62692
62757
  const eventSources = data.eventSources ?? [];
@@ -62719,13 +62784,14 @@ var OrbPreviewNodeInner = (props) => {
62719
62784
  e.stopPropagation();
62720
62785
  const target = e.target;
62721
62786
  const patternEl = target.closest("[data-pattern]");
62722
- contentRef.current?.querySelectorAll(".pattern-selected").forEach((el) => {
62787
+ const container = contentRef.current;
62788
+ container?.querySelectorAll(".pattern-selected").forEach((el) => {
62723
62789
  el.classList.remove("pattern-selected");
62724
62790
  });
62725
- if (patternEl) {
62791
+ if (patternEl && container) {
62726
62792
  patternEl.classList.add("pattern-selected");
62727
- const nodeRect = contentRef.current?.getBoundingClientRect();
62728
- const elRect = patternEl.getBoundingClientRect();
62793
+ const rect = rectRelativeTo(patternEl, container);
62794
+ setSelectedRect(rect);
62729
62795
  select({
62730
62796
  patternType: patternEl.getAttribute("data-pattern") ?? "unknown",
62731
62797
  // `data-pattern-path` is the SExpr tree path (`children.0.…`) emitted by
@@ -62736,18 +62802,33 @@ var OrbPreviewNodeInner = (props) => {
62736
62802
  patternId: patternEl.getAttribute("data-pattern-path") ?? void 0,
62737
62803
  sourceTrait: patternEl.getAttribute("data-source-trait") ?? void 0,
62738
62804
  nodeData: data,
62739
- rect: nodeRect ? {
62740
- top: elRect.top - nodeRect.top,
62741
- left: elRect.left - nodeRect.left,
62742
- width: elRect.width,
62743
- height: elRect.height
62744
- } : void 0
62805
+ rect: rect ?? void 0
62745
62806
  });
62807
+ const focus = buildFocus(patternEl, data.orbitalName);
62808
+ if (focus) eventBus.emit("UI:ELEMENT_SELECTED", { focus: { ...focus } });
62746
62809
  } else {
62810
+ setSelectedRect(null);
62747
62811
  select(null);
62748
- }
62749
- }, [data, select]);
62750
- const eventBus = useEventBus();
62812
+ eventBus.emit("UI:ELEMENT_SELECTED", { focus: null });
62813
+ }
62814
+ }, [data, select, eventBus]);
62815
+ const handleContentMouseMove = React98.useCallback((e) => {
62816
+ const container = contentRef.current;
62817
+ if (!container) return;
62818
+ const el = e.target.closest("[data-pattern]");
62819
+ if (el === lastHoverElRef.current) return;
62820
+ lastHoverElRef.current = el;
62821
+ setHoverRect(el ? rectRelativeTo(el, container) : null);
62822
+ }, []);
62823
+ const handleContentMouseLeave = React98.useCallback(() => {
62824
+ lastHoverElRef.current = null;
62825
+ setHoverRect(null);
62826
+ }, []);
62827
+ React98.useEffect(() => {
62828
+ setSelectedRect(null);
62829
+ setHoverRect(null);
62830
+ lastHoverElRef.current = null;
62831
+ }, [orbitalSchema]);
62751
62832
  const [dragActive, setDragActive] = React98.useState(false);
62752
62833
  React98.useEffect(() => {
62753
62834
  const unsub1 = eventBus.on("UI:DRAG_START", (e) => {
@@ -62966,26 +63047,66 @@ var OrbPreviewNodeInner = (props) => {
62966
63047
  src.event
62967
63048
  )) })
62968
63049
  ] }),
62969
- /* @__PURE__ */ jsxRuntime.jsx(
63050
+ /* @__PURE__ */ jsxRuntime.jsxs(
62970
63051
  Box,
62971
63052
  {
62972
63053
  ref: setContentRef,
62973
- className: `orb-preview-live nodrag${dragActive || l2IsOver ? " drag-active" : ""}`,
63054
+ className: `orb-preview-live nodrag relative${dragActive || l2IsOver ? " drag-active" : ""}`,
62974
63055
  onClick: handleContentClick,
62975
- children: orbitalSchema ? (
62976
- // L1 and L2 both auto-grow with content. L2's `buildTransitionSchema`
62977
- // rewrites portal slots (modal/drawer/overlay/center) to `main`, so
62978
- // the rendered pattern lands inline in the main slot and contributes
62979
- // to the card's height — same height model as L1 orbital cards.
62980
- /* @__PURE__ */ jsxRuntime.jsx(Box, { style: { minHeight: preset.minHeight }, children: /* @__PURE__ */ jsxRuntime.jsx(
62981
- BrowserPlayground,
63056
+ onMouseMove: handleContentMouseMove,
63057
+ onMouseLeave: handleContentMouseLeave,
63058
+ children: [
63059
+ hoverRect && /* @__PURE__ */ jsxRuntime.jsx(
63060
+ "div",
62982
63061
  {
62983
- schema: orbitalSchema,
62984
- mode: "mock",
62985
- height: "auto"
63062
+ "aria-hidden": true,
63063
+ style: {
63064
+ position: "absolute",
63065
+ pointerEvents: "none",
63066
+ top: hoverRect.top,
63067
+ left: hoverRect.left,
63068
+ width: hoverRect.width,
63069
+ height: hoverRect.height,
63070
+ outline: "2px dashed var(--color-primary)",
63071
+ outlineOffset: "1px",
63072
+ borderRadius: "2px",
63073
+ zIndex: 20
63074
+ }
62986
63075
  }
62987
- ) })
62988
- ) : /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex items-center justify-center", style: { minHeight: preset.minHeight }, children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "text-muted-foreground", children: "No preview available" }) })
63076
+ ),
63077
+ selectedRect && /* @__PURE__ */ jsxRuntime.jsx(
63078
+ "div",
63079
+ {
63080
+ "aria-hidden": true,
63081
+ style: {
63082
+ position: "absolute",
63083
+ pointerEvents: "none",
63084
+ top: selectedRect.top,
63085
+ left: selectedRect.left,
63086
+ width: selectedRect.width,
63087
+ height: selectedRect.height,
63088
+ outline: "2px solid var(--color-primary)",
63089
+ outlineOffset: "1px",
63090
+ borderRadius: "2px",
63091
+ zIndex: 21
63092
+ }
63093
+ }
63094
+ ),
63095
+ orbitalSchema ? (
63096
+ // L1 and L2 both auto-grow with content. L2's `buildTransitionSchema`
63097
+ // rewrites portal slots (modal/drawer/overlay/center) to `main`, so
63098
+ // the rendered pattern lands inline in the main slot and contributes
63099
+ // to the card's height — same height model as L1 orbital cards.
63100
+ /* @__PURE__ */ jsxRuntime.jsx(Box, { style: { minHeight: preset.minHeight }, children: /* @__PURE__ */ jsxRuntime.jsx(
63101
+ BrowserPlayground,
63102
+ {
63103
+ schema: orbitalSchema,
63104
+ mode: "mock",
63105
+ height: "auto"
63106
+ }
63107
+ ) })
63108
+ ) : /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex items-center justify-center", style: { minHeight: preset.minHeight }, children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "text-muted-foreground", children: "No preview available" }) })
63109
+ ]
62989
63110
  }
62990
63111
  ),
62991
63112
  /* @__PURE__ */ jsxRuntime.jsx(react.Handle, { type: "target", position: react.Position.Left, style: TARGET_HANDLE_STYLE }),