@astherix/ui 0.40.0 → 0.41.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
@@ -19,7 +19,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
19
19
  var ModalContext = React.createContext(null);
20
20
  function useModalContext(component) {
21
21
  const ctx = React.useContext(ModalContext);
22
- if (!ctx) throw new Error(`<${component}> must be used inside <Modal>.`);
22
+ if (!ctx) throw new Error(`<${component}> must be used inside <Modal> or <Drawer>.`);
23
23
  return ctx;
24
24
  }
25
25
  function Modal({ open: openProp, defaultOpen = false, onOpenChange, children }) {
@@ -153,10 +153,11 @@ var modalPanelVariants = cva(
153
153
  }
154
154
  );
155
155
  var XIcon = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: 1.7, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M5.5 5.5l9 9M14.5 5.5l-9 9" }) });
156
- var ModalContent = React.forwardRef(
156
+ var DialogSurface = React.forwardRef(
157
157
  ({
158
- className,
159
- size,
158
+ component,
159
+ dialogClassName,
160
+ panelClassName,
160
161
  backdrop = "default",
161
162
  dismissible = true,
162
163
  showCloseButton = true,
@@ -165,7 +166,7 @@ var ModalContent = React.forwardRef(
165
166
  "aria-label": ariaLabel,
166
167
  ...props
167
168
  }, ref) => {
168
- const { open, setOpen, titleId, descriptionId, hasDescription } = useModalContext("ModalContent");
169
+ const { open, setOpen, titleId, descriptionId, hasDescription } = useModalContext(component);
169
170
  const dialogRef = React.useRef(null);
170
171
  const returnFocusRef = React.useRef(null);
171
172
  const pressedBackdrop = React.useRef(false);
@@ -228,9 +229,8 @@ var ModalContent = React.forwardRef(
228
229
  "data-state": state,
229
230
  className: cn(
230
231
  // The dialog fills the viewport so clicks outside the panel land on it (= backdrop).
231
- "fixed inset-0 m-0 h-dvh max-h-none w-full max-w-none overflow-y-auto overscroll-contain",
232
- "bg-transparent p-4 text-fg sm:p-6 open:flex open:flex-col open:items-center",
233
- size === "full" && "p-0 sm:p-0",
232
+ "fixed inset-0 m-0 h-dvh max-h-none w-full max-w-none overscroll-contain bg-transparent text-fg",
233
+ dialogClassName,
234
234
  backdropClasses[backdrop],
235
235
  "data-[state=open]:backdrop:animate-[ui-fade-in_200ms_ease-out]",
236
236
  "data-[state=closing]:backdrop:animate-[ui-fade-out_140ms_ease-in_forwards]"
@@ -253,24 +253,30 @@ var ModalContent = React.forwardRef(
253
253
  if (dismissible && pressedBackdrop.current && event.target === event.currentTarget) setOpen(false);
254
254
  pressedBackdrop.current = false;
255
255
  },
256
- children: (state === "open" || state === "closing") && /* @__PURE__ */ jsxs(
257
- "div",
258
- {
259
- ref: setPanelRef,
260
- tabIndex: -1,
261
- "data-state": state,
262
- className: cn(modalPanelVariants({ size }), className),
263
- ...props,
264
- children: [
265
- children,
266
- showCloseButton && dismissible && /* @__PURE__ */ jsx("div", { className: "absolute right-3 top-3", children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", iconOnly: true, "aria-label": closeLabel, onClick: () => setOpen(false), children: /* @__PURE__ */ jsx(XIcon, {}) }) })
267
- ]
268
- }
269
- )
256
+ children: (state === "open" || state === "closing") && /* @__PURE__ */ jsxs("div", { ref: setPanelRef, tabIndex: -1, "data-state": state, className: panelClassName, ...props, children: [
257
+ children,
258
+ showCloseButton && dismissible && /* @__PURE__ */ jsx("div", { className: "absolute right-3 top-3", children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", iconOnly: true, "aria-label": closeLabel, onClick: () => setOpen(false), children: /* @__PURE__ */ jsx(XIcon, {}) }) })
259
+ ] })
270
260
  }
271
261
  );
272
262
  }
273
263
  );
264
+ DialogSurface.displayName = "DialogSurface";
265
+ var ModalContent = React.forwardRef(
266
+ ({ className, size, ...props }, ref) => /* @__PURE__ */ jsx(
267
+ DialogSurface,
268
+ {
269
+ ref,
270
+ component: "ModalContent",
271
+ dialogClassName: cn(
272
+ "overflow-y-auto p-4 sm:p-6 open:flex open:flex-col open:items-center",
273
+ size === "full" && "p-0 sm:p-0"
274
+ ),
275
+ panelClassName: cn(modalPanelVariants({ size }), className),
276
+ ...props
277
+ }
278
+ )
279
+ );
274
280
  ModalContent.displayName = "ModalContent";
275
281
  var ModalHeader = React.forwardRef(
276
282
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("grid gap-1.5 px-6 pb-2 pr-14 pt-6", className), ...props })
@@ -3937,7 +3943,7 @@ function DataTable({
3937
3943
  onQueryChange,
3938
3944
  className
3939
3945
  }) {
3940
- const keyOf2 = React12.useCallback(
3946
+ const keyOf3 = React12.useCallback(
3941
3947
  (row) => typeof rowKey === "function" ? rowKey(row) : row[rowKey],
3942
3948
  [rowKey]
3943
3949
  );
@@ -3996,10 +4002,10 @@ function DataTable({
3996
4002
  setSort((s) => !s || s.key !== key ? { key, direction: "asc" } : s.direction === "asc" ? { key, direction: "desc" } : null);
3997
4003
  setPage(1);
3998
4004
  };
3999
- const pageKeys = paged.map(keyOf2);
4005
+ const pageKeys = paged.map(keyOf3);
4000
4006
  const selectedOnPage = pageKeys.filter((k) => selected.includes(k));
4001
4007
  const allOnPage = pageKeys.length > 0 && selectedOnPage.length === pageKeys.length;
4002
- const selectedRows = data.filter((r) => selected.includes(keyOf2(r)));
4008
+ const selectedRows = data.filter((r) => selected.includes(keyOf3(r)));
4003
4009
  const refreshing = loading && paged.length > 0 || refreshingByButton;
4004
4010
  const [justRefreshed, setJustRefreshed] = React12.useState(false);
4005
4011
  const wasRefreshing = React12.useRef(false);
@@ -4225,7 +4231,7 @@ function DataTable({
4225
4231
  ] }) }),
4226
4232
  /* @__PURE__ */ jsxs12("tbody", { role: "rowgroup", className: cn(cardBody), children: [
4227
4233
  loading && paged.length === 0 ? Array.from({ length: Math.min(pageSize || 5, 5) }, (_, i) => /* @__PURE__ */ jsx12("tr", { role: "row", className: cn(cardRow), children: Array.from({ length: colCount }, (_2, j) => /* @__PURE__ */ jsx12("td", { role: "cell", className: cn("border-b border-border", pad, cards && "@max-[40rem]:border-0 @max-[40rem]:p-0"), children: /* @__PURE__ */ jsx12(Skeleton, { className: "h-3.5 w-full max-w-32 rounded-full" }) }, j)) }, `sk-${i}`)) : paged.map((row, ri) => {
4228
- const k = keyOf2(row);
4234
+ const k = keyOf3(row);
4229
4235
  const isSel = selected.includes(k);
4230
4236
  return /* @__PURE__ */ jsxs12(
4231
4237
  "tr",
@@ -10409,6 +10415,868 @@ function HeroMedia({ frame = false, url, float = false, tilt = false, className,
10409
10415
  }
10410
10416
  );
10411
10417
  }
10418
+
10419
+ // src/components/drawer/Drawer.tsx
10420
+ import * as React35 from "react";
10421
+ import { jsx as jsx34 } from "react/jsx-runtime";
10422
+ var Drawer = Modal;
10423
+ var DrawerTrigger = ModalTrigger;
10424
+ var DrawerClose = ModalClose;
10425
+ var DrawerTitle = ModalTitle;
10426
+ var DrawerDescription = ModalDescription;
10427
+ function useDrawer(initialOpen = false) {
10428
+ const { modalProps, ...rest } = useModal(initialOpen);
10429
+ return { ...rest, drawerProps: modalProps };
10430
+ }
10431
+ var placement = {
10432
+ left: "open:justify-start",
10433
+ right: "open:justify-end",
10434
+ top: "open:flex-col open:justify-start",
10435
+ bottom: "open:flex-col open:justify-end"
10436
+ };
10437
+ var panelSide = {
10438
+ left: "h-full border-r [--ui-drawer-from:translateX(-100%)]",
10439
+ right: "h-full border-l [--ui-drawer-from:translateX(100%)]",
10440
+ top: "w-full border-b [--ui-drawer-from:translateY(-100%)]",
10441
+ bottom: "w-full border-t rounded-t-modal [--ui-drawer-from:translateY(100%)]"
10442
+ };
10443
+ var reach = {
10444
+ x: { sm: "w-[min(18rem,calc(100%-3rem))]", md: "w-[min(24rem,calc(100%-3rem))]", lg: "w-[min(36rem,calc(100%-3rem))]" },
10445
+ y: { sm: "max-h-[40dvh]", md: "max-h-[65dvh]", lg: "max-h-[85dvh]" }
10446
+ };
10447
+ var DrawerContent = React35.forwardRef(
10448
+ ({ side = "right", size = "md", className, ...props }, ref) => /* @__PURE__ */ jsx34(
10449
+ DialogSurface,
10450
+ {
10451
+ ref,
10452
+ component: "DrawerContent",
10453
+ dialogClassName: cn("overflow-hidden p-0 open:flex", placement[side]),
10454
+ panelClassName: cn(
10455
+ "relative flex flex-col overflow-y-auto overscroll-contain border-border bg-surface text-fg outline-none",
10456
+ "shadow-[var(--ui-shadow-modal)]",
10457
+ panelSide[side],
10458
+ reach[side === "left" || side === "right" ? "x" : "y"][size],
10459
+ "data-[state=open]:animate-[ui-drawer-in_260ms_cubic-bezier(0.2,0.9,0.3,1)]",
10460
+ "data-[state=closing]:animate-[ui-drawer-out_140ms_ease-in_forwards]",
10461
+ "motion-reduce:data-[state=open]:animate-[ui-fade-in_140ms_ease-out]",
10462
+ "motion-reduce:data-[state=closing]:animate-[ui-fade-out_140ms_ease-in_forwards]",
10463
+ // Keep content clear of notches and the home indicator.
10464
+ "pb-[env(safe-area-inset-bottom)]",
10465
+ className
10466
+ ),
10467
+ ...props
10468
+ }
10469
+ )
10470
+ );
10471
+ DrawerContent.displayName = "DrawerContent";
10472
+ var DrawerHeader = React35.forwardRef(
10473
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx34("div", { ref, className: cn("grid gap-1.5 px-5 pb-2 pr-14 pt-5", className), ...props })
10474
+ );
10475
+ DrawerHeader.displayName = "DrawerHeader";
10476
+ var DrawerBody = React35.forwardRef(
10477
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx34("div", { ref, className: cn("flex-1 px-5 py-3 text-[0.9375rem] leading-relaxed", className), ...props })
10478
+ );
10479
+ DrawerBody.displayName = "DrawerBody";
10480
+ var DrawerFooter = React35.forwardRef(
10481
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx34(
10482
+ "div",
10483
+ {
10484
+ ref,
10485
+ className: cn("mt-auto flex flex-col-reverse gap-2 border-t border-border px-5 py-4 sm:flex-row sm:justify-end", className),
10486
+ ...props
10487
+ }
10488
+ )
10489
+ );
10490
+ DrawerFooter.displayName = "DrawerFooter";
10491
+
10492
+ // src/components/tooltip/Tooltip.tsx
10493
+ import * as React36 from "react";
10494
+ import { Slot as Slot6 } from "@radix-ui/react-slot";
10495
+ import { Fragment as Fragment20, jsx as jsx35, jsxs as jsxs34 } from "react/jsx-runtime";
10496
+ var openCount = 0;
10497
+ var lastHiddenAt = 0;
10498
+ var closeCurrent = null;
10499
+ var WARM_MS = 500;
10500
+ var GAP2 = 6;
10501
+ var supportsPopover = () => typeof HTMLElement !== "undefined" && "showPopover" in HTMLElement.prototype;
10502
+ function Tooltip2({
10503
+ content,
10504
+ children,
10505
+ side = "top",
10506
+ align = "center",
10507
+ delay = 400,
10508
+ disabled = false,
10509
+ as = "description",
10510
+ className
10511
+ }) {
10512
+ const id = React36.useId();
10513
+ const triggerRef = React36.useRef(null);
10514
+ const tipRef = React36.useRef(null);
10515
+ const timers = React36.useRef({ show: 0, hide: 0 });
10516
+ const [open, setOpen] = React36.useState(false);
10517
+ const [placed, setPlaced] = React36.useState({
10518
+ side,
10519
+ style: { visibility: "hidden" }
10520
+ });
10521
+ const clear = () => {
10522
+ window.clearTimeout(timers.current.show);
10523
+ window.clearTimeout(timers.current.hide);
10524
+ };
10525
+ const show = (wait) => {
10526
+ if (disabled) return;
10527
+ clear();
10528
+ const instant = wait === 0 || openCount > 0 || Date.now() - lastHiddenAt < WARM_MS;
10529
+ if (instant) setOpen(true);
10530
+ else timers.current.show = window.setTimeout(() => setOpen(true), wait);
10531
+ };
10532
+ const hide = (wait = 0) => {
10533
+ clear();
10534
+ const done = () => setOpen(false);
10535
+ if (wait) timers.current.hide = window.setTimeout(done, wait);
10536
+ else done();
10537
+ };
10538
+ React36.useEffect(() => () => clear(), []);
10539
+ React36.useEffect(() => {
10540
+ if (!open) return;
10541
+ const close = () => hide();
10542
+ if (closeCurrent && closeCurrent !== close) closeCurrent();
10543
+ closeCurrent = close;
10544
+ openCount++;
10545
+ return () => {
10546
+ openCount--;
10547
+ lastHiddenAt = Date.now();
10548
+ if (closeCurrent === close) closeCurrent = null;
10549
+ };
10550
+ }, [open]);
10551
+ React36.useEffect(() => {
10552
+ if (disabled) hide();
10553
+ }, [disabled]);
10554
+ const place = React36.useCallback(() => {
10555
+ const t = triggerRef.current;
10556
+ const tip = tipRef.current;
10557
+ if (!t || !tip) return;
10558
+ const r = t.getBoundingClientRect();
10559
+ const w = tip.offsetWidth;
10560
+ const h = tip.offsetHeight;
10561
+ const vw = window.innerWidth;
10562
+ const vh = window.innerHeight;
10563
+ const room = { top: r.top, bottom: vh - r.bottom, left: r.left, right: vw - r.right };
10564
+ const need = side === "top" || side === "bottom" ? h + GAP2 : w + GAP2;
10565
+ const opposite = { top: "bottom", bottom: "top", left: "right", right: "left" };
10566
+ const s = room[side] >= need || room[side] >= room[opposite[side]] ? side : opposite[side];
10567
+ let left;
10568
+ let top;
10569
+ if (s === "top" || s === "bottom") {
10570
+ top = s === "top" ? r.top - h - GAP2 : r.bottom + GAP2;
10571
+ left = align === "start" ? r.left : align === "end" ? r.right - w : r.left + r.width / 2 - w / 2;
10572
+ } else {
10573
+ left = s === "left" ? r.left - w - GAP2 : r.right + GAP2;
10574
+ top = align === "start" ? r.top : align === "end" ? r.bottom - h : r.top + r.height / 2 - h / 2;
10575
+ }
10576
+ left = Math.max(8, Math.min(left, vw - w - 8));
10577
+ top = Math.max(8, Math.min(top, vh - h - 8));
10578
+ setPlaced({ side: s, style: { left, top } });
10579
+ }, [side, align]);
10580
+ React36.useLayoutEffect(() => {
10581
+ const tip = tipRef.current;
10582
+ if (!tip) return;
10583
+ if (!open) {
10584
+ if (supportsPopover() && tip.matches(":popover-open")) tip.hidePopover();
10585
+ setPlaced((p) => ({ ...p, style: { visibility: "hidden" } }));
10586
+ return;
10587
+ }
10588
+ if (supportsPopover() && !tip.matches(":popover-open")) tip.showPopover();
10589
+ place();
10590
+ const onKey = (e) => {
10591
+ if (e.key === "Escape") hide();
10592
+ };
10593
+ window.addEventListener("resize", place);
10594
+ window.addEventListener("scroll", place, true);
10595
+ document.addEventListener("keydown", onKey, true);
10596
+ return () => {
10597
+ window.removeEventListener("resize", place);
10598
+ window.removeEventListener("scroll", place, true);
10599
+ document.removeEventListener("keydown", onKey, true);
10600
+ };
10601
+ }, [open, place]);
10602
+ const childProps = children.props;
10603
+ const ariaKey = as === "label" ? "aria-labelledby" : "aria-describedby";
10604
+ const ariaValue = [childProps[ariaKey], `${id}-tip`].filter(Boolean).join(" ");
10605
+ const fromBySide = {
10606
+ top: "translateY(3px)",
10607
+ bottom: "translateY(-3px)",
10608
+ left: "translateX(3px)",
10609
+ right: "translateX(-3px)"
10610
+ };
10611
+ return /* @__PURE__ */ jsxs34(Fragment20, { children: [
10612
+ /* @__PURE__ */ jsx35(
10613
+ Slot6,
10614
+ {
10615
+ ref: triggerRef,
10616
+ ...{ [ariaKey]: disabled && as === "description" ? childProps[ariaKey] : ariaValue },
10617
+ onPointerEnter: (e) => {
10618
+ if (e.pointerType !== "touch") show(delay);
10619
+ },
10620
+ onPointerLeave: (e) => {
10621
+ if (e.pointerType !== "touch") hide(100);
10622
+ },
10623
+ onPointerDown: () => hide(),
10624
+ onFocus: (e) => {
10625
+ if (e.currentTarget.matches(":focus-visible")) show(0);
10626
+ },
10627
+ onBlur: () => hide(),
10628
+ children
10629
+ }
10630
+ ),
10631
+ /* @__PURE__ */ jsx35(
10632
+ "div",
10633
+ {
10634
+ ref: tipRef,
10635
+ id: `${id}-tip`,
10636
+ role: "tooltip",
10637
+ popover: supportsPopover() ? "manual" : void 0,
10638
+ hidden: !supportsPopover() && !open ? true : void 0,
10639
+ "data-side": placed.side,
10640
+ "data-state": open ? "open" : "closed",
10641
+ onPointerEnter: () => open && clear(),
10642
+ onPointerLeave: () => hide(100),
10643
+ style: { ...placed.style, ["--ui-tooltip-from"]: fromBySide[placed.side] },
10644
+ className: cn(
10645
+ // Undo the browser's popover box (centered, bordered) and place it ourselves.
10646
+ "fixed inset-auto m-0 overflow-visible border-0 p-0",
10647
+ "z-[70] max-w-[min(18rem,calc(100vw-1rem))] rounded-control bg-ash-900 px-2.5 py-1.5 text-[0.8125rem] font-medium leading-snug text-white",
10648
+ "shadow-[var(--ui-shadow-md)] dark:bg-ash-50 dark:text-ash-950",
10649
+ "data-[state=open]:animate-[ui-tooltip-in_120ms_ease-out] motion-reduce:data-[state=open]:animate-[ui-fade-in_120ms_ease-out]",
10650
+ className
10651
+ ),
10652
+ children: content
10653
+ }
10654
+ )
10655
+ ] });
10656
+ }
10657
+
10658
+ // src/components/input/OtpInput.tsx
10659
+ import * as React37 from "react";
10660
+ import { jsx as jsx36, jsxs as jsxs35 } from "react/jsx-runtime";
10661
+ var boxSize = {
10662
+ sm: "h-8 w-7 text-sm",
10663
+ md: "h-10 w-9 text-base",
10664
+ lg: "h-12 w-11 text-lg"
10665
+ };
10666
+ var allowPattern = (allow) => allow instanceof RegExp ? allow : allow === "alphanumeric" ? /[0-9a-zA-Z]/ : /[0-9]/;
10667
+ var OtpInput = React37.forwardRef(function OtpInput2({
10668
+ length = 6,
10669
+ value: valueProp,
10670
+ defaultValue = "",
10671
+ onValueChange,
10672
+ onComplete,
10673
+ allow = "digits",
10674
+ groupSize,
10675
+ mask = false,
10676
+ size = "md",
10677
+ invalid: invalidProp,
10678
+ id: idProp,
10679
+ required: requiredProp,
10680
+ disabled: disabledProp,
10681
+ "aria-describedby": describedBy,
10682
+ "aria-label": ariaLabel,
10683
+ autoComplete = "one-time-code",
10684
+ frameClassName,
10685
+ className,
10686
+ onFocus,
10687
+ onBlur,
10688
+ onKeyDown,
10689
+ ...props
10690
+ }, ref) {
10691
+ const control = useFieldControlProps({
10692
+ id: idProp,
10693
+ invalid: invalidProp,
10694
+ required: requiredProp,
10695
+ disabled: disabledProp,
10696
+ "aria-describedby": describedBy
10697
+ });
10698
+ const inputRef = React37.useRef(null);
10699
+ const [inner, setInner] = React37.useState(() => defaultValue.slice(0, length));
10700
+ const value = (valueProp ?? inner).slice(0, length);
10701
+ const [focused, setFocused] = React37.useState(false);
10702
+ const pattern = allowPattern(allow);
10703
+ const commit = (next) => {
10704
+ if (valueProp === void 0) setInner(next);
10705
+ onValueChange?.(next);
10706
+ if (next.length === length && value.length !== length) onComplete?.(next);
10707
+ };
10708
+ React37.useEffect(() => {
10709
+ const form = inputRef.current?.form;
10710
+ if (!form) return;
10711
+ const onReset = () => {
10712
+ if (valueProp === void 0) setInner(defaultValue.slice(0, length));
10713
+ onValueChange?.(defaultValue.slice(0, length));
10714
+ };
10715
+ form.addEventListener("reset", onReset);
10716
+ return () => form.removeEventListener("reset", onReset);
10717
+ });
10718
+ const caretToEnd = () => {
10719
+ const el = inputRef.current;
10720
+ if (el) requestAnimationFrame(() => el.setSelectionRange(el.value.length, el.value.length));
10721
+ };
10722
+ const active = focused ? Math.min(value.length, length - 1) : -1;
10723
+ const full = value.length === length;
10724
+ return /* @__PURE__ */ jsxs35(
10725
+ "div",
10726
+ {
10727
+ className: cn("relative inline-flex items-center gap-1.5", control.disabled && "opacity-70", frameClassName),
10728
+ onPointerDown: (e) => {
10729
+ if (e.target !== inputRef.current) {
10730
+ e.preventDefault();
10731
+ inputRef.current?.focus();
10732
+ }
10733
+ },
10734
+ children: [
10735
+ Array.from({ length }, (_, i) => {
10736
+ const char = value[i];
10737
+ const isActive2 = i === active;
10738
+ return /* @__PURE__ */ jsxs35(React37.Fragment, { children: [
10739
+ groupSize && i > 0 && i % groupSize === 0 && /* @__PURE__ */ jsx36("span", { "aria-hidden": "true", className: "mx-0.5 h-px w-2.5 bg-fg-muted/70" }),
10740
+ /* @__PURE__ */ jsx36(
10741
+ "div",
10742
+ {
10743
+ "aria-hidden": "true",
10744
+ "data-active": isActive2 || void 0,
10745
+ "data-filled": char !== void 0 || void 0,
10746
+ className: cn(
10747
+ "relative grid shrink-0 place-items-center rounded-control border border-border-strong bg-surface font-medium tabular-nums text-fg",
10748
+ "shadow-[inset_0_1px_2px_rgb(var(--ui-shadow-color)/0.07)] transition-[border-color,box-shadow] duration-100",
10749
+ "data-[active]:border-ring data-[active]:ring-3 data-[active]:ring-ring/25",
10750
+ control.invalid && "border-danger data-[active]:border-danger data-[active]:ring-danger/25",
10751
+ control.disabled && "cursor-not-allowed bg-secondary-hover",
10752
+ "motion-reduce:transition-none",
10753
+ boxSize[size]
10754
+ ),
10755
+ children: char !== void 0 ? mask ? /* @__PURE__ */ jsx36("span", { className: "size-2 rounded-full bg-fg" }) : char : isActive2 && !full ? /* @__PURE__ */ jsx36("span", { className: "h-[1.1em] w-px animate-[ui-caret_1s_steps(1)_infinite] bg-fg motion-reduce:animate-none" }) : null
10756
+ }
10757
+ )
10758
+ ] }, i);
10759
+ }),
10760
+ /* @__PURE__ */ jsx36(
10761
+ "input",
10762
+ {
10763
+ ref: (node) => {
10764
+ inputRef.current = node;
10765
+ if (typeof ref === "function") ref(node);
10766
+ else if (ref) ref.current = node;
10767
+ },
10768
+ type: "text",
10769
+ inputMode: allow === "digits" ? "numeric" : "text",
10770
+ autoComplete,
10771
+ autoCapitalize: "off",
10772
+ autoCorrect: "off",
10773
+ spellCheck: false,
10774
+ maxLength: length,
10775
+ value,
10776
+ "aria-label": ariaLabel ?? (control.id ? void 0 : `Verification code, ${length} characters`),
10777
+ id: control.id,
10778
+ required: control.required,
10779
+ disabled: control.disabled,
10780
+ "aria-invalid": control["aria-invalid"],
10781
+ "aria-describedby": control["aria-describedby"],
10782
+ className: cn(
10783
+ // Invisible but real: it takes the taps, typing, pastes and autofill.
10784
+ "absolute inset-0 h-full w-full cursor-text bg-transparent text-transparent caret-transparent outline-none",
10785
+ "selection:bg-transparent [-webkit-text-fill-color:transparent] disabled:cursor-not-allowed",
10786
+ className
10787
+ ),
10788
+ onChange: (e) => {
10789
+ const next = Array.from(e.target.value).filter((c) => pattern.test(c)).join("").slice(0, length);
10790
+ commit(allow === "alphanumeric" ? next.toUpperCase() : next);
10791
+ },
10792
+ onFocus: (e) => {
10793
+ setFocused(true);
10794
+ caretToEnd();
10795
+ onFocus?.(e);
10796
+ },
10797
+ onBlur: (e) => {
10798
+ setFocused(false);
10799
+ onBlur?.(e);
10800
+ },
10801
+ onKeyDown: (e) => {
10802
+ onKeyDown?.(e);
10803
+ if (["ArrowLeft", "ArrowRight", "Home", "End", "ArrowUp", "ArrowDown"].includes(e.key)) e.preventDefault();
10804
+ },
10805
+ onSelect: caretToEnd,
10806
+ ...props
10807
+ }
10808
+ )
10809
+ ]
10810
+ }
10811
+ );
10812
+ });
10813
+
10814
+ // src/components/breadcrumbs/Breadcrumbs.tsx
10815
+ import * as React38 from "react";
10816
+ import { Fragment as Fragment23, jsx as jsx37, jsxs as jsxs36 } from "react/jsx-runtime";
10817
+ var Chevron4 = () => /* @__PURE__ */ jsx37("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.6, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", className: "size-3.5 rtl:-scale-x-100", children: /* @__PURE__ */ jsx37("path", { d: "M6 3.5l4.5 4.5L6 12.5" }) });
10818
+ function Breadcrumbs({
10819
+ items,
10820
+ maxItems = 4,
10821
+ separator = "chevron",
10822
+ linkComponent: LinkC = "a",
10823
+ size = "sm",
10824
+ className,
10825
+ "aria-label": ariaLabel = "Breadcrumb",
10826
+ ...props
10827
+ }) {
10828
+ const [expanded, setExpanded] = React38.useState(false);
10829
+ const firstHiddenRef = React38.useRef(null);
10830
+ const fold = !expanded && maxItems > 0 && items.length > maxItems;
10831
+ const tail = Math.max(1, maxItems - 2);
10832
+ const hiddenFrom = 1;
10833
+ const hiddenTo = items.length - tail;
10834
+ React38.useEffect(() => {
10835
+ if (expanded) firstHiddenRef.current?.focus();
10836
+ }, [expanded]);
10837
+ const sep = /* @__PURE__ */ jsx37("li", { role: "presentation", "aria-hidden": "true", className: "flex shrink-0 items-center text-fg-muted/70", children: separator === "chevron" ? /* @__PURE__ */ jsx37(Chevron4, {}) : separator === "slash" ? /* @__PURE__ */ jsx37("span", { className: "px-0.5", children: "/" }) : separator });
10838
+ const crumb = (item, i) => {
10839
+ const current = i === items.length - 1;
10840
+ const content = /* @__PURE__ */ jsxs36(Fragment23, { children: [
10841
+ renderIcon(item.icon),
10842
+ /* @__PURE__ */ jsx37("span", { className: "truncate", children: item.label })
10843
+ ] });
10844
+ const base = "inline-flex min-w-0 max-w-[16rem] items-center gap-1.5 rounded-control-sm [&_svg]:size-[1.1em] [&_svg]:shrink-0";
10845
+ return /* @__PURE__ */ jsx37("li", { className: "flex min-w-0 items-center", children: current || !item.href ? /* @__PURE__ */ jsx37("span", { "aria-current": current ? "page" : void 0, title: item.label, className: cn(base, current ? "font-medium text-fg" : "text-fg-muted"), children: content }) : /* @__PURE__ */ jsx37(
10846
+ LinkC,
10847
+ {
10848
+ ref: i === hiddenFrom && expanded ? firstHiddenRef : void 0,
10849
+ href: item.href,
10850
+ title: item.label,
10851
+ className: cn(
10852
+ base,
10853
+ "text-fg-muted underline-offset-4 transition-colors hover:text-fg hover:underline",
10854
+ "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
10855
+ ),
10856
+ children: content
10857
+ }
10858
+ ) }, `${item.label}-${i}`);
10859
+ };
10860
+ const shown = [];
10861
+ items.forEach((item, i) => {
10862
+ if (fold && i >= hiddenFrom && i < hiddenTo) {
10863
+ if (i === hiddenFrom) {
10864
+ shown.push(sep);
10865
+ shown.push(
10866
+ /* @__PURE__ */ jsx37("li", { className: "flex items-center", children: /* @__PURE__ */ jsx37(
10867
+ "button",
10868
+ {
10869
+ type: "button",
10870
+ onClick: () => setExpanded(true),
10871
+ "aria-label": `Show ${hiddenTo - hiddenFrom} more`,
10872
+ className: "grid h-6 min-w-6 cursor-pointer place-items-center rounded-control-sm px-1 text-fg-muted hover:bg-secondary-hover hover:text-fg focus-visible:outline-2 focus-visible:outline-ring",
10873
+ children: "\u2026"
10874
+ }
10875
+ ) }, "more")
10876
+ );
10877
+ }
10878
+ return;
10879
+ }
10880
+ if (i > 0) shown.push(sep);
10881
+ shown.push(crumb(item, i));
10882
+ });
10883
+ return /* @__PURE__ */ jsx37("nav", { "aria-label": ariaLabel, className: cn("min-w-0", className), ...props, children: /* @__PURE__ */ jsx37(
10884
+ "ol",
10885
+ {
10886
+ className: cn(
10887
+ "flex min-w-0 flex-wrap items-center gap-x-1.5 gap-y-1",
10888
+ size === "sm" ? "text-sm" : "text-[0.9375rem]"
10889
+ ),
10890
+ children: shown.map((node, i) => /* @__PURE__ */ jsx37(React38.Fragment, { children: node }, i))
10891
+ }
10892
+ ) });
10893
+ }
10894
+
10895
+ // src/components/navigation/NavigationMenu.tsx
10896
+ import * as React39 from "react";
10897
+ import { jsx as jsx38, jsxs as jsxs37 } from "react/jsx-runtime";
10898
+ var keyOf2 = (item, i) => item.id ?? `${item.label}-${i}`;
10899
+ function matches2(href, active, nested) {
10900
+ if (!href || !active) return false;
10901
+ if (href === active) return true;
10902
+ return nested && href !== "/" && active.startsWith(href.endsWith("/") ? href : `${href}/`);
10903
+ }
10904
+ function bestMatch(items, active, nested) {
10905
+ let found;
10906
+ const walk = (list) => list.forEach((it) => {
10907
+ if (matches2(it.href, active, nested) && (!found || (it.href?.length ?? 0) > found.length)) found = it.href;
10908
+ if (it.children) walk(it.children);
10909
+ });
10910
+ walk(items);
10911
+ return found;
10912
+ }
10913
+ var ExternalIcon = () => /* @__PURE__ */ jsx38("svg", { viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.4, strokeLinecap: "round", "aria-hidden": "true", className: "size-3 shrink-0 opacity-60", children: /* @__PURE__ */ jsx38("path", { d: "M4 2.5h5.5V8M9.5 2.5L3 9" }) });
10914
+ var Chevron5 = ({ open, vertical }) => /* @__PURE__ */ jsx38(
10915
+ "svg",
10916
+ {
10917
+ viewBox: "0 0 16 16",
10918
+ fill: "none",
10919
+ stroke: "currentColor",
10920
+ strokeWidth: 1.7,
10921
+ strokeLinecap: "round",
10922
+ strokeLinejoin: "round",
10923
+ "aria-hidden": "true",
10924
+ className: cn(
10925
+ "size-3.5 shrink-0 opacity-70 transition-transform duration-200 motion-reduce:transition-none",
10926
+ open && (vertical ? "rotate-90" : "rotate-180"),
10927
+ vertical && !open && "rtl:-scale-x-100"
10928
+ ),
10929
+ children: /* @__PURE__ */ jsx38("path", { d: vertical ? "M6 3.5l4.5 4.5L6 12.5" : "M3.5 6l4.5 4.5L12.5 6" })
10930
+ }
10931
+ );
10932
+ function ItemLink2({
10933
+ item,
10934
+ ctx,
10935
+ className,
10936
+ children,
10937
+ ...rest
10938
+ }) {
10939
+ const Comp = item.external ? "a" : ctx.linkComponent;
10940
+ const active = !!item.href && item.href === ctx.best;
10941
+ return /* @__PURE__ */ jsx38(
10942
+ Comp,
10943
+ {
10944
+ href: item.href,
10945
+ "aria-current": active ? "page" : void 0,
10946
+ "data-active": active || void 0,
10947
+ ...item.external ? { target: "_blank", rel: "noopener noreferrer" } : {},
10948
+ className,
10949
+ onClick: () => {
10950
+ ctx.close();
10951
+ if (item.href) ctx.onNavigate?.(item.href);
10952
+ },
10953
+ ...rest,
10954
+ children
10955
+ }
10956
+ );
10957
+ }
10958
+ var focusRing = "outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring";
10959
+ function NavigationMenu({
10960
+ items,
10961
+ activeHref,
10962
+ matchNested = true,
10963
+ orientation = "horizontal",
10964
+ indicator = "pill",
10965
+ linkComponent = "a",
10966
+ onNavigate,
10967
+ "aria-label": ariaLabel = "Main",
10968
+ className
10969
+ }) {
10970
+ const vertical = orientation === "vertical";
10971
+ const best = React39.useMemo(() => bestMatch(items, activeHref, matchNested), [items, activeHref, matchNested]);
10972
+ const [openKey, setOpenKey] = React39.useState(null);
10973
+ const navRef = React39.useRef(null);
10974
+ const hoverTimer = React39.useRef(0);
10975
+ const focusFirstLink = React39.useRef(false);
10976
+ const id = React39.useId();
10977
+ const ctx = { linkComponent, best, onNavigate, close: () => !vertical && setOpenKey(null) };
10978
+ React39.useEffect(() => {
10979
+ if (vertical || openKey === null) return;
10980
+ const onDown = (e) => {
10981
+ if (!navRef.current?.contains(e.target)) setOpenKey(null);
10982
+ };
10983
+ document.addEventListener("pointerdown", onDown);
10984
+ return () => document.removeEventListener("pointerdown", onDown);
10985
+ }, [vertical, openKey]);
10986
+ React39.useEffect(() => () => window.clearTimeout(hoverTimer.current), []);
10987
+ React39.useEffect(() => {
10988
+ if (!focusFirstLink.current || openKey === null) return;
10989
+ focusFirstLink.current = false;
10990
+ document.getElementById(`${id}-${openKey}`)?.querySelector("a,button")?.focus();
10991
+ }, [openKey, id]);
10992
+ React39.useLayoutEffect(() => {
10993
+ if (vertical || openKey === null) return;
10994
+ const panel = document.getElementById(`${id}-${openKey}`);
10995
+ if (!panel) return;
10996
+ const fit = () => {
10997
+ panel.style.translate = "";
10998
+ const r = panel.getBoundingClientRect();
10999
+ const vw = document.documentElement.clientWidth;
11000
+ let shift = 0;
11001
+ if (r.right > vw - 8) shift = vw - 8 - r.right;
11002
+ if (r.left + shift < 8) shift = 8 - r.left;
11003
+ if (shift) panel.style.translate = `${shift}px 0`;
11004
+ };
11005
+ fit();
11006
+ window.addEventListener("resize", fit);
11007
+ return () => window.removeEventListener("resize", fit);
11008
+ }, [vertical, openKey, id]);
11009
+ const triggers = () => Array.from(navRef.current?.querySelectorAll("[data-nav-top]") ?? []);
11010
+ const onTopKeyDown = (e, key, hasPanel) => {
11011
+ if (vertical) return;
11012
+ const list = triggers();
11013
+ const i = list.indexOf(e.currentTarget);
11014
+ const rtl = getComputedStyle(e.currentTarget).direction === "rtl";
11015
+ const next = rtl ? "ArrowLeft" : "ArrowRight";
11016
+ const prev = rtl ? "ArrowRight" : "ArrowLeft";
11017
+ if (e.key === "Escape" && openKey !== null) {
11018
+ e.preventDefault();
11019
+ setOpenKey(null);
11020
+ } else if (e.key === next || e.key === prev) {
11021
+ e.preventDefault();
11022
+ const target = list[(i + (e.key === next ? 1 : -1) + list.length) % list.length];
11023
+ target?.focus();
11024
+ if (openKey !== null) setOpenKey(null);
11025
+ } else if (e.key === "Home" || e.key === "End") {
11026
+ e.preventDefault();
11027
+ list[e.key === "Home" ? 0 : list.length - 1]?.focus();
11028
+ } else if (e.key === "ArrowDown" && hasPanel) {
11029
+ e.preventDefault();
11030
+ if (openKey === key) {
11031
+ document.getElementById(`${id}-${key}`)?.querySelector("a,button")?.focus();
11032
+ } else {
11033
+ focusFirstLink.current = true;
11034
+ setOpenKey(key);
11035
+ }
11036
+ }
11037
+ };
11038
+ const onPanelKeyDown = (e, key) => {
11039
+ const links = Array.from(e.currentTarget.querySelectorAll("a,button"));
11040
+ const i = links.indexOf(document.activeElement);
11041
+ if (e.key === "Escape") {
11042
+ e.preventDefault();
11043
+ e.stopPropagation();
11044
+ setOpenKey(null);
11045
+ navRef.current?.querySelector(`[data-nav-key="${CSS.escape(key)}"]`)?.focus();
11046
+ } else if (e.key === "ArrowDown" || e.key === "ArrowUp") {
11047
+ e.preventDefault();
11048
+ links[(i + (e.key === "ArrowDown" ? 1 : -1) + links.length) % links.length]?.focus();
11049
+ }
11050
+ };
11051
+ const hoverOpen = (key, e) => {
11052
+ if (vertical || e.pointerType !== "mouse") return;
11053
+ window.clearTimeout(hoverTimer.current);
11054
+ hoverTimer.current = window.setTimeout(() => setOpenKey(key), key === null ? 220 : openKey === null ? 120 : 0);
11055
+ };
11056
+ const topItemClass = cn(
11057
+ "relative inline-flex h-9 shrink-0 cursor-pointer items-center gap-2 whitespace-nowrap rounded-control px-3 text-sm font-medium text-fg-muted transition-colors duration-100",
11058
+ "hover:text-fg [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0",
11059
+ focusRing,
11060
+ indicator === "pill" ? "hover:bg-secondary-hover data-[active]:bg-secondary-hover data-[active]:text-fg" : "data-[active]:text-fg"
11061
+ );
11062
+ const underline = (on) => indicator === "underline" && on ? /* @__PURE__ */ jsx38("span", { "aria-hidden": "true", className: "pointer-events-none absolute inset-x-3 bottom-0 h-0.5 rounded-full bg-fg" }) : null;
11063
+ const rowClass = cn(
11064
+ "flex min-h-9 w-full min-w-0 cursor-pointer items-center gap-2.5 rounded-control px-3 py-1.5 text-start text-sm text-fg-muted transition-colors duration-100",
11065
+ "hover:bg-secondary-hover hover:text-fg data-[active]:bg-[color:color-mix(in_srgb,var(--color-primary)_10%,transparent)] data-[active]:font-medium data-[active]:text-primary",
11066
+ "[&_svg:not([class*='size-'])]:size-[1.125rem] [&_svg]:shrink-0",
11067
+ focusRing
11068
+ );
11069
+ const panelLink = (child, j) => vertical ? (
11070
+ // In a list, sub-links are quieter rows under their section, like SidebarNav.
11071
+ /* @__PURE__ */ jsx38("li", { children: /* @__PURE__ */ jsxs37(ItemLink2, { item: child, ctx, className: cn(rowClass, "min-h-8 py-1"), children: [
11072
+ renderIcon(child.icon),
11073
+ /* @__PURE__ */ jsx38("span", { className: "min-w-0 flex-1 truncate", children: child.label }),
11074
+ child.badge,
11075
+ child.external && /* @__PURE__ */ jsx38(ExternalIcon, {})
11076
+ ] }) }, keyOf2(child, j))
11077
+ ) : /* @__PURE__ */ jsx38("li", { children: /* @__PURE__ */ jsxs37(
11078
+ ItemLink2,
11079
+ {
11080
+ item: child,
11081
+ ctx,
11082
+ className: cn(
11083
+ "group/link flex min-w-0 gap-3 rounded-control p-2.5 text-sm transition-colors duration-100 hover:bg-secondary-hover",
11084
+ "data-[active]:bg-[color:color-mix(in_srgb,var(--color-primary)_10%,transparent)]",
11085
+ "[&_svg]:mt-0.5 [&_svg]:size-[1.125rem] [&_svg]:shrink-0 [&_svg]:text-fg-muted data-[active]:[&_svg]:text-primary",
11086
+ focusRing
11087
+ ),
11088
+ children: [
11089
+ renderIcon(child.icon),
11090
+ /* @__PURE__ */ jsxs37("span", { className: "grid min-w-0 gap-0.5", children: [
11091
+ /* @__PURE__ */ jsxs37("span", { className: "flex items-center gap-1.5 font-medium text-fg group-data-[active]/link:text-primary", children: [
11092
+ /* @__PURE__ */ jsx38("span", { className: "truncate", children: child.label }),
11093
+ child.badge,
11094
+ child.external && /* @__PURE__ */ jsx38(ExternalIcon, {})
11095
+ ] }),
11096
+ child.description && /* @__PURE__ */ jsx38("span", { className: "text-[0.8125rem] leading-snug text-fg-muted", children: child.description })
11097
+ ] })
11098
+ ]
11099
+ }
11100
+ ) }, keyOf2(child, j));
11101
+ return /* @__PURE__ */ jsx38(
11102
+ "nav",
11103
+ {
11104
+ ref: navRef,
11105
+ "aria-label": ariaLabel,
11106
+ className: cn(vertical ? "grid" : "flex items-stretch", className),
11107
+ onBlur: (e) => {
11108
+ if (!vertical && !e.currentTarget.contains(e.relatedTarget)) setOpenKey(null);
11109
+ },
11110
+ children: /* @__PURE__ */ jsx38("ul", { role: "list", className: cn(vertical ? "grid gap-0.5" : "flex items-stretch gap-1"), children: items.map((item, i) => {
11111
+ const key = keyOf2(item, i);
11112
+ const hasPanel = !!item.children?.length;
11113
+ const holdsActive = hasPanel && item.children.some((c) => !!c.href && c.href === best);
11114
+ const active = !!item.href && item.href === best;
11115
+ if (vertical) {
11116
+ return /* @__PURE__ */ jsx38(VerticalItem, { item, ctx, rowClass, holdsActive, panelLink }, key);
11117
+ }
11118
+ const open = openKey === key;
11119
+ return /* @__PURE__ */ jsxs37(
11120
+ "li",
11121
+ {
11122
+ className: "relative flex items-center",
11123
+ onPointerEnter: (e) => hasPanel && hoverOpen(key, e),
11124
+ onPointerLeave: (e) => hasPanel && hoverOpen(null, e),
11125
+ children: [
11126
+ hasPanel ? /* @__PURE__ */ jsxs37(
11127
+ "button",
11128
+ {
11129
+ type: "button",
11130
+ "data-nav-top": "",
11131
+ "data-nav-key": key,
11132
+ "data-active": holdsActive || void 0,
11133
+ "aria-expanded": open,
11134
+ "aria-controls": `${id}-${key}`,
11135
+ onClick: () => setOpenKey(open ? null : key),
11136
+ onKeyDown: (e) => onTopKeyDown(e, key, true),
11137
+ className: topItemClass,
11138
+ children: [
11139
+ renderIcon(item.icon),
11140
+ item.label,
11141
+ item.badge,
11142
+ /* @__PURE__ */ jsx38(Chevron5, { open, vertical: false })
11143
+ ]
11144
+ }
11145
+ ) : /* @__PURE__ */ jsxs37(ItemLink2, { item, ctx, "data-nav-top": "", onKeyDown: (e) => onTopKeyDown(e, key, false), className: topItemClass, children: [
11146
+ renderIcon(item.icon),
11147
+ item.label,
11148
+ item.badge,
11149
+ item.external && /* @__PURE__ */ jsx38(ExternalIcon, {})
11150
+ ] }),
11151
+ underline(active || holdsActive),
11152
+ hasPanel && /* @__PURE__ */ jsx38(
11153
+ "div",
11154
+ {
11155
+ id: `${id}-${key}`,
11156
+ hidden: !open,
11157
+ onKeyDown: (e) => onPanelKeyDown(e, key),
11158
+ className: cn(
11159
+ "absolute left-0 top-full z-50 pt-2 rtl:left-auto rtl:right-0",
11160
+ "animate-[ui-menu-down_140ms_cubic-bezier(0.2,0.9,0.3,1)] motion-reduce:animate-none"
11161
+ ),
11162
+ children: /* @__PURE__ */ jsx38(
11163
+ "ul",
11164
+ {
11165
+ role: "list",
11166
+ className: cn(
11167
+ "grid w-max max-w-[min(36rem,calc(100vw-2rem))] gap-0.5 rounded-control-lg border border-border bg-surface p-1.5 shadow-[var(--ui-shadow-lg)]",
11168
+ item.children.length > 4 && item.children.some((c) => c.description) ? "sm:grid-cols-2" : "min-w-56"
11169
+ ),
11170
+ children: item.children.map(panelLink)
11171
+ }
11172
+ )
11173
+ }
11174
+ )
11175
+ ]
11176
+ },
11177
+ key
11178
+ );
11179
+ }) })
11180
+ }
11181
+ );
11182
+ }
11183
+ function VerticalItem({
11184
+ item,
11185
+ ctx,
11186
+ rowClass,
11187
+ holdsActive,
11188
+ panelLink
11189
+ }) {
11190
+ const [open, setOpen] = React39.useState(holdsActive);
11191
+ const subId = React39.useId();
11192
+ const subRef = React39.useRef(null);
11193
+ React39.useEffect(() => {
11194
+ if (holdsActive) setOpen(true);
11195
+ }, [holdsActive]);
11196
+ React39.useEffect(() => {
11197
+ if (!subRef.current) return;
11198
+ if (open) subRef.current.removeAttribute("inert");
11199
+ else subRef.current.setAttribute("inert", "");
11200
+ }, [open]);
11201
+ if (!item.children?.length) {
11202
+ return /* @__PURE__ */ jsx38("li", { children: /* @__PURE__ */ jsxs37(ItemLink2, { item, ctx, className: rowClass, children: [
11203
+ renderIcon(item.icon),
11204
+ /* @__PURE__ */ jsx38("span", { className: "min-w-0 flex-1 truncate", children: item.label }),
11205
+ item.badge,
11206
+ item.external && /* @__PURE__ */ jsx38(ExternalIcon, {})
11207
+ ] }) });
11208
+ }
11209
+ return /* @__PURE__ */ jsxs37("li", { className: "grid", children: [
11210
+ /* @__PURE__ */ jsxs37(
11211
+ "button",
11212
+ {
11213
+ type: "button",
11214
+ "aria-expanded": open,
11215
+ "aria-controls": subId,
11216
+ onClick: () => setOpen((o) => !o),
11217
+ className: cn(rowClass, holdsActive && !open && "text-fg"),
11218
+ children: [
11219
+ renderIcon(item.icon),
11220
+ /* @__PURE__ */ jsx38("span", { className: "min-w-0 flex-1 truncate", children: item.label }),
11221
+ item.badge,
11222
+ /* @__PURE__ */ jsx38(Chevron5, { open, vertical: true })
11223
+ ]
11224
+ }
11225
+ ),
11226
+ /* @__PURE__ */ jsx38(
11227
+ "div",
11228
+ {
11229
+ ref: subRef,
11230
+ id: subId,
11231
+ className: cn(
11232
+ "grid transition-[grid-template-rows,opacity] duration-200 ease-out motion-reduce:transition-none",
11233
+ open ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"
11234
+ ),
11235
+ children: /* @__PURE__ */ jsx38("div", { className: "min-h-0 overflow-hidden", children: /* @__PURE__ */ jsx38("ul", { role: "list", className: "ms-4 grid gap-0.5 border-s border-border py-1 ps-2", children: item.children.map(panelLink) }) })
11236
+ }
11237
+ )
11238
+ ] });
11239
+ }
11240
+
11241
+ // src/components/placeholder/Placeholder.tsx
11242
+ import * as React40 from "react";
11243
+ import { jsx as jsx39, jsxs as jsxs38 } from "react/jsx-runtime";
11244
+ var patterns = {
11245
+ stripes: {
11246
+ backgroundImage: "repeating-linear-gradient(-45deg, color-mix(in srgb, currentColor 14%, transparent) 0 1px, transparent 1px 9px)"
11247
+ },
11248
+ dots: {
11249
+ backgroundImage: "radial-gradient(color-mix(in srgb, currentColor 22%, transparent) 1px, transparent 1.5px)",
11250
+ backgroundSize: "12px 12px"
11251
+ },
11252
+ grid: {
11253
+ backgroundImage: "linear-gradient(color-mix(in srgb, currentColor 11%, transparent) 1px, transparent 1px), linear-gradient(90deg, color-mix(in srgb, currentColor 11%, transparent) 1px, transparent 1px)",
11254
+ backgroundSize: "16px 16px",
11255
+ backgroundPosition: "-1px -1px"
11256
+ },
11257
+ none: {}
11258
+ };
11259
+ var Placeholder = React40.forwardRef(function Placeholder2({ pattern = "stripes", label, ratio, border = "dashed", className, style, children, ...props }, ref) {
11260
+ return /* @__PURE__ */ jsxs38(
11261
+ "div",
11262
+ {
11263
+ ref,
11264
+ "aria-hidden": label || children ? void 0 : true,
11265
+ className: cn(
11266
+ "relative grid min-h-24 place-items-center overflow-hidden rounded-card text-fg-muted",
11267
+ border === "dashed" && "border border-dashed border-border-strong",
11268
+ border === "solid" && "border border-border",
11269
+ className
11270
+ ),
11271
+ style: { ...patterns[pattern], aspectRatio: ratio, ...style },
11272
+ ...props,
11273
+ children: [
11274
+ label && /* @__PURE__ */ jsx39("span", { className: "rounded-control bg-surface px-2.5 py-1 text-[0.8125rem] font-medium text-fg-muted shadow-[0_0_0_6px_var(--color-surface)]", children: label }),
11275
+ children
11276
+ ]
11277
+ }
11278
+ );
11279
+ });
10412
11280
  export {
10413
11281
  Accordion,
10414
11282
  AccordionItem,
@@ -10426,6 +11294,7 @@ export {
10426
11294
  BarChart,
10427
11295
  BarList,
10428
11296
  Blockquote,
11297
+ Breadcrumbs,
10429
11298
  Button,
10430
11299
  CalendarHeatmap,
10431
11300
  Card,
@@ -10450,6 +11319,15 @@ export {
10450
11319
  DataTable,
10451
11320
  Divider,
10452
11321
  DonutChart,
11322
+ Drawer,
11323
+ DrawerBody,
11324
+ DrawerClose,
11325
+ DrawerContent,
11326
+ DrawerDescription,
11327
+ DrawerFooter,
11328
+ DrawerHeader,
11329
+ DrawerTitle,
11330
+ DrawerTrigger,
10453
11331
  DropdownMenu,
10454
11332
  DropdownMenuCheckboxItem,
10455
11333
  DropdownMenuContent,
@@ -10499,10 +11377,13 @@ export {
10499
11377
  ModalHeader,
10500
11378
  ModalTitle,
10501
11379
  ModalTrigger,
11380
+ NavigationMenu,
11381
+ OtpInput,
10502
11382
  Pagination,
10503
11383
  Pill,
10504
11384
  PillGroup,
10505
11385
  PillOption,
11386
+ Placeholder,
10506
11387
  ProgressBar,
10507
11388
  ProgressRing,
10508
11389
  Prose,
@@ -10551,6 +11432,7 @@ export {
10551
11432
  TimelineGroup,
10552
11433
  TimelineItem,
10553
11434
  Toaster,
11435
+ Tooltip2 as Tooltip,
10554
11436
  WidgetCard,
10555
11437
  buttonVariants,
10556
11438
  cardVariants,
@@ -10572,6 +11454,7 @@ export {
10572
11454
  toast,
10573
11455
  useAccordion,
10574
11456
  useDelayedLoading,
11457
+ useDrawer,
10575
11458
  useField,
10576
11459
  useFileUploads,
10577
11460
  useModal,