@geomak/ui 6.20.0 → 6.21.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,9 +1,10 @@
1
1
  import { colors_default } from './chunk-I2P4JJDB.js';
2
2
  export { colors_default as COLORS, PALETTE as palette, semanticTokens, vars } from './chunk-I2P4JJDB.js';
3
3
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
- import React24, { createContext, useState, useEffect, useMemo, useId, useCallback, useRef, useContext, useSyncExternalStore, useLayoutEffect } from 'react';
4
+ import React26, { createContext, useState, useEffect, useMemo, useId, useCallback, useRef, useContext, useSyncExternalStore, useLayoutEffect } from 'react';
5
5
  import { createPortal } from 'react-dom';
6
6
  import * as AvatarPrimitive from '@radix-ui/react-avatar';
7
+ import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
7
8
  import * as Dialog from '@radix-ui/react-dialog';
8
9
  import { useReducedMotion, AnimatePresence, motion } from 'framer-motion';
9
10
  import * as TooltipPrimitive from '@radix-ui/react-tooltip';
@@ -692,7 +693,7 @@ var SIZE_CLASSES = {
692
693
  md: "h-9 px-4 text-sm gap-1.5 rounded-lg",
693
694
  lg: "h-11 px-5 text-sm gap-2 rounded-xl"
694
695
  };
