@glyphui/ui 0.3.0 → 0.4.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.
Files changed (63) hide show
  1. package/README.md +65 -12
  2. package/dist/chart.d.ts +1 -1
  3. package/dist/chart.js +28 -19
  4. package/dist/color-mode-script-Bf7LYkOj.js +13 -0
  5. package/dist/color-mode-script-KdwItKN2.d.ts +16 -0
  6. package/dist/data-table.js +3 -3
  7. package/dist/{field-CWZ_FdkD.js → field-CVbywksE.js} +21 -6
  8. package/dist/{field-context-anGPYXbW.js → field-context-7I-cWvvS.js} +2 -2
  9. package/dist/form.d.ts +11 -4
  10. package/dist/form.js +34 -9
  11. package/dist/glyph-context-DxGJrdvr.d.ts +50 -0
  12. package/dist/{index-BWK4czIG.d.ts → index-Bi7rZ6ba.d.ts} +11 -0
  13. package/dist/index.d.ts +87 -80
  14. package/dist/index.js +155 -82
  15. package/dist/react-router.d.ts +18 -0
  16. package/dist/react-router.js +44 -0
  17. package/dist/{states-DR-OoDT-.js → states-IvhjBgna.js} +2 -2
  18. package/dist/{tv-DBAGlgXJ.js → tv-DLbT66dC.js} +13 -1
  19. package/dist/vite.d.ts +27 -0
  20. package/dist/vite.js +29 -0
  21. package/fonts.css +10 -0
  22. package/package.json +32 -5
  23. package/src/components/badge/badge.styles.ts +11 -16
  24. package/src/components/banner/banner.styles.ts +4 -4
  25. package/src/components/button/button.styles.ts +3 -1
  26. package/src/components/calendar/calendar.styles.ts +3 -2
  27. package/src/components/dropdown-menu/dropdown-menu.styles.ts +2 -2
  28. package/src/components/eyebrow/eyebrow.styles.ts +1 -1
  29. package/src/components/field/field-context.ts +2 -0
  30. package/src/components/field/field.styles.ts +4 -2
  31. package/src/components/field/field.tsx +25 -4
  32. package/src/components/file-upload/file-upload.styles.ts +3 -3
  33. package/src/components/link/link.styles.ts +1 -1
  34. package/src/components/modal/modal.styles.ts +2 -0
  35. package/src/components/modal/modal.tsx +30 -5
  36. package/src/components/navigation-menu/navigation-menu.tsx +14 -4
  37. package/src/components/password-input/password-input.styles.ts +3 -3
  38. package/src/components/prose/prose.tsx +1 -1
  39. package/src/components/scroll-area/scroll-area.tsx +23 -1
  40. package/src/components/segmented-control/segmented-control.tsx +17 -2
  41. package/src/components/slider/slider.tsx +9 -2
  42. package/src/components/stepper/stepper.styles.ts +2 -2
  43. package/src/components/tag-input/tag-input.styles.ts +1 -1
  44. package/src/components/text/text.styles.ts +4 -4
  45. package/src/integrations/react-router.tsx +39 -0
  46. package/src/integrations/vite.test-d.ts +6 -0
  47. package/src/integrations/vite.test.ts +28 -0
  48. package/src/integrations/vite.ts +33 -0
  49. package/src/internal/dev.ts +12 -0
  50. package/src/patterns/app-shell/app-shell.styles.ts +1 -1
  51. package/src/patterns/app-shell/navbar.tsx +34 -16
  52. package/src/patterns/app-shell/sidebar.tsx +7 -2
  53. package/src/patterns/chart/chart-parts.tsx +10 -2
  54. package/src/patterns/chart/chart.tsx +22 -5
  55. package/src/patterns/form/form.tsx +48 -6
  56. package/src/patterns/form/index.ts +1 -0
  57. package/src/provider/color-mode-script.ts +28 -0
  58. package/src/provider/color-mode.tsx +14 -20
  59. package/src/provider/glyph-context.ts +38 -0
  60. package/src/provider/glyph-provider.tsx +16 -4
  61. package/src/provider/index.ts +4 -0
  62. package/src/provider/theme-scope.tsx +7 -3
  63. package/styles.css +72 -0
package/dist/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  "use client";
2
- import { a as themedBorder, c as GlyphContext, i as popover, l as useGlyph, n as twMergeConfig, o as transition, r as focusRing, s as useComponentConfig, t as tv, u as useLinkComponent } from "./tv-DBAGlgXJ.js";
3
- import { _ as MenuIcon, a as buttonStyles, b as SearchIcon, d as CheckIcon$4, f as ChevronDownIcon$1, h as ChevronsUpDownIcon, i as IconButton, m as ChevronRightIcon$2, n as useFieldControl, o as Spinner, p as ChevronLeftIcon, r as Button, s as spinnerStyles, t as FieldContext, x as XIcon$2, y as PanelLeftIcon } from "./field-context-anGPYXbW.js";
4
- import { _ as inputStyles, a as Table, c as TableCell, d as TableRow, f as tableStyles, g as fieldControl, h as Input, i as stateStyles, l as TableHead, m as skeletonStyles, n as ErrorState, o as TableBody, p as Skeleton, r as LoadingState, s as TableCaption, t as EmptyState, u as TableHeader, v as Checkbox, y as checkboxStyles } from "./states-DR-OoDT-.js";
5
- import { i as labelStyles, n as fieldStyles, r as Label, t as Field } from "./field-CWZ_FdkD.js";
2
+ import { a as themedBorder, c as GlyphContext, d as useIsActive, f as useLinkComponent, i as popover, l as neverActive, n as twMergeConfig, o as transition, r as focusRing, s as useComponentConfig, t as tv, u as useGlyph } from "./tv-DLbT66dC.js";
3
+ import { _ as MenuIcon, a as buttonStyles, b as SearchIcon, d as CheckIcon$4, f as ChevronDownIcon$1, h as ChevronsUpDownIcon, i as IconButton, m as ChevronRightIcon$2, n as useFieldControl, o as Spinner, p as ChevronLeftIcon, r as Button, s as spinnerStyles, t as FieldContext, x as XIcon$2, y as PanelLeftIcon } from "./field-context-7I-cWvvS.js";
4
+ import { _ as inputStyles, a as Table, c as TableCell, d as TableRow, f as tableStyles, g as fieldControl, h as Input, i as stateStyles, l as TableHead, m as skeletonStyles, n as ErrorState, o as TableBody, p as Skeleton, r as LoadingState, s as TableCaption, t as EmptyState, u as TableHeader, v as Checkbox, y as checkboxStyles } from "./states-IvhjBgna.js";
5
+ import { i as labelStyles, n as fieldStyles, r as Label, t as Field } from "./field-CVbywksE.js";
6
+ import { n as DEFAULT_STORAGE_KEY, r as getColorModeScript, t as DARK_QUERY } from "./color-mode-script-Bf7LYkOj.js";
6
7
  import { defineTheme, defineTheme as defineTheme$1, presets as presets$1, scopeSelector, themeToCss, themeToCss as themeToCss$1, tokenVar } from "@glyphui/tokens";
7
8
  import { Accordion as Accordion$1, AlertDialog as AlertDialog$1, AspectRatio as AspectRatio$1, Avatar as Avatar$1, Collapsible as Collapsible$1, ContextMenu as ContextMenu$1, Dialog as Dialog$1, DropdownMenu as DropdownMenu$1, HoverCard as HoverCard$1, NavigationMenu as NavigationMenu$1, Popover as Popover$1, Progress as Progress$1, RadioGroup as RadioGroup$1, ScrollArea as ScrollArea$1, Select as Select$1, Separator as Separator$1, Slider as Slider$1, Slot, Switch as Switch$1, Tabs as Tabs$1, Toggle as Toggle$1, ToggleGroup as ToggleGroup$1, Tooltip as Tooltip$1, VisuallyHidden as VisuallyHidden$1 } from "radix-ui";
8
9
  import { Children, createContext, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
@@ -561,29 +562,29 @@ const tones = {
561
562
  outline: "border-border-strong text-foreground"
562
563
  },
563
564
  primary: {
564
- soft: "bg-primary/14 text-[color-mix(in_oklab,var(--color-primary)_62%,var(--color-foreground))]",
565
+ soft: "bg-primary/14 text-primary-text",
565
566
  solid: "bg-primary text-primary-foreground",
566
- outline: "border-primary/50 text-[color-mix(in_oklab,var(--color-primary)_62%,var(--color-foreground))]"
567
+ outline: "border-primary/50 text-primary-text"
567
568
  },
568
569
  success: {
569
- soft: "bg-success/14 text-[color-mix(in_oklab,var(--color-success)_62%,var(--color-foreground))]",
570
+ soft: "bg-success/14 text-success-text",
570
571
  solid: "bg-success text-success-foreground",
571
- outline: "border-success/50 text-[color-mix(in_oklab,var(--color-success)_62%,var(--color-foreground))]"
572
+ outline: "border-success/50 text-success-text"
572
573
  },
573
574
  warning: {
574
- soft: "bg-warning/16 text-[color-mix(in_oklab,var(--color-warning)_62%,var(--color-foreground))]",
575
+ soft: "bg-warning/14 text-warning-text",
575
576
  solid: "bg-warning text-warning-foreground",
576
- outline: "border-warning/50 text-[color-mix(in_oklab,var(--color-warning)_62%,var(--color-foreground))]"
577
+ outline: "border-warning/50 text-warning-text"
577
578
  },
578
579
  danger: {
579
- soft: "bg-danger/14 text-[color-mix(in_oklab,var(--color-danger)_62%,var(--color-foreground))]",
580
+ soft: "bg-danger/14 text-danger-text",
580
581
  solid: "bg-danger text-danger-foreground",
581
- outline: "border-danger/50 text-[color-mix(in_oklab,var(--color-danger)_62%,var(--color-foreground))]"
582
+ outline: "border-danger/50 text-danger-text"
582
583
  },
583
584
  info: {
584
- soft: "bg-info/14 text-[color-mix(in_oklab,var(--color-info)_62%,var(--color-foreground))]",
585
+ soft: "bg-info/14 text-info-text",
585
586
  solid: "bg-info text-info-foreground",
586
- outline: "border-info/50 text-[color-mix(in_oklab,var(--color-info)_62%,var(--color-foreground))]"
587
+ outline: "border-info/50 text-info-text"
587
588
  }
588
589
  };
589
590
  const badgeStyles = tv({
@@ -659,10 +660,10 @@ const bannerStyles = tv({
659
660
  icon: "text-muted-foreground"
660
661
  },
661
662
  primary: { root: "border-primary bg-primary text-primary-foreground" },
662
- info: { root: "border-info/25 bg-info/10 text-[color-mix(in_oklab,var(--color-info)_55%,var(--color-foreground))]" },
663
- success: { root: "border-success/25 bg-success/10 text-[color-mix(in_oklab,var(--color-success)_55%,var(--color-foreground))]" },
664
- warning: { root: "border-warning/30 bg-warning/14 text-[color-mix(in_oklab,var(--color-warning)_50%,var(--color-foreground))]" },
665
- danger: { root: "border-danger/25 bg-danger/10 text-[color-mix(in_oklab,var(--color-danger)_55%,var(--color-foreground))]" }
663
+ info: { root: "border-info/25 bg-info/10 text-info-text" },
664
+ success: { root: "border-success/25 bg-success/10 text-success-text" },
665
+ warning: { root: "border-warning/30 bg-warning/14 text-warning-text" },
666
+ danger: { root: "border-danger/25 bg-danger/10 text-danger-text" }
666
667
  } },
667
668
  defaultVariants: { tone: "neutral" }
668
669
  });