695
- var Button = React24.forwardRef(function Button2({
696
+ var Button = React26.forwardRef(function Button2({
696
697
  content,
697
698
  variant = "primary",
698
699
  size = "md",
@@ -749,6 +750,80 @@ var Button = React24.forwardRef(function Button2({
749
750
  });
750
751
  Button.displayName = "Button";
751
752
  var Button_default = Button;
753
+ var Chevron = () => /* @__PURE__ */ jsx(
754
+ "svg",
755
+ {
756
+ viewBox: "0 0 24 24",
757
+ fill: "none",
758
+ stroke: "currentColor",
759
+ strokeWidth: 2,
760
+ "aria-hidden": "true",
761
+ className: "h-4 w-4 transition-transform duration-150 group-data-[state=open]:rotate-180",
762
+ children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 9l6 6 6-6" })
763
+ }
764
+ );
765
+ function MenuButton({
766
+ label,
767
+ items,
768
+ variant = "secondary",
769
+ size = "md",
770
+ icon,
771
+ hideChevron = false,
772
+ disabled,
773
+ align = "start",
774
+ side = "bottom",
775
+ className = ""
776
+ }) {
777
+ return /* @__PURE__ */ jsxs(DropdownMenu.Root, { children: [
778
+ /* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
779
+ Button_default,
780
+ {
781
+ variant,
782
+ size,
783
+ icon,
784
+ disabled,
785
+ className: `group ${className}`.trim(),
786
+ content: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5", children: [
787
+ label,
788
+ !hideChevron && /* @__PURE__ */ jsx(Chevron, {})
789
+ ] })
790
+ }
791
+ ) }),
792
+ /* @__PURE__ */ jsx(DropdownMenu.Portal, { children: /* @__PURE__ */ jsx(
793
+ DropdownMenu.Content,
794
+ {
795
+ align,
796
+ side,
797
+ sideOffset: 6,
798
+ collisionPadding: 8,
799
+ className: [
800
+ "z-[400] min-w-[10rem] rounded-lg border border-border bg-surface p-1 shadow-lg",
801
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
802
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95"
803
+ ].join(" "),
804
+ children: items.map((item) => /* @__PURE__ */ jsxs(React26.Fragment, { children: [
805
+ item.separatorBefore && /* @__PURE__ */ jsx(DropdownMenu.Separator, { className: "my-1 h-px bg-border" }),
806
+ /* @__PURE__ */ jsxs(
807
+ DropdownMenu.Item,
808
+ {
809
+ disabled: item.disabled,
810
+ onSelect: () => item.onSelect?.(),
811
+ className: [
812
+ "flex items-center gap-2.5 rounded-md px-2.5 py-1.5 text-sm outline-none cursor-pointer select-none transition-colors",
813
+ "data-[disabled]:opacity-50 data-[disabled]:pointer-events-none",
814
+ item.danger ? "text-status-error data-[highlighted]:bg-status-error/10" : "text-foreground data-[highlighted]:bg-surface-raised"
815
+ ].join(" "),
816
+ children: [
817
+ item.icon && /* @__PURE__ */ jsx("span", { className: "flex h-4 w-4 flex-shrink-0 items-center justify-center", children: item.icon }),
818
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: item.label })
819
+ ]
820
+ }
821
+ )
822
+ ] }, item.key))
823
+ }
824
+ ) })
825
+ ] });
826
+ }
752
827
  var SIZE_MAP = {
753
828
  sm: 400,
754
829
  md: 600,
@@ -1092,7 +1167,7 @@ function TabsList({ children, "aria-label": ariaLabel, className = "" }) {
1092
1167
  const scrollClass = scrollable ? horizontal ? "overflow-x-auto overflow-y-hidden hidden-scrollbar" : "overflow-y-auto overflow-x-hidden hidden-scrollbar" : "";
1093
1168
  const overflowing = scrollable && (edges.start || edges.end);
1094
1169
  return /* @__PURE__ */ jsxs("div", { className: ["relative flex min-w-0 gap-1", horizontal ? "flex-row items-stretch" : "flex-col items-stretch", className].filter(Boolean).join(" "), children: [
1095
- scrollable && edges.start && /* @__PURE__ */ jsx(Chevron, { side: "start", orientation, onClick: () => nudge(-1) }),
1170
+ scrollable && edges.start && /* @__PURE__ */ jsx(Chevron2, { side: "start", orientation, onClick: () => nudge(-1) }),
1096
1171
  /* @__PURE__ */ jsx(
1097
1172
  TabsPrimitive.List,
1098
1173
  {
@@ -1103,11 +1178,11 @@ function TabsList({ children, "aria-label": ariaLabel, className = "" }) {
1103
1178
  children
1104
1179
  }
1105
1180
  ),
1106
- scrollable && edges.end && /* @__PURE__ */ jsx(Chevron, { side: "end", orientation, onClick: () => nudge(1) }),
1181
+ scrollable && edges.end && /* @__PURE__ */ jsx(Chevron2, { side: "end", orientation, onClick: () => nudge(1) }),
1107
1182
  overflowing && /* @__PURE__ */ jsx(OverflowMenu, {})
1108
1183
  ] });
1109
1184
  }
1110
- function Chevron({ side, orientation, onClick }) {
1185
+ function Chevron2({ side, orientation, onClick }) {
1111
1186
  const horizontal = orientation === "horizontal";
1112
1187
  const rotate = horizontal ? side === "start" ? "rotate-180" : "" : side === "start" ? "-rotate-90" : "rotate-90";
1113
1188
  return /* @__PURE__ */ jsx(
@@ -1459,7 +1534,7 @@ function Accordion2({
1459
1534
  }
1460
1535
  );
1461
1536
  }
1462
- var Chevron2 = /* @__PURE__ */ jsx(
1537
+ var Chevron3 = /* @__PURE__ */ jsx(
1463
1538
  "svg",
1464
1539
  {
1465
1540
  viewBox: "0 0 24 24",
@@ -1491,7 +1566,7 @@ function AccordionItem({ value, title, icon, children, disabled, className = ""
1491
1566
  children: [
1492
1567
  icon && /* @__PURE__ */ jsx("span", { className: "flex h-5 w-5 flex-shrink-0 items-center justify-center text-foreground-muted group-data-[state=open]/acc:text-accent", children: icon }),
1493
1568
  /* @__PURE__ */ jsx("span", { className: "flex-1 min-w-0", children: title }),
1494
- Chevron2
1569
+ Chevron3
1495
1570
  ]
1496
1571
  }
1497
1572
  ) }),
@@ -1661,6 +1736,156 @@ function Badge({
1661
1736
  !hidden && /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-1 flex", children: indicator })
1662
1737
  ] });
1663
1738
  }
1739
+ var SIZES = {
1740
+ sm: { box: "h-7 w-7 text-xs", center: 14, title: "text-xs", desc: "text-[11px]" },
1741
+ md: { box: "h-9 w-9 text-sm", center: 18, title: "text-sm", desc: "text-xs" }
1742
+ };
1743
+ var Check = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 3, "aria-hidden": "true", className: "h-1/2 w-1/2 animate-check-pop", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M20 6L9 17l-5-5" }) });
1744
+ var Cross = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 3, "aria-hidden": "true", className: "h-1/2 w-1/2", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M18 6 6 18M6 6l12 12" }) });
1745
+ var Spinner = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", className: "h-1/2 w-1/2 animate-spin", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M4.755 10.059a7.5 7.5 0 0112.548-3.364l1.903 1.903h-3.183a.75.75 0 100 1.5h4.992a.75.75 0 00.75-.75V4.356a.75.75 0 00-1.5 0v3.18l-1.9-1.9A9 9 0 003.306 9.67a.75.75 0 101.45.388zm15.408 3.352a.75.75 0 00-.919.53 7.5 7.5 0 01-12.548 3.364l-1.902-1.903h3.183a.75.75 0 000-1.5H2.984a.75.75 0 00-.75.75v4.992a.75.75 0 001.5 0v-3.18l1.9 1.9a9 9 0 0015.059-4.035.75.75 0 00-.53-.918z" }) });
1746
+ function Indicator({ state, index, step, sizeKey }) {
1747
+ const reduced = useReducedMotion();
1748
+ const s = SIZES[sizeKey];
1749
+ const base = `relative z-10 flex flex-shrink-0 items-center justify-center rounded-full font-semibold transition-colors ${s.box}`;
1750
+ const tone = {
1751
+ completed: "bg-accent text-accent-fg",
1752
+ active: "border-2 border-accent bg-surface text-accent",
1753
+ loading: "border-2 border-accent bg-surface text-accent",
1754
+ error: "bg-status-error text-white",
1755
+ pending: "border border-border bg-surface text-foreground-muted"
1756
+ };
1757
+ const content = step.icon ?? (state === "completed" ? /* @__PURE__ */ jsx(Check, {}) : state === "error" ? /* @__PURE__ */ jsx(Cross, {}) : state === "loading" ? /* @__PURE__ */ jsx(Spinner, {}) : index + 1);
1758
+ return /* @__PURE__ */ jsx(
1759
+ motion.span,
1760
+ {
1761
+ className: `${base} ${tone[state]}`,
1762
+ initial: false,
1763
+ animate: reduced ? void 0 : { scale: state === "active" || state === "loading" ? 1.06 : 1 },
1764
+ transition: { type: "spring", stiffness: 320, damping: 24 },
1765
+ "aria-current": state === "active" || state === "loading" ? "step" : void 0,
1766
+ children: content
1767
+ }
1768
+ );
1769
+ }
1770
+ function VConnector({ filled, reduced }) {
1771
+ return /* @__PURE__ */ jsx("span", { className: "relative my-1 w-0.5 flex-1 bg-border", children: /* @__PURE__ */ jsx(
1772
+ motion.span,
1773
+ {
1774
+ className: "absolute inset-0 bg-accent",
1775
+ style: { originY: 0 },
1776
+ initial: false,
1777
+ animate: { scaleY: filled ? 1 : 0 },
1778
+ transition: reduced ? { duration: 0 } : { duration: 0.35, ease: [0.16, 1, 0.3, 1] }
1779
+ }
1780
+ ) });
1781
+ }
1782
+ function Stepper({
1783
+ steps,
1784
+ current,
1785
+ status = "active",
1786
+ orientation = "horizontal",
1787
+ responsive = true,
1788
+ onStepClick,
1789
+ size = "md",
1790
+ className = ""
1791
+ }) {
1792
+ const reduced = useReducedMotion();
1793
+ const [forcedVertical, setForcedVertical] = useState(false);
1794
+ useEffect(() => {
1795
+ if (!responsive || orientation === "vertical") return;
1796
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
1797
+ const mq = window.matchMedia("(max-width: 767px)");
1798
+ const apply = (e) => setForcedVertical(e.matches);
1799
+ apply(mq);
1800
+ const handler = (e) => apply(e);
1801
+ mq.addEventListener("change", handler);
1802
+ return () => mq.removeEventListener("change", handler);
1803
+ }, [responsive, orientation]);
1804
+ const vertical = orientation === "vertical" || forcedVertical;
1805
+ const s = SIZES[size];
1806
+ const stateOf = (i) => i < current ? "completed" : i === current ? status : "pending";
1807
+ const Label = ({ step, state, align }) => /* @__PURE__ */ jsxs("div", { className: align === "center" ? "mt-2 text-center" : "pt-0.5", children: [
1808
+ /* @__PURE__ */ jsx("div", { className: `font-medium leading-tight ${s.title} ${state === "pending" ? "text-foreground-muted" : "text-foreground"}`, children: step.title }),
1809
+ step.description && /* @__PURE__ */ jsx("div", { className: `mt-0.5 leading-snug text-foreground-muted ${s.desc}`, children: step.description })
1810
+ ] });
1811
+ const clickable = (i) => Boolean(onStepClick) && i <= current;
1812
+ const stepButton = (i, node) => clickable(i) ? /* @__PURE__ */ jsx("button", { type: "button", onClick: () => onStepClick?.(i), className: "rounded-md text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-accent", children: node }) : node;
1813
+ if (vertical) {
1814
+ return /* @__PURE__ */ jsx("ol", { className: ["flex flex-col", className].filter(Boolean).join(" "), children: steps.map((step, i) => {
1815
+ const state = stateOf(i);
1816
+ const last = i === steps.length - 1;
1817
+ return /* @__PURE__ */ jsxs("li", { className: "flex gap-3", children: [
1818
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center self-stretch", children: [
1819
+ stepButton(i, /* @__PURE__ */ jsx(Indicator, { state, index: i, step, sizeKey: size })),
1820
+ !last && /* @__PURE__ */ jsx(VConnector, { filled: i < current, reduced })
1821
+ ] }),
1822
+ /* @__PURE__ */ jsx("div", { className: last ? "" : "pb-6", children: stepButton(i, /* @__PURE__ */ jsx(Label, { step, state, align: "left" })) })
1823
+ ] }, step.key);
1824
+ }) });
1825
+ }
1826
+ return /* @__PURE__ */ jsx("ol", { className: ["flex items-start", className].filter(Boolean).join(" "), children: steps.map((step, i) => {
1827
+ const state = stateOf(i);
1828
+ return /* @__PURE__ */ jsxs("li", { className: "relative flex flex-1 flex-col items-center last:flex-none", children: [
1829
+ i > 0 && /* @__PURE__ */ jsx("span", { className: "absolute right-1/2 h-0.5 w-full bg-border", style: { top: s.center - 1 }, children: /* @__PURE__ */ jsx(
1830
+ motion.span,
1831
+ {
1832
+ className: "absolute inset-0 bg-accent",
1833
+ style: { originX: 0 },
1834
+ initial: false,
1835
+ animate: { scaleX: i <= current ? 1 : 0 },
1836
+ transition: reduced ? { duration: 0 } : { duration: 0.35, ease: [0.16, 1, 0.3, 1] }
1837
+ }
1838
+ ) }),
1839
+ stepButton(i, /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center", children: [
1840
+ /* @__PURE__ */ jsx(Indicator, { state, index: i, step, sizeKey: size }),
1841
+ /* @__PURE__ */ jsx(Label, { step, state, align: "center" })
1842
+ ] }))
1843
+ ] }, step.key);
1844
+ }) });
1845
+ }
1846
+ var Check2 = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 3, "aria-hidden": "true", className: "h-3 w-3", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M20 6L9 17l-5-5" }) });
1847
+ var Cross2 = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 3, "aria-hidden": "true", className: "h-3 w-3", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M18 6 6 18M6 6l12 12" }) });
1848
+ var NODE = {
1849
+ complete: "bg-accent text-accent-fg border-accent",
1850
+ current: "bg-surface text-accent border-accent",
1851
+ upcoming: "bg-surface text-transparent border-border",
1852
+ error: "bg-status-error text-white border-status-error"
1853
+ };
1854
+ function Timeline({ events, current, className = "" }) {
1855
+ const statusOf = (e, i) => {
1856
+ if (e.status) return e.status;
1857
+ if (current == null) return "upcoming";
1858
+ return i < current ? "complete" : i === current ? "current" : "upcoming";
1859
+ };
1860
+ return /* @__PURE__ */ jsx("ol", { className: ["flex flex-col", className].filter(Boolean).join(" "), children: events.map((event, i) => {
1861
+ const status = statusOf(event, i);
1862
+ const last = i === events.length - 1;
1863
+ const railFilled = status === "complete";
1864
+ return /* @__PURE__ */ jsxs("li", { className: "flex gap-3", children: [
1865
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center self-stretch", children: [
1866
+ /* @__PURE__ */ jsxs(
1867
+ "span",
1868
+ {
1869
+ className: [
1870
+ "relative z-10 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full border-2 transition-colors",
1871
+ NODE[status]
1872
+ ].join(" "),
1873
+ children: [
1874
+ status === "current" && /* @__PURE__ */ jsx("span", { className: "absolute inset-0 animate-breathe rounded-full ring-2 ring-accent/40", "aria-hidden": "true" }),
1875
+ event.icon ?? (status === "complete" ? /* @__PURE__ */ jsx(Check2, {}) : status === "error" ? /* @__PURE__ */ jsx(Cross2, {}) : /* @__PURE__ */ jsx("span", { className: `h-2 w-2 rounded-full ${status === "current" ? "bg-accent" : "bg-border"}` }))
1876
+ ]
1877
+ }
1878
+ ),
1879
+ !last && /* @__PURE__ */ jsx("span", { className: `my-1 w-0.5 flex-1 ${railFilled ? "bg-accent" : "bg-border"}` })
1880
+ ] }),
1881
+ /* @__PURE__ */ jsxs("div", { className: last ? "pb-0" : "pb-6", children: [
1882
+ /* @__PURE__ */ jsx("div", { className: `text-sm font-medium leading-tight ${status === "upcoming" ? "text-foreground-muted" : "text-foreground"}`, children: event.title }),
1883
+ event.description && /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs leading-snug text-foreground-secondary", children: event.description }),
1884
+ event.timestamp && /* @__PURE__ */ jsx("div", { className: "mt-1 text-[11px] uppercase tracking-wide text-foreground-muted", children: event.timestamp })
1885
+ ] })
1886
+ ] }, event.key);
1887
+ }) });
1888
+ }
1664
1889
  var SIZE3 = {
1665
1890
  sm: "h-5 min-w-[20px] px-1.5 text-[11px]",
1666
1891
  md: "h-6 min-w-[24px] px-2 text-xs"
@@ -1675,7 +1900,7 @@ function Kbd({
1675
1900
  style
1676
1901
  }) {
1677
1902
  if (keys && keys.length > 0) {
1678
- return /* @__PURE__ */ jsx("span", { className: ["inline-flex items-center gap-1", className].filter(Boolean).join(" "), style, children: keys.map((k, i) => /* @__PURE__ */ jsxs(React24.Fragment, { children: [
1903
+ return /* @__PURE__ */ jsx("span", { className: ["inline-flex items-center gap-1", className].filter(Boolean).join(" "), style, children: keys.map((k, i) => /* @__PURE__ */ jsxs(React26.Fragment, { children: [
1679
1904
  i > 0 && /* @__PURE__ */ jsx("span", { className: "text-foreground-muted text-xs select-none", children: separator }),
1680
1905
  /* @__PURE__ */ jsx("kbd", { className: [cap, SIZE3[size]].join(" "), children: k })
1681
1906
  ] }, `${k}-${i}`)) });
@@ -1767,7 +1992,7 @@ function FlatCarousel({
1767
1992
  style
1768
1993
  }) {
1769
1994
  const scrollerRef = useRef(null);
1770
- const slides = React24.Children.toArray(children);
1995
+ const slides = React26.Children.toArray(children);
1771
1996
  const [active, setActive] = useState(0);
1772
1997
  const [atStart, setAtStart] = useState(true);
1773
1998
  const [atEnd, setAtEnd] = useState(false);
@@ -1822,7 +2047,7 @@ function RotatingCarousel({
1822
2047
  className = "",
1823
2048
  style
1824
2049
  }) {
1825
- const slides = React24.Children.toArray(children);
2050
+ const slides = React26.Children.toArray(children);
1826
2051
  const count = slides.length;
1827
2052
  const [active, setActive] = useState(0);
1828
2053
  const reduced = useReducedMotion();
@@ -2315,7 +2540,7 @@ function EmptyCart({
2315
2540
  );
2316
2541
  }
2317
2542
  var defaultFormat = (v) => `${v < 0 ? "-" : ""}$${Math.abs(v).toFixed(2)}`;
2318
- var Stepper = ({
2543
+ var Stepper2 = ({
2319
2544
  quantity,
2320
2545
  max,
2321
2546
  onChange
@@ -2348,7 +2573,7 @@ function Cart({
2348
2573
  /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium text-foreground", children: it.name }),
2349
2574
  it.meta && /* @__PURE__ */ jsx("div", { className: "truncate text-xs text-foreground-muted mt-0.5", children: it.meta }),
2350
2575
  /* @__PURE__ */ jsxs("div", { className: "mt-1.5 flex items-center gap-3", children: [
2351
- /* @__PURE__ */ jsx(Stepper, { quantity: it.quantity, max: it.max, onChange: (q) => onQuantityChange?.(it.id, Math.max(1, q)) }),
2576
+ /* @__PURE__ */ jsx(Stepper2, { quantity: it.quantity, max: it.max, onChange: (q) => onQuantityChange?.(it.id, Math.max(1, q)) }),
2352
2577
  onRemove && /* @__PURE__ */ jsx(
2353
2578
  "button",
2354
2579
  {
@@ -4316,7 +4541,7 @@ function Wizard({
4316
4541
  ] });
4317
4542
  }
4318
4543
  var SearchIcon = /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-4 h-4", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10.5 3.75a6.75 6.75 0 100 13.5 6.75 6.75 0 000-13.5zM2.25 10.5a8.25 8.25 0 1114.59 5.28l4.69 4.69a.75.75 0 11-1.06 1.06l-4.69-4.69A8.25 8.25 0 012.25 10.5z", clipRule: "evenodd" }) });
4319
- var SearchInput = React24.forwardRef(function SearchInput2({ value, onChange, disabled, label, htmlFor, placeholder, name, inputStyle, style, layout = "vertical", size = "md", icon, helperText, className }, ref) {
4544
+ var SearchInput = React26.forwardRef(function SearchInput2({ value, onChange, disabled, label, htmlFor, placeholder, name, inputStyle, style, layout = "vertical", size = "md", icon, helperText, className }, ref) {
4320
4545
  return /* @__PURE__ */ jsx(Field, { className, label, htmlFor, layout, helperText, children: /* @__PURE__ */ jsxs(
4321
4546
  "div",
4322
4547
  {
@@ -4817,7 +5042,7 @@ function TableBody({
4817
5042
  return /* @__PURE__ */ jsx("tbody", { children: rows.map((row, i) => {
4818
5043
  const rowKey = getRowKey(row, i);
4819
5044
  const isExpanded = expanded.has(rowKey);
4820
- return /* @__PURE__ */ jsxs(React24.Fragment, { children: [
5045
+ return /* @__PURE__ */ jsxs(React26.Fragment, { children: [
4821
5046
  /* @__PURE__ */ jsxs(
4822
5047
  "tr",
4823
5048
  {
@@ -5361,8 +5586,8 @@ function MegaMenuLink({ href, icon, description, active, onClick, children, clas
5361
5586
  function MegaMenuFeatured({ children, className = "" }) {
5362
5587
  return /* @__PURE__ */ jsx("div", { className: ["min-w-0 rounded-lg bg-surface-raised border border-border p-4 flex flex-col", className].filter(Boolean).join(" "), children });
5363
5588
  }
5364
- var elementsOfType = (children, type) => React24.Children.toArray(children).filter(
5365
- (c) => React24.isValidElement(c) && c.type === type
5589
+ var elementsOfType = (children, type) => React26.Children.toArray(children).filter(
5590
+ (c) => React26.isValidElement(c) && c.type === type
5366
5591
  );
5367
5592
  var MOBILE_CHEVRON = /* @__PURE__ */ jsx(
5368
5593
  "svg",
@@ -5399,9 +5624,9 @@ function MobileLinkRow({ link, onNavigate }) {
5399
5624
  );
5400
5625
  }
5401
5626
  function MobilePanel({ panel, onNavigate }) {
5402
- const nodes = React24.Children.toArray(panel.props.children);
5627
+ const nodes = React26.Children.toArray(panel.props.children);
5403
5628
  return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4 px-2 pb-3 pt-1", children: nodes.map((node, i) => {
5404
- if (!React24.isValidElement(node)) return null;
5629
+ if (!React26.isValidElement(node)) return null;
5405
5630
  const el = node;
5406
5631
  if (el.type === MegaMenuSection) {
5407
5632
  const { title, children } = el.props;
@@ -5591,6 +5816,101 @@ function AppShell({
5591
5816
  ] })
5592
5817
  ] });
5593
5818
  }
5819
+ function parseJwt(token) {
5820
+ try {
5821
+ const part = token.split(".")[1];
5822
+ if (!part || typeof atob === "undefined") return null;
5823
+ const json = decodeURIComponent(
5824
+ atob(part.replace(/-/g, "+").replace(/_/g, "/")).split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")
5825
+ );
5826
+ return JSON.parse(json);
5827
+ } catch {
5828
+ return null;
5829
+ }
5830
+ }
5831
+ function tokenValid(token) {
5832
+ const payload = parseJwt(token);
5833
+ if (!payload) return false;
5834
+ const exp = typeof payload.exp === "number" ? payload.exp : null;
5835
+ if (exp == null) return true;
5836
+ return exp * 1e3 > Date.now();
5837
+ }
5838
+ var has = (have, need, all) => all ? need.every((n) => have?.includes(n)) : need.some((n) => have?.includes(n));
5839
+ var Spinner2 = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", className: "h-6 w-6 animate-spin text-accent", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M4.755 10.059a7.5 7.5 0 0112.548-3.364l1.903 1.903h-3.183a.75.75 0 100 1.5h4.992a.75.75 0 00.75-.75V4.356a.75.75 0 00-1.5 0v3.18l-1.9-1.9A9 9 0 003.306 9.67a.75.75 0 101.45.388zm15.408 3.352a.75.75 0 00-.919.53 7.5 7.5 0 01-12.548 3.364l-1.902-1.903h3.183a.75.75 0 000-1.5H2.984a.75.75 0 00-.75.75v4.992a.75.75 0 001.5 0v-3.18l1.9 1.9a9 9 0 0015.059-4.035.75.75 0 00-.53-.918z" }) });
5840
+ function SecureLayout({
5841
+ children,
5842
+ isAuthenticated,
5843
+ token,
5844
+ roles,
5845
+ requiredRoles,
5846
+ requireAllRoles,
5847
+ permissions,
5848
+ requiredPermissions,
5849
+ requireAllPermissions,
5850
+ canAccess,
5851
+ loadingFallback,
5852
+ fallback,
5853
+ onDeny,
5854
+ className = ""
5855
+ }) {
5856
+ const reduced = useReducedMotion();
5857
+ const [state, setState] = useState("checking");
5858
+ const rolesKey = JSON.stringify(roles);
5859
+ const requiredRolesKey = JSON.stringify(requiredRoles);
5860
+ const permissionsKey = JSON.stringify(permissions);
5861
+ const requiredPermissionsKey = JSON.stringify(requiredPermissions);
5862
+ useEffect(() => {
5863
+ let cancelled = false;
5864
+ setState("checking");
5865
+ const evaluate = async () => {
5866
+ let authed = isAuthenticated;
5867
+ if (authed === void 0 && token !== void 0) authed = tokenValid(token);
5868
+ if (authed === void 0) authed = true;
5869
+ if (!authed) return false;
5870
+ if (requiredRoles?.length && !has(roles, requiredRoles, requireAllRoles)) return false;
5871
+ if (requiredPermissions?.length && !has(permissions, requiredPermissions, requireAllPermissions)) return false;
5872
+ if (canAccess && !await canAccess()) return false;
5873
+ return true;
5874
+ };
5875
+ evaluate().then((ok) => {
5876
+ if (cancelled) return;
5877
+ setState(ok ? "granted" : "denied");
5878
+ if (!ok) onDeny?.();
5879
+ });
5880
+ return () => {
5881
+ cancelled = true;
5882
+ };
5883
+ }, [
5884
+ isAuthenticated,
5885
+ token,
5886
+ requireAllRoles,
5887
+ requireAllPermissions,
5888
+ canAccess,
5889
+ rolesKey,
5890
+ requiredRolesKey,
5891
+ permissionsKey,
5892
+ requiredPermissionsKey
5893
+ ]);
5894
+ if (state === "checking") {
5895
+ return /* @__PURE__ */ jsx("div", { className: ["flex min-h-[8rem] items-center justify-center", className].filter(Boolean).join(" "), children: loadingFallback ?? /* @__PURE__ */ jsx(Spinner2, {}) });
5896
+ }
5897
+ if (state === "denied") {
5898
+ return /* @__PURE__ */ jsx("div", { className: className || void 0, children: fallback ?? /* @__PURE__ */ jsxs("div", { className: "flex min-h-[8rem] flex-col items-center justify-center gap-1 rounded-xl border border-border bg-surface p-8 text-center", children: [
5899
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-foreground", children: "Access denied" }),
5900
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-foreground-muted", children: "You don\u2019t have permission to view this content." })
5901
+ ] }) });
5902
+ }
5903
+ return /* @__PURE__ */ jsx(
5904
+ motion.div,
5905
+ {
5906
+ className: className || void 0,
5907
+ initial: reduced ? false : { opacity: 0 },
5908
+ animate: { opacity: 1 },
5909
+ transition: { duration: reduced ? 0 : 0.2, ease: "easeOut" },
5910
+ children
5911
+ }
5912
+ );
5913
+ }
5594
5914
  function px(v) {
5595
5915
  return typeof v === "number" ? `${v}px` : v;
5596
5916
  }
@@ -5694,7 +6014,7 @@ function ThemeProvider({
5694
6014
  className = "",
5695
6015
  style
5696
6016
  }) {
5697
- const id = React24.useId().replace(/:/g, "");
6017
+ const id = React26.useId().replace(/:/g, "");
5698
6018
  const scopeClass = `geo-th-${id}`;
5699
6019
  const divRef = useRef(null);
5700
6020
  useEffect(() => {
@@ -7630,7 +7950,7 @@ function OtpInput({
7630
7950
  emit(valid.join(""));
7631
7951
  focusBox(valid.length);
7632
7952
  };
7633
- return /* @__PURE__ */ jsx(Field, { className, label, htmlFor, errorId, errorMessage, required, layout, helperText, children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", role: "group", "aria-label": typeof label === "string" ? label : "One-time code", children: chars.map((char, idx) => /* @__PURE__ */ jsxs(React24.Fragment, { children: [
7953
+ return /* @__PURE__ */ jsx(Field, { className, label, htmlFor, errorId, errorMessage, required, layout, helperText, children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", role: "group", "aria-label": typeof label === "string" ? label : "One-time code", children: chars.map((char, idx) => /* @__PURE__ */ jsxs(React26.Fragment, { children: [
7634
7954
  /* @__PURE__ */ jsx(
7635
7955
  "input",
7636
7956
  {
@@ -8210,6 +8530,6 @@ function ColorPicker({
8210
8530
  ] });
8211
8531
  }
8212
8532
 
8213
- export { Accordion_default as Accordion, AppShell, AutoComplete, Avatar, Badge, Box, Breadcrumbs, Button_default as Button, CARD_BRANDS, Calendar2 as Calendar, Card_default as Card, CardCarousel, Cart, CartButton, CartProvider, Catalog, CatalogCarousel, CatalogGrid, Checkbox, Checkout, ColorPicker, ContextMenu, CreditCardForm, DateRangePicker, Drawer, Dropdown, EmptyCart, FAB, FadingBase, Field, FieldHelpIcon, FieldLabel, FileInput, Flex, Form, FormContext, FormField, FormStore, Grid2 as Grid, GridCard, icons_default as Icon, IconButton, Kbd, List2 as List, LoadingSpinner, MegaMenu_default as MegaMenu, Modal, NotificationProvider, NumberInput, OpaqueGridCard, OtpInput, Password, PopConfirm, Portal, RadioGroup, Rating, ScalableContainer, SearchInput_default as SearchInput, SegmentedControl, Sidebar, SkeletonBox, SkeletonCard, SkeletonCircle, SkeletonText, Slider, Statistic, Switch, Table, Tabs_default as Tabs, TagsInput, DatePicker as Temporal, TextArea, TextInput, ThemeProvider, ThemeSwitch, TimePicker, Tooltip, TooltipProvider, TopBar, Tree, TreeSelect, Typography, Wizard, cardNumberError, cvvError, detectBrand, expiryError, fieldShell, formatCardNumber, formatExpiry, isRequired, luhnValid, onlyDigits, patterns, runFieldRules, useCart, useFieldArray, useForm, useFormField, useFormStore, useNotification };
8533
+ export { Accordion_default as Accordion, AppShell, AutoComplete, Avatar, Badge, Box, Breadcrumbs, Button_default as Button, CARD_BRANDS, Calendar2 as Calendar, Card_default as Card, CardCarousel, Cart, CartButton, CartProvider, Catalog, CatalogCarousel, CatalogGrid, Checkbox, Checkout, ColorPicker, ContextMenu, CreditCardForm, DateRangePicker, Drawer, Dropdown, EmptyCart, FAB, FadingBase, Field, FieldHelpIcon, FieldLabel, FileInput, Flex, Form, FormContext, FormField, FormStore, Grid2 as Grid, GridCard, icons_default as Icon, IconButton, Kbd, List2 as List, LoadingSpinner, MegaMenu_default as MegaMenu, MenuButton, Modal, NotificationProvider, NumberInput, OpaqueGridCard, OtpInput, Password, PopConfirm, Portal, RadioGroup, Rating, ScalableContainer, SearchInput_default as SearchInput, SecureLayout, SegmentedControl, Sidebar, SkeletonBox, SkeletonCard, SkeletonCircle, SkeletonText, Slider, Statistic, Stepper, Switch, Table, Tabs_default as Tabs, TagsInput, DatePicker as Temporal, TextArea, TextInput, ThemeProvider, ThemeSwitch, TimePicker, Timeline, Tooltip, TooltipProvider, TopBar, Tree, TreeSelect, Typography, Wizard, cardNumberError, cvvError, detectBrand, expiryError, fieldShell, formatCardNumber, formatExpiry, isRequired, luhnValid, onlyDigits, patterns, runFieldRules, useCart, useFieldArray, useForm, useFormField, useFormStore, useNotification };
8214
8534
  //# sourceMappingURL=index.js.map
8215
8535
  //# sourceMappingURL=index.js.map