@@ -889,9 +890,9 @@ const calendarStyles = tv({ slots: {
889
890
  transition,
890
891
  focusRing
891
892
  ],
892
- today: "[&>button]:font-semibold [&>button]:text-primary",
893
+ today: "[&>button]:font-semibold [&:not([data-selected])>button]:text-primary-text",
893
894
  selected: "[&>button]:bg-primary [&>button]:text-primary-foreground [&>button]:hover:bg-primary/90",
894
- outside: "[&>button]:text-faint-foreground [&>button]:opacity-60",
895
+ outside: "[&:not([data-selected])>button]:text-faint-foreground",
895
896
  disabled: "[&>button]:pointer-events-none [&>button]:opacity-35 [&>button]:line-through",
896
897
  rangeStart: "rounded-l-md bg-primary/14",
897
898
  rangeMiddle: "bg-primary/14 [&>button]:rounded-none [&>button]:bg-transparent! [&>button]:text-foreground! [&>button]:hover:bg-primary/10!",
@@ -1812,8 +1813,8 @@ const dropdownMenuStyles = tv({
1812
1813
  tone: {
1813
1814
  default: {},
1814
1815
  danger: {
1815
- item: "text-danger data-highlighted:bg-danger/10",
1816
- icon: "text-danger"
1816
+ item: "text-danger-text data-highlighted:bg-danger/10",
1817
+ icon: "text-danger-text"
1817
1818
  }
1818
1819
  },
1819
1820
  inset: { true: {
@@ -2688,7 +2689,7 @@ const eyebrowStyles = tv({
2688
2689
  slots: { base: "font-mono text-caption uppercase tracking-[0.12em]" },
2689
2690
  variants: { tone: {
2690
2691
  muted: { base: "text-muted-foreground" },
2691
- primary: { base: "text-primary" }
2692
+ primary: { base: "text-primary-text" }
2692
2693
  } },
2693
2694
  defaultVariants: { tone: "muted" }
2694
2695
  });
@@ -2721,16 +2722,16 @@ const fileUploadStyles = tv({ slots: {
2721
2722
  ],
2722
2723
  icon: "flex size-10 items-center justify-center rounded-md border border-border bg-surface text-muted-foreground shadow-xs [&>svg]:size-5",
2723
2724
  title: "text-small font-medium text-foreground",
2724
- highlight: "text-primary",
2725
+ highlight: "text-primary-text",
2725
2726
  hint: "text-caption text-faint-foreground",
2726
- rejections: "grid gap-1 text-small font-medium text-danger",
2727
+ rejections: "grid gap-1 text-small font-medium text-danger-text",
2727
2728
  list: "grid gap-2",
2728
2729
  item: ["grid grid-cols-[auto_1fr_auto] items-center gap-3 rounded-md border-border bg-surface px-3 py-2.5", themedBorder],
2729
2730
  fileIcon: "flex size-8 items-center justify-center rounded-sm bg-muted text-muted-foreground [&>svg]:size-4",
2730
2731
  fileText: "grid min-w-0 gap-1",
2731
2732
  fileName: "truncate text-small font-medium text-foreground",
2732
2733
  fileMeta: "text-caption tabular-nums text-faint-foreground",
2733
- fileError: "text-caption font-medium text-danger",
2734
+ fileError: "text-caption font-medium text-danger-text",
2734
2735
  progress: "h-1 overflow-hidden rounded-pill bg-muted",
2735
2736
  progressBar: "h-full rounded-pill bg-primary transition-[width] duration-(--glyph-duration-base)"
2736
2737
  } });
@@ -3284,7 +3285,7 @@ const linkStyles = tv({
3284
3285
  tone: {
3285
3286
  default: { base: "text-foreground" },
3286
3287
  muted: { base: "text-muted-foreground hover:text-foreground" },
3287
- primary: { base: "text-primary" }
3288
+ primary: { base: "text-primary-text" }
3288
3289
  },
3289
3290
  underline: {
3290
3291
  hover: { base: "bg-[length:0%_1px] hover:bg-[length:100%_1px] focus-visible:bg-[length:100%_1px]" },
@@ -3355,7 +3356,9 @@ const modalStyles = tv({ slots: {
3355
3356
  content: "",
3356
3357
  header: "",
3357
3358
  body: "grid gap-4",
3358
- footer: ""
3359
+ footer: "",
3360
+ /** Wraps body and footer when `onSubmit` is set; `contents` keeps the dialog layout. */
3361
+ form: "contents"
3359
3362
  } });
3360
3363
  //#endregion
3361
3364
  //#region src/components/modal/modal.tsx
@@ -3365,8 +3368,15 @@ const modalStyles = tv({ slots: {
3365
3368
  */
3366
3369
  function Modal(inProps) {
3367
3370
  const [props, slots] = useComponentConfig("Modal", inProps);
3368
- const { open, defaultOpen, onOpenChange, title, description, trigger, footer, closeOnOverlayClick = true, onPointerDownOutside, className, children, ...rest } = props;
3371
+ const { open, defaultOpen, onOpenChange, title, description, trigger, footer, closeOnOverlayClick = true, onSubmit, onPointerDownOutside, className, children, ...rest } = props;
3369
3372
  const s = modalStyles();
3373
+ const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [children && /* @__PURE__ */ jsx(DialogBody, {
3374
+ className: s.body({ class: slots.body }),
3375
+ children
3376
+ }), footer && /* @__PURE__ */ jsx(DialogFooter, {
3377
+ className: s.footer({ class: slots.footer }),
3378
+ children: footer
3379
+ })] });
3370
3380
  return /* @__PURE__ */ jsxs(Dialog, {
3371
3381
  ...open !== void 0 && { open },
3372
3382
  ...defaultOpen !== void 0 && { defaultOpen },
@@ -3383,20 +3393,18 @@ function Modal(inProps) {
3383
3393
  },
3384
3394
  ...!description && { "aria-describedby": void 0 },
3385
3395
  ...rest,
3386
- children: [
3387
- /* @__PURE__ */ jsxs(DialogHeader, {
3388
- className: s.header({ class: slots.header }),
3389
- children: [/* @__PURE__ */ jsx(DialogTitle, { children: title }), description && /* @__PURE__ */ jsx(DialogDescription, { children: description })]
3390
- }),
3391
- children && /* @__PURE__ */ jsx(DialogBody, {
3392
- className: s.body({ class: slots.body }),
3393
- children
3394
- }),
3395
- footer && /* @__PURE__ */ jsx(DialogFooter, {
3396
- className: s.footer({ class: slots.footer }),
3397
- children: footer
3398
- })
3399
- ]
3396
+ children: [/* @__PURE__ */ jsxs(DialogHeader, {
3397
+ className: s.header({ class: slots.header }),
3398
+ children: [/* @__PURE__ */ jsx(DialogTitle, { children: title }), description && /* @__PURE__ */ jsx(DialogDescription, { children: description })]
3399
+ }), onSubmit ? /* @__PURE__ */ jsx("form", {
3400
+ "data-slot": "modal-form",
3401
+ className: s.form({ class: slots.form }),
3402
+ onSubmit: (event) => {
3403
+ event.preventDefault();
3404
+ onSubmit(event);
3405
+ },
3406
+ children: content
3407
+ }) : content]
3400
3408
  })]
3401
3409
  });
3402
3410
  }
@@ -3513,20 +3521,26 @@ function NavigationMenuViewport({ className, ...props }) {
3513
3521
  })
3514
3522
  });
3515
3523
  }
3516
- /** A top-level link (no panel). Set `active` for the current page. */
3517
- function NavigationMenuLink({ href, className, children, ...props }) {
3524
+ /**
3525
+ * A top-level link (no panel). Marked active automatically from the provider's `router`;
3526
+ * set `active` to override.
3527
+ */
3528
+ function NavigationMenuLink({ href, active: activeProp, className, children, ...props }) {
3518
3529
  const slots = useSlots$2();
3519
3530
  const Link = useLinkComponent();
3531
+ const active = useIsActive(href, { explicit: activeProp });
3520
3532
  const cls = s$2.link({ class: [slots.link, className] });
3521
3533
  if (href === void 0) return /* @__PURE__ */ jsx(NavigationMenu$1.Link, {
3522
3534
  "data-slot": "navigation-menu-link",
3523
3535
  className: cls,
3536
+ active,
3524
3537
  ...props,
3525
3538
  children
3526
3539
  });
3527
3540
  return /* @__PURE__ */ jsx(NavigationMenu$1.Link, {
3528
3541
  "data-slot": "navigation-menu-link",
3529
3542
  asChild: true,
3543
+ active,
3530
3544
  ...props,
3531
3545
  children: /* @__PURE__ */ jsx(Link, {
3532
3546
  href,
@@ -3895,9 +3909,9 @@ const passwordInputStyles = tv({
3895
3909
  label: "w-14 text-right text-caption text-muted-foreground"
3896
3910
  },
3897
3911
  variants: { strength: {
3898
- weak: { label: "text-danger" },
3899
- fair: { label: "text-warning" },
3900
- strong: { label: "text-success" }
3912
+ weak: { label: "text-danger-text" },
3913
+ fair: { label: "text-warning-text" },
3914
+ strong: { label: "text-success-text" }
3901
3915
  } }
3902
3916
  });
3903
3917
  //#endregion
@@ -4181,7 +4195,7 @@ const proseStyles = tv({
4181
4195
  "[&_h4]:mt-[1.4em] [&_h4]:mb-[0.4em] [&_h4]:font-display [&_h4]:text-h4",
4182
4196
  "[&_p]:my-[1em] [&_p]:text-pretty",
4183
4197
  "[&_strong]:font-semibold [&_strong]:text-foreground",
4184
- "[&_a]:font-medium [&_a]:text-primary [&_a]:underline [&_a]:decoration-primary/40 [&_a]:underline-offset-4 [&_a:hover]:decoration-primary",
4198
+ "[&_a]:font-medium [&_a]:text-primary-text [&_a]:underline [&_a]:decoration-primary/40 [&_a]:underline-offset-4 [&_a:hover]:decoration-primary",
4185
4199
  "[&_ul]:my-[1em] [&_ul]:list-disc [&_ul]:pl-[1.5em] [&_ol]:my-[1em] [&_ol]:list-decimal [&_ol]:pl-[1.5em]",
4186
4200
  "[&_li]:my-[0.35em] [&_li]:pl-[0.25em] [&_li::marker]:text-faint-foreground",
4187
4201
  "[&_blockquote]:my-[1.5em] [&_blockquote]:border-l-2 [&_blockquote]:border-primary [&_blockquote]:pl-[1em] [&_blockquote]:font-display [&_blockquote]:text-muted-foreground",
@@ -4477,6 +4491,22 @@ const scrollAreaStyles = tv({ slots: {
4477
4491
  thumb: "relative flex-1 rounded-pill bg-border-strong hover:bg-faint-foreground",
4478
4492
  corner: "bg-transparent"
4479
4493
  } });
4494
+ /** True while the element's content overflows it, so keyboard users need to reach it. */
4495
+ function useOverflowing() {
4496
+ const ref = useRef(null);
4497
+ const [overflowing, setOverflowing] = useState(false);
4498
+ useEffect(() => {
4499
+ const el = ref.current;
4500
+ if (!el) return;
4501
+ const measure = () => setOverflowing(el.scrollHeight > el.clientHeight || el.scrollWidth > el.clientWidth);
4502
+ measure();
4503
+ const observer = new ResizeObserver(measure);
4504
+ observer.observe(el);
4505
+ if (el.firstElementChild) observer.observe(el.firstElementChild);
4506
+ return () => observer.disconnect();
4507
+ }, []);
4508
+ return [ref, overflowing];
4509
+ }
4480
4510
  /**
4481
4511
  * A scrollable region with slim, theme-colored scrollbars. Give it a height (or width)
4482
4512
  * with `className`, e.g. `h-72`.
@@ -4485,6 +4515,7 @@ function ScrollArea(inProps) {
4485
4515
  const [props, slots] = useComponentConfig("ScrollArea", inProps);
4486
4516
  const { orientation = "vertical", viewportClassName, className, children, ...rest } = props;
4487
4517
  const s = scrollAreaStyles();
4518
+ const [viewportRef, overflowing] = useOverflowing();
4488
4519
  const bar = (o) => /* @__PURE__ */ jsx(ScrollArea$1.Scrollbar, {
4489
4520
  "data-slot": "scroll-area-scrollbar",
4490
4521
  orientation: o,
@@ -4497,7 +4528,9 @@ function ScrollArea(inProps) {
4497
4528
  ...rest,
4498
4529
  children: [
4499
4530
  /* @__PURE__ */ jsx(ScrollArea$1.Viewport, {
4531
+ ref: viewportRef,
4500
4532
  "data-slot": "scroll-area-viewport",
4533
+ tabIndex: overflowing ? 0 : void 0,
4501
4534
  className: s.viewport({ class: [slots.viewport, viewportClassName] }),
4502
4535
  children
4503
4536
  }),
@@ -4544,6 +4577,16 @@ function Section({ spacing, tone, divided, asChild, className, ...props }) {
4544
4577
  });
4545
4578
  }
4546
4579
  //#endregion
4580
+ //#region src/internal/dev.ts
4581
+ const warned = /* @__PURE__ */ new Set();
4582
+ /** Logs a console warning once per key, in development only. */
4583
+ function warnOnce(key, message) {
4584
+ if (typeof process === "undefined" || process.env.NODE_ENV === "production") return;
4585
+ if (warned.has(key)) return;
4586
+ warned.add(key);
4587
+ console.warn(`[glyph] ${message}`);
4588
+ }
4589
+ //#endregion
4547
4590
  //#region src/components/segmented-control/segmented-control.tsx
4548
4591
  const segmentedControlStyles = tv({
4549
4592
  slots: {
@@ -4581,10 +4624,14 @@ const segmentedControlStyles = tv({
4581
4624
  function SegmentedControl(inProps) {
4582
4625
  const [props, slots] = useComponentConfig("SegmentedControl", inProps);
4583
4626
  const { options, value, defaultValue, onValueChange, size, fullWidth, className, ...rest } = props;
4627
+ const field = useContext(FieldContext);
4584
4628
  const s = segmentedControlStyles({
4585
4629
  size,
4586
4630
  fullWidth
4587
4631
  });
4632
+ const labelledBy = rest["aria-labelledby"] ?? (rest["aria-label"] ? void 0 : field?.labelId);
4633
+ if (!rest["aria-label"] && !labelledBy) warnOnce("segmented-control-label", "SegmentedControl needs a label: pass aria-label, or place it in a <Field label=\"…\">.");
4634
+ const describedBy = [field?.descriptionId, field?.errorId].filter(Boolean).join(" ");
4588
4635
  return /* @__PURE__ */ jsx(ToggleGroup$1.Root, {
4589
4636
  type: "single",
4590
4637
  "data-slot": "segmented-control",
@@ -4592,6 +4639,8 @@ function SegmentedControl(inProps) {
4592
4639
  ...defaultValue !== void 0 && { defaultValue },
4593
4640
  onValueChange: (next) => next && onValueChange?.(next),
4594
4641
  className: s.root({ class: [slots.root, className] }),
4642
+ "aria-labelledby": labelledBy,
4643
+ "aria-describedby": describedBy || void 0,
4595
4644
  ...rest,
4596
4645
  children: options.map((option) => /* @__PURE__ */ jsx(ToggleGroup$1.Item, {
4597
4646
  value: option.value,
@@ -4853,7 +4902,9 @@ const toArray = (value) => value === void 0 ? void 0 : typeof value === "number"
4853
4902
  /** Choose a number, or a range with two thumbs, by dragging or with arrow keys. */
4854
4903
  function Slider(inProps) {
4855
4904
  const [props, slots] = useComponentConfig("Slider", useFieldControl(inProps));
4856
- const { value, defaultValue, onValueChange, onValueCommit, min = 0, max = 100, step = 1, minStepsBetweenThumbs, marks, showValue = false, disabled, name, id, className, thumbLabels = ["Minimum", "Maximum"], "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid } = props;
4905
+ const { value, defaultValue, onValueChange, onValueCommit, min = 0, max = 100, step = 1, minStepsBetweenThumbs, marks, showValue = false, disabled, name, id, className, thumbLabels = ["Minimum", "Maximum"], "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid } = props;
4906
+ const field = useContext(FieldContext);
4907
+ const labelledBy = ariaLabelledBy ?? (ariaLabel ? void 0 : field?.labelId);
4857
4908
  const isRange = Array.isArray(value ?? defaultValue);
4858
4909
  const fromArray = (next) => isRange ? [next[0], next[1]] : next[0];
4859
4910
  const current = toArray(value) ?? toArray(defaultValue) ?? [min];
@@ -4880,6 +4931,7 @@ function Slider(inProps) {
4880
4931
  }), current.map((thumbValue, index) => /* @__PURE__ */ jsx(Slider$1.Thumb, {
4881
4932
  id: index === 0 ? id : void 0,
4882
4933
  "aria-label": isRange ? thumbLabels[index] : ariaLabel,
4934
+ "aria-labelledby": isRange ? void 0 : labelledBy,
4883
4935
  "aria-describedby": ariaDescribedBy,
4884
4936
  "aria-invalid": ariaInvalid,
4885
4937
  className: s.thumb({ class: slots.thumb }),
@@ -5090,7 +5142,7 @@ const stepperStyles = tv({
5090
5142
  title: "text-foreground"
5091
5143
  },
5092
5144
  current: {
5093
- indicator: "border-primary bg-surface text-primary ring-4 ring-primary/15",
5145
+ indicator: "border-primary bg-surface text-primary-text ring-4 ring-primary/15",
5094
5146
  connector: "bg-border",
5095
5147
  title: "text-foreground"
5096
5148
  },
@@ -5102,7 +5154,7 @@ const stepperStyles = tv({
5102
5154
  error: {
5103
5155
  indicator: "border-danger bg-danger text-danger-foreground",
5104
5156
  connector: "bg-border",
5105
- title: "text-danger"
5157
+ title: "text-danger-text"
5106
5158
  }
5107
5159
  },
5108
5160
  clickable: { true: { trigger: "cursor-pointer hover:[&_[data-slot=stepper-title]]:underline underline-offset-4" } }
@@ -5329,7 +5381,7 @@ const tagInputStyles = tv({
5329
5381
  tagLabel: "truncate",
5330
5382
  remove: "inline-flex size-4 shrink-0 cursor-pointer items-center justify-center rounded-pill text-muted-foreground hover:bg-border-strong/40 hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring [&>svg]:size-3",
5331
5383
  input: "h-7 min-w-24 flex-1 bg-transparent px-1 text-foreground outline-none placeholder:text-faint-foreground disabled:cursor-not-allowed",
5332
- message: "text-small font-medium text-danger"
5384
+ message: "text-small font-medium text-danger-text"
5333
5385
  },
5334
5386
  variants: { size: {
5335
5387
  sm: { frame: "min-h-(--glyph-control-sm) text-small" },
@@ -5492,10 +5544,10 @@ const textStyles = tv({
5492
5544
  default: { base: "text-foreground" },
5493
5545
  muted: { base: "text-muted-foreground" },
5494
5546
  faint: { base: "text-faint-foreground" },
5495
- primary: { base: "text-primary" },
5496
- success: { base: "text-success" },
5497
- warning: { base: "text-warning" },
5498
- danger: { base: "text-danger" }
5547
+ primary: { base: "text-primary-text" },
5548
+ success: { base: "text-success-text" },
5549
+ warning: { base: "text-warning-text" },
5550
+ danger: { base: "text-danger-text" }
5499
5551
  },
5500
5552
  weight: {
5501
5553
  regular: { base: "font-normal" },
@@ -6091,7 +6143,7 @@ const sidebarStyles = tv({ slots: {
6091
6143
  "group-data-[collapsed=true]/sidebar:justify-center group-data-[collapsed=true]/sidebar:px-0"
6092
6144
  ],
6093
6145
  itemIcon: "flex size-4 shrink-0 items-center justify-center [&>svg]:size-4",
6094
- itemLabel: "min-w-0 flex-1 truncate text-left group-data-[collapsed=true]/sidebar:hidden",
6146
+ itemLabel: "min-w-0 flex-1 truncate text-left group-data-[collapsed=true]/sidebar:sr-only",
6095
6147
  itemBadge: "ml-auto font-mono text-caption text-faint-foreground tabular-nums group-data-[collapsed=true]/sidebar:hidden",
6096
6148
  footer: ["mt-auto flex shrink-0 items-center gap-2 border-t-border px-3 py-3 group-data-[collapsed=true]/sidebar:justify-center", "border-t-(length:--glyph-border-width)"]
6097
6149
  } });
@@ -6206,7 +6258,11 @@ function SidebarSection({ label, className, children, ...props }) {
6206
6258
  }), children]
6207
6259
  });
6208
6260
  }
6209
- function SidebarItem({ href, icon, badge, active, onClick, className, children }) {
6261
+ function SidebarItem({ href, icon, badge, active: activeProp, end, onClick, className, children }) {
6262
+ const active = useIsActive(href, {
6263
+ explicit: activeProp,
6264
+ ...end !== void 0 && { end }
6265
+ });
6210
6266
  const { collapsed, inDrawer, setMobileOpen } = useAppShell();
6211
6267
  const Link = useLinkComponent();
6212
6268
  const slots = useSlots();
@@ -6347,6 +6403,20 @@ function AppShell(inProps) {
6347
6403
  }
6348
6404
  //#endregion
6349
6405
  //#region src/patterns/app-shell/navbar.tsx
6406
+ function NavbarLink({ link, className, onClick }) {
6407
+ const Link = useLinkComponent();
6408
+ const active = useIsActive(link.href, {
6409
+ explicit: link.active,
6410
+ ...link.end !== void 0 && { end: link.end }
6411
+ });
6412
+ return /* @__PURE__ */ jsx(Link, {
6413
+ href: link.href,
6414
+ "aria-current": active ? "page" : void 0,
6415
+ className,
6416
+ ...onClick && { onClick },
6417
+ children: link.label
6418
+ });
6419
+ }
6350
6420
  /**
6351
6421
  * Site header from data: brand, links, actions. Responsive out of the box: links collapse
6352
6422
  * into a focus-trapped menu panel on small screens.
@@ -6356,7 +6426,6 @@ function Navbar(inProps) {
6356
6426
  const { brand, links = [], actions, sticky = true, blurOnScroll = true, containerSize, className, ...rest } = props;
6357
6427
  const [open, setOpen] = useState(false);
6358
6428
  const scrolled = useScrolled(8, blurOnScroll);
6359
- const Link = useLinkComponent();
6360
6429
  const s = navbarStyles({
6361
6430
  sticky,
6362
6431
  blur: blurOnScroll
@@ -6377,11 +6446,9 @@ function Navbar(inProps) {
6377
6446
  links.length > 0 && /* @__PURE__ */ jsx("nav", {
6378
6447
  "aria-label": "Main",
6379
6448
  className: s.links({ class: slots.links }),
6380
- children: links.map((link) => /* @__PURE__ */ jsx(Link, {
6381
- href: link.href,
6382
- "aria-current": link.active ? "page" : void 0,
6383
- className: s.link({ class: slots.link }),
6384
- children: link.label
6449
+ children: links.map((link) => /* @__PURE__ */ jsx(NavbarLink, {
6450
+ link,
6451
+ className: s.link({ class: slots.link })
6385
6452
  }, link.href))
6386
6453
  }),
6387
6454
  actions && /* @__PURE__ */ jsx("div", {
@@ -6421,12 +6488,10 @@ function Navbar(inProps) {
6421
6488
  /* @__PURE__ */ jsx("nav", {
6422
6489
  "aria-label": "Main",
6423
6490
  className: s.panelLinks({ class: slots.panelLinks }),
6424
- children: links.map((link) => /* @__PURE__ */ jsx(Link, {
6425
- href: link.href,
6426
- "aria-current": link.active ? "page" : void 0,
6491
+ children: links.map((link) => /* @__PURE__ */ jsx(NavbarLink, {
6492
+ link,
6427
6493
  className: s.panelLink({ class: slots.panelLink }),
6428
- onClick: () => setOpen(false),
6429
- children: link.label
6494
+ onClick: () => setOpen(false)
6430
6495
  }, link.href))
6431
6496
  }),
6432
6497
  actions && /* @__PURE__ */ jsx("div", {
@@ -6532,7 +6597,6 @@ function useAsync(fn, deps) {
6532
6597
  //#endregion
6533
6598
  //#region src/provider/color-mode.tsx
6534
6599
  const ColorModeContext = createContext(null);
6535
- const DARK_QUERY = "(prefers-color-scheme: dark)";
6536
6600
  const isBrowser = typeof window !== "undefined";
6537
6601
  function readStoredMode(storageKey) {
6538
6602
  if (!isBrowser || !storageKey) return null;
@@ -6544,7 +6608,7 @@ function readStoredMode(storageKey) {
6544
6608
  }
6545
6609
  }
6546
6610
  function systemMode() {
6547
- return isBrowser && window.matchMedia(DARK_QUERY).matches ? "dark" : "light";
6611
+ return isBrowser && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
6548
6612
  }
6549
6613
  /** Disables transitions for one frame so a mode switch doesn't animate every color. */
6550
6614
  function suppressTransitions() {
@@ -6556,7 +6620,7 @@ function suppressTransitions() {
6556
6620
  requestAnimationFrame(() => style.remove());
6557
6621
  };
6558
6622
  }
6559
- function ColorModeProvider({ children, defaultMode = "system", storageKey = "glyph-mode" }) {
6623
+ function ColorModeProvider({ children, defaultMode = "system", storageKey = DEFAULT_STORAGE_KEY }) {
6560
6624
  const [mode, setModeState] = useState(() => readStoredMode(storageKey) ?? defaultMode);
6561
6625
  const [system, setSystem] = useState(systemMode);
6562
6626
  useEffect(() => {
@@ -6605,9 +6669,6 @@ function useColorMode() {
6605
6669
  * Inline script that sets `data-mode` on `<html>` before first paint, so a dark-mode
6606
6670
  * visitor never sees a light flash. Put it in `<head>`.
6607
6671
  */
6608
- function getColorModeScript({ defaultMode = "system", storageKey = "glyph-mode" } = {}) {
6609
- return `(function(){try{var d=${JSON.stringify(defaultMode)},m=null;${storageKey ? `m=localStorage.getItem(${JSON.stringify(storageKey)});` : ""}if(m!=='light'&&m!=='dark'&&m!=='system')m=d;if(m==='system')m=matchMedia('${DARK_QUERY}').matches?'dark':'light';var r=document.documentElement;r.dataset.mode=m;r.style.colorScheme=m}catch(e){}})()`;
6610
- }
6611
6672
  /** `<script>` element version of `getColorModeScript` for SSR frameworks. */
6612
6673
  function ColorModeScript(props) {
6613
6674
  return /* @__PURE__ */ jsx("script", {
@@ -6674,11 +6735,18 @@ function ThemeStyle({ theme, selector }) {
6674
6735
  //#endregion
6675
6736
  //#region src/provider/glyph-provider.tsx
6676
6737
  /** Provides the theme, color mode, tooltips, `useConfirm()` and (optionally) toasts. Render once at the root. */
6677
- function GlyphProvider({ children, theme = glyph, defaultMode, storageKey, linkComponent = "a", toaster = false }) {
6738
+ function GlyphProvider({ children, theme = glyph, defaultMode, storageKey, router, linkComponent, toaster = false }) {
6739
+ const Link = router?.Link ?? linkComponent ?? "a";
6740
+ const useIsActive = router?.useIsActive ?? neverActive;
6678
6741
  const value = useMemo(() => ({
6679
6742
  theme,
6680
- linkComponent
6681
- }), [theme, linkComponent]);
6743
+ linkComponent: Link,
6744
+ useIsActive
6745
+ }), [
6746
+ theme,
6747
+ Link,
6748
+ useIsActive
6749
+ ]);
6682
6750
  return /* @__PURE__ */ jsx(ColorModeProvider, {
6683
6751
  ...defaultMode !== void 0 && { defaultMode },
6684
6752
  ...storageKey !== void 0 && { storageKey },
@@ -6698,8 +6766,13 @@ function ThemeScope({ theme, mode, asChild, children, ...props }) {
6698
6766
  const parent = useContext(GlyphContext);
6699
6767
  const value = useMemo(() => ({
6700
6768
  theme,
6701
- linkComponent: parent?.linkComponent ?? "a"
6702
- }), [theme, parent?.linkComponent]);
6769
+ linkComponent: parent?.linkComponent ?? "a",
6770
+ useIsActive: parent?.useIsActive ?? neverActive
6771
+ }), [
6772
+ theme,
6773
+ parent?.linkComponent,
6774
+ parent?.useIsActive
6775
+ ]);
6703
6776
  const Comp = asChild ? Slot.Root : "div";
6704
6777
  return /* @__PURE__ */ jsxs(GlyphContext.Provider, {
6705
6778
  value,
@@ -6716,4 +6789,4 @@ function ThemeScope({ theme, mode, asChild, children, ...props }) {
6716
6789
  });
6717
6790
  }
6718
6791
  //#endregion
6719
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, AppShell, AspectRatio, Async, Avatar, AvatarGroup, Badge, Banner, Blockquote, Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, Calendar, Card, CardBody, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselDots, CarouselItem, CarouselNext, CarouselPrevious, Checkbox, Code, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, ColorModeProvider, ColorModeScript, Combobox, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandMenu, CommandSeparator, ConfirmProvider, Container, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, DatePicker, DateRangePicker, DescriptionItem, DescriptionList, Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, ErrorState, Eyebrow, Field, FieldContext, FileUpload, GlyphProvider, Grid, Heading, HoverCard, HoverCardContent, HoverCardTrigger, IconButton, Input, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Kbd, Label, Link, LoadingState, Modal, ModalClose, Navbar, NavigationMenu, NavigationMenuCard, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, NumberInput, PageHeader, Pagination, PasswordInput, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, ProgressCircle, Prose, REGEXP_ONLY_CHARS, REGEXP_ONLY_DIGITS, REGEXP_ONLY_DIGITS_AND_CHARS, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScrollArea, Section, SegmentedControl, Select, SelectItem, Separator, Sheet, SheetBody, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarHeader, SidebarItem, SidebarSection, SidebarTrigger, Skeleton, Slider, Spinner, Stack, StatCard, Stepper, Switch, Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, TagInput, Text, Textarea, ThemeScope, Timeline, TimelineItem, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TreeView, VisuallyHidden, accordionStyles, alertDialogStyles, alertStyles, appShellStyles, aspectRatioStyles, avatarStyles, badgeStyles, bannerStyles, blockquoteStyles, breadcrumbStyles, brutalist, buttonStyles, calendarStyles, cardStyles, carouselStyles, checkboxStyles, cn, codeBlockStyles, codeStyles, collapsibleStyles, comboboxStyles, commandMenuStyles, containerStyles, contextMenuStyles, copyButtonStyles, datePickerStyles, defineTheme, descriptionListStyles, dialogStyles, dropdownMenuStyles, editorial, eyebrowStyles, fieldControl, fieldStyles, fileUploadStyles, formatBytes, getColorModeScript, glyph, gridStyles, headingStyles, hoverCardStyles, inputOtpStyles, inputStyles, kbdStyles, labelStyles, linkStyles, matchesAccept, modalStyles, navbarStyles, navigationMenuStyles, numberInputStyles, pageHeaderStyles, paginationRange, paginationStyles, passwordInputStyles, passwordStrength, playful, popoverStyles, presets, progressCircleStyles, progressStyles, proseStyles, radioGroupStyles, resizableStyles, scrollAreaStyles, sectionStyles, segmentedControlStyles, selectStyles, separatorStyles, sheetStyles, sidebarStyles, skeletonStyles, sliderStyles, spinnerStyles, stackStyles, statCardStyles, stateStyles, stepperStyles, switchStyles, tableStyles, tabsStyles, tagInputStyles, textStyles, textareaStyles, themeToCss, timelineStyles, toast, toastStyles, toggleStyles, tokenVar, tooltipStyles, treeViewStyles, tv, useAppShell, useAsync, useColorMode, useCommandMenuShortcut, useComponentConfig, useConfirm, useControllableState, useFieldControl, useGlyph, useLinkComponent, useMediaQuery, useScrolled };
6792
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, AppShell, AspectRatio, Async, Avatar, AvatarGroup, Badge, Banner, Blockquote, Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, Calendar, Card, CardBody, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselDots, CarouselItem, CarouselNext, CarouselPrevious, Checkbox, Code, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, ColorModeProvider, ColorModeScript, Combobox, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandMenu, CommandSeparator, ConfirmProvider, Container, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, DatePicker, DateRangePicker, DescriptionItem, DescriptionList, Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, ErrorState, Eyebrow, Field, FieldContext, FileUpload, GlyphProvider, Grid, Heading, HoverCard, HoverCardContent, HoverCardTrigger, IconButton, Input, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Kbd, Label, Link, LoadingState, Modal, ModalClose, Navbar, NavigationMenu, NavigationMenuCard, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, NumberInput, PageHeader, Pagination, PasswordInput, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, ProgressCircle, Prose, REGEXP_ONLY_CHARS, REGEXP_ONLY_DIGITS, REGEXP_ONLY_DIGITS_AND_CHARS, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScrollArea, Section, SegmentedControl, Select, SelectItem, Separator, Sheet, SheetBody, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarHeader, SidebarItem, SidebarSection, SidebarTrigger, Skeleton, Slider, Spinner, Stack, StatCard, Stepper, Switch, Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, TagInput, Text, Textarea, ThemeScope, Timeline, TimelineItem, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TreeView, VisuallyHidden, accordionStyles, alertDialogStyles, alertStyles, appShellStyles, aspectRatioStyles, avatarStyles, badgeStyles, bannerStyles, blockquoteStyles, breadcrumbStyles, brutalist, buttonStyles, calendarStyles, cardStyles, carouselStyles, checkboxStyles, cn, codeBlockStyles, codeStyles, collapsibleStyles, comboboxStyles, commandMenuStyles, containerStyles, contextMenuStyles, copyButtonStyles, datePickerStyles, defineTheme, descriptionListStyles, dialogStyles, dropdownMenuStyles, editorial, eyebrowStyles, fieldControl, fieldStyles, fileUploadStyles, formatBytes, getColorModeScript, glyph, gridStyles, headingStyles, hoverCardStyles, inputOtpStyles, inputStyles, kbdStyles, labelStyles, linkStyles, matchesAccept, modalStyles, navbarStyles, navigationMenuStyles, numberInputStyles, pageHeaderStyles, paginationRange, paginationStyles, passwordInputStyles, passwordStrength, playful, popoverStyles, presets, progressCircleStyles, progressStyles, proseStyles, radioGroupStyles, resizableStyles, scrollAreaStyles, sectionStyles, segmentedControlStyles, selectStyles, separatorStyles, sheetStyles, sidebarStyles, skeletonStyles, sliderStyles, spinnerStyles, stackStyles, statCardStyles, stateStyles, stepperStyles, switchStyles, tableStyles, tabsStyles, tagInputStyles, textStyles, textareaStyles, themeToCss, timelineStyles, toast, toastStyles, toggleStyles, tokenVar, tooltipStyles, treeViewStyles, tv, useAppShell, useAsync, useColorMode, useCommandMenuShortcut, useComponentConfig, useConfirm, useControllableState, useFieldControl, useGlyph, useIsActive, useLinkComponent, useMediaQuery, useScrolled };
@@ -0,0 +1,18 @@
1
+ import { a as LinkComponentProps, n as GlyphRouter, r as IsActiveOptions } from "./glyph-context-DxGJrdvr.js";
2
+ //#region src/integrations/react-router.d.ts
3
+ /**
4
+ * React Router's `Link`, taking Glyph's `href` prop. Hash links, external URLs and `mailto:`
5
+ * stay plain anchors, so in-page anchors scroll and external links leave the app.
6
+ */
7
+ export declare function RouterLink({ href, ...props }: LinkComponentProps): import("react").JSX.Element;
8
+ /** Whether an in-app `href` matches a pathname, with React Router's `NavLink` semantics. */
9
+ export declare function matchPath(href: string, pathname: string, { end }?: IsActiveOptions): boolean;
10
+ /** Marks a link active when the current location matches it, like React Router's `NavLink`. */
11
+ export declare function useRouterIsActive(href: string, options?: IsActiveOptions): boolean;
12
+ /**
13
+ * Connects Glyph's navigation to React Router (v7 or later):
14
+ * `<GlyphProvider router={reactRouter}>`. Links render client-side, and Sidebar, Navbar
15
+ * and NavigationMenu items mark the current page automatically. Must render inside a router.
16
+ */
17
+ export declare const reactRouter: GlyphRouter;
18
+ //#endregion
@@ -0,0 +1,44 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { Link, useLocation } from "react-router";
4
+ //#region src/integrations/react-router.tsx
5
+ const isOutsideApp = (href) => href.startsWith("#") || href.startsWith("//") || /^[a-z][a-z\d+.-]*:/i.test(href);
6
+ /**
7
+ * React Router's `Link`, taking Glyph's `href` prop. Hash links, external URLs and `mailto:`
8
+ * stay plain anchors, so in-page anchors scroll and external links leave the app.
9
+ */
10
+ function RouterLink({ href, ...props }) {
11
+ if (isOutsideApp(href)) return /* @__PURE__ */ jsx("a", {
12
+ href,
13
+ ...props
14
+ });
15
+ return /* @__PURE__ */ jsx(Link, {
16
+ to: href,
17
+ ...props
18
+ });
19
+ }
20
+ const trimSlash = (path) => path.length > 1 ? path.replace(/\/+$/, "") : path;
21
+ /** Whether an in-app `href` matches a pathname, with React Router's `NavLink` semantics. */
22
+ function matchPath(href, pathname, { end = false } = {}) {
23
+ if (!href || isOutsideApp(href)) return false;
24
+ const target = trimSlash(new URL(href, "http://glyph.local").pathname);
25
+ const current = trimSlash(pathname);
26
+ if (end || target === "/") return current === target;
27
+ return current === target || current.startsWith(`${target}/`);
28
+ }
29
+ /** Marks a link active when the current location matches it, like React Router's `NavLink`. */
30
+ function useRouterIsActive(href, options) {
31
+ const { pathname } = useLocation();
32
+ return matchPath(href, pathname, options);
33
+ }
34
+ /**
35
+ * Connects Glyph's navigation to React Router (v7 or later):
36
+ * `<GlyphProvider router={reactRouter}>`. Links render client-side, and Sidebar, Navbar
37
+ * and NavigationMenu items mark the current page automatically. Must render inside a router.
38
+ */
39
+ const reactRouter = {
40
+ Link: RouterLink,
41
+ useIsActive: useRouterIsActive
42
+ };
43
+ //#endregion
44
+ export { RouterLink, matchPath, reactRouter, useRouterIsActive };
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { a as themedBorder, o as transition, r as focusRing, s as useComponentConfig, t as tv } from "./tv-DBAGlgXJ.js";
3
- import { c as AlertIcon, d as CheckIcon, g as InboxIcon, n as useFieldControl, o as Spinner, r as Button, v as MinusIcon } from "./field-context-anGPYXbW.js";
2
+ import { a as themedBorder, o as transition, r as focusRing, s as useComponentConfig, t as tv } from "./tv-DLbT66dC.js";
3
+ import { c as AlertIcon, d as CheckIcon, g as InboxIcon, n as useFieldControl, o as Spinner, r as Button, v as MinusIcon } from "./field-context-7I-cWvvS.js";
4
4
  import { Checkbox } from "radix-ui";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  //#region src/components/checkbox/checkbox.tsx