@mitcsutt/kiln-ui 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/dist/components/actions/CopyButton/CopyButton.d.ts +58 -0
  2. package/dist/components/actions/CopyButton/CopyButton.js +63 -0
  3. package/dist/components/actions/CopyButton/CopyButton.js.map +1 -0
  4. package/dist/components/actions/CopyButton/CopyButton.module.js +9 -0
  5. package/dist/components/actions/CopyButton/CopyButton.module.js.map +1 -0
  6. package/dist/components/actions/CopyButton/index.d.ts +4 -0
  7. package/dist/components/actions/CopyButton/useClipboard.d.ts +35 -0
  8. package/dist/components/actions/CopyButton/useClipboard.js +62 -0
  9. package/dist/components/actions/CopyButton/useClipboard.js.map +1 -0
  10. package/dist/components/actions/ToggleChip/ToggleChip.d.ts +6 -0
  11. package/dist/components/actions/ToggleChip/ToggleChip.js +26 -24
  12. package/dist/components/actions/ToggleChip/ToggleChip.js.map +1 -1
  13. package/dist/components/display/Badge/Badge.d.ts +6 -0
  14. package/dist/components/display/Badge/Badge.js +16 -15
  15. package/dist/components/display/Badge/Badge.js.map +1 -1
  16. package/dist/components/display/CodeBlock/CodeBlock.js +45 -58
  17. package/dist/components/display/CodeBlock/CodeBlock.js.map +1 -1
  18. package/dist/components/display/Swatch/Swatch.d.ts +52 -0
  19. package/dist/components/display/Swatch/Swatch.js +27 -0
  20. package/dist/components/display/Swatch/Swatch.js.map +1 -0
  21. package/dist/components/display/Swatch/Swatch.module.js +6 -0
  22. package/dist/components/display/Swatch/Swatch.module.js.map +1 -0
  23. package/dist/components/display/Swatch/index.d.ts +2 -0
  24. package/dist/components/display/Tag/Tag.d.ts +7 -0
  25. package/dist/components/display/Tag/Tag.js +31 -26
  26. package/dist/components/display/Tag/Tag.js.map +1 -1
  27. package/dist/components/feedback/Indicator/Indicator.d.ts +74 -0
  28. package/dist/components/feedback/Indicator/Indicator.js +50 -0
  29. package/dist/components/feedback/Indicator/Indicator.js.map +1 -0
  30. package/dist/components/feedback/Indicator/Indicator.module.js +12 -0
  31. package/dist/components/feedback/Indicator/Indicator.module.js.map +1 -0
  32. package/dist/components/feedback/Indicator/index.d.ts +2 -0
  33. package/dist/components/feedback/Toaster/Toaster.d.ts +71 -0
  34. package/dist/components/feedback/Toaster/Toaster.js +121 -0
  35. package/dist/components/feedback/Toaster/Toaster.js.map +1 -0
  36. package/dist/components/feedback/Toaster/Toaster.module.js +20 -0
  37. package/dist/components/feedback/Toaster/Toaster.module.js.map +1 -0
  38. package/dist/components/feedback/Toaster/index.d.ts +4 -0
  39. package/dist/components/feedback/Toaster/toast.d.ts +96 -0
  40. package/dist/components/feedback/Toaster/toast.js +131 -0
  41. package/dist/components/feedback/Toaster/toast.js.map +1 -0
  42. package/dist/components/inputs/Calendar/Calendar.d.ts +87 -0
  43. package/dist/components/inputs/Calendar/Calendar.js +217 -0
  44. package/dist/components/inputs/Calendar/Calendar.js.map +1 -0
  45. package/dist/components/inputs/Calendar/Calendar.module.js +18 -0
  46. package/dist/components/inputs/Calendar/Calendar.module.js.map +1 -0
  47. package/dist/components/inputs/Calendar/index.d.ts +2 -0
  48. package/dist/components/inputs/Combobox/Combobox.js +36 -44
  49. package/dist/components/inputs/Combobox/Combobox.js.map +1 -1
  50. package/dist/components/inputs/DateField/DateField.d.ts +17 -0
  51. package/dist/components/inputs/DateField/DateField.js +26 -0
  52. package/dist/components/inputs/DateField/DateField.js.map +1 -0
  53. package/dist/components/inputs/DateField/index.d.ts +2 -0
  54. package/dist/components/inputs/DateInput/DateInput.d.ts +75 -0
  55. package/dist/components/inputs/DateInput/DateInput.js +139 -0
  56. package/dist/components/inputs/DateInput/DateInput.js.map +1 -0
  57. package/dist/components/inputs/DateInput/index.d.ts +3 -0
  58. package/dist/components/inputs/DateRangeField/DateRangeField.d.ts +18 -3
  59. package/dist/components/inputs/DateRangeField/DateRangeField.js.map +1 -1
  60. package/dist/components/inputs/DateRangeField/DateRangeField.module.js +8 -3
  61. package/dist/components/inputs/DateRangeField/DateRangeField.module.js.map +1 -1
  62. package/dist/components/inputs/DateRangeField/DateRangePickerField.d.ts +48 -0
  63. package/dist/components/inputs/DateRangeField/DateRangePickerField.js +246 -0
  64. package/dist/components/inputs/DateRangeField/DateRangePickerField.js.map +1 -0
  65. package/dist/components/inputs/DateRangeField/index.d.ts +2 -0
  66. package/dist/components/inputs/DateTimeField/DateTimeField.d.ts +17 -0
  67. package/dist/components/inputs/DateTimeField/DateTimeField.js +26 -0
  68. package/dist/components/inputs/DateTimeField/DateTimeField.js.map +1 -0
  69. package/dist/components/inputs/DateTimeField/index.d.ts +2 -0
  70. package/dist/components/inputs/DateTimeInput/DateTimeInput.d.ts +83 -0
  71. package/dist/components/inputs/DateTimeInput/DateTimeInput.js +216 -0
  72. package/dist/components/inputs/DateTimeInput/DateTimeInput.js.map +1 -0
  73. package/dist/components/inputs/DateTimeInput/index.d.ts +2 -0
  74. package/dist/components/inputs/JsonField/JsonField.d.ts +23 -0
  75. package/dist/components/inputs/JsonField/JsonField.js +33 -0
  76. package/dist/components/inputs/JsonField/JsonField.js.map +1 -0
  77. package/dist/components/inputs/JsonField/index.d.ts +2 -0
  78. package/dist/components/inputs/JsonInput/JsonInput.d.ts +119 -0
  79. package/dist/components/inputs/JsonInput/JsonInput.js +282 -0
  80. package/dist/components/inputs/JsonInput/JsonInput.js.map +1 -0
  81. package/dist/components/inputs/JsonInput/JsonInput.module.js +16 -0
  82. package/dist/components/inputs/JsonInput/JsonInput.module.js.map +1 -0
  83. package/dist/components/inputs/JsonInput/index.d.ts +4 -0
  84. package/dist/components/inputs/JsonInput/json.d.ts +59 -0
  85. package/dist/components/inputs/JsonInput/json.js +231 -0
  86. package/dist/components/inputs/JsonInput/json.js.map +1 -0
  87. package/dist/components/inputs/TagsInput/TagsInput.js +5 -5
  88. package/dist/components/inputs/TimeField/TimeField.d.ts +17 -0
  89. package/dist/components/inputs/TimeField/TimeField.js +26 -0
  90. package/dist/components/inputs/TimeField/TimeField.js.map +1 -0
  91. package/dist/components/inputs/TimeField/index.d.ts +2 -0
  92. package/dist/components/inputs/TimeInput/TimeInput.d.ts +54 -0
  93. package/dist/components/inputs/TimeInput/TimeInput.js +107 -0
  94. package/dist/components/inputs/TimeInput/TimeInput.js.map +1 -0
  95. package/dist/components/inputs/TimeInput/index.d.ts +2 -0
  96. package/dist/components/inputs/internal/DateSegments.d.ts +80 -0
  97. package/dist/components/inputs/internal/DateSegments.js +345 -0
  98. package/dist/components/inputs/internal/DateSegments.js.map +1 -0
  99. package/dist/components/inputs/internal/dates.d.ts +101 -0
  100. package/dist/components/inputs/internal/dates.js +262 -0
  101. package/dist/components/inputs/internal/dates.js.map +1 -0
  102. package/dist/components/inputs/internal/filterOptions.d.ts +22 -1
  103. package/dist/components/inputs/internal/filterOptions.js +32 -1
  104. package/dist/components/inputs/internal/filterOptions.js.map +1 -1
  105. package/dist/components/inputs/internal/picker.d.ts +31 -0
  106. package/dist/components/inputs/internal/picker.js +29 -0
  107. package/dist/components/inputs/internal/picker.js.map +1 -0
  108. package/dist/components/inputs/internal/picker.module.js +15 -0
  109. package/dist/components/inputs/internal/picker.module.js.map +1 -0
  110. package/dist/components/inputs/internal/segments.module.js +10 -0
  111. package/dist/components/inputs/internal/segments.module.js.map +1 -0
  112. package/dist/components/layout/Collapse/Collapse.d.ts +39 -0
  113. package/dist/components/layout/Collapse/Collapse.js +24 -0
  114. package/dist/components/layout/Collapse/Collapse.js.map +1 -0
  115. package/dist/components/layout/Collapse/Collapse.module.js +6 -0
  116. package/dist/components/layout/Collapse/Collapse.module.js.map +1 -0
  117. package/dist/components/layout/Collapse/index.d.ts +2 -0
  118. package/dist/components/layout/Collapse/useHeightMotion.d.ts +16 -0
  119. package/dist/components/layout/Collapse/useHeightMotion.js +60 -0
  120. package/dist/components/layout/Collapse/useHeightMotion.js.map +1 -0
  121. package/dist/components/layout/ShowMore/ShowMore.d.ts +59 -0
  122. package/dist/components/layout/ShowMore/ShowMore.js +114 -0
  123. package/dist/components/layout/ShowMore/ShowMore.js.map +1 -0
  124. package/dist/components/layout/ShowMore/ShowMore.module.js +12 -0
  125. package/dist/components/layout/ShowMore/ShowMore.module.js.map +1 -0
  126. package/dist/components/layout/ShowMore/index.d.ts +2 -0
  127. package/dist/components/layout/Transition/Transition.d.ts +63 -0
  128. package/dist/components/layout/Transition/Transition.js +63 -0
  129. package/dist/components/layout/Transition/Transition.js.map +1 -0
  130. package/dist/components/layout/Transition/Transition.module.js +17 -0
  131. package/dist/components/layout/Transition/Transition.module.js.map +1 -0
  132. package/dist/components/layout/Transition/index.d.ts +2 -0
  133. package/dist/components/navigation/Collapsible/Collapsible.d.ts +49 -0
  134. package/dist/components/navigation/Collapsible/Collapsible.js +83 -0
  135. package/dist/components/navigation/Collapsible/Collapsible.js.map +1 -0
  136. package/dist/components/navigation/Collapsible/Collapsible.module.js +11 -0
  137. package/dist/components/navigation/Collapsible/Collapsible.module.js.map +1 -0
  138. package/dist/components/navigation/Collapsible/index.d.ts +2 -0
  139. package/dist/components/navigation/NavLinks/NavLinks.d.ts +32 -4
  140. package/dist/components/navigation/NavLinks/NavLinks.js +69 -39
  141. package/dist/components/navigation/NavLinks/NavLinks.js.map +1 -1
  142. package/dist/components/navigation/NavLinks/NavLinks.module.js +13 -3
  143. package/dist/components/navigation/NavLinks/NavLinks.module.js.map +1 -1
  144. package/dist/components/navigation/NavLinks/NavLinksGroup.d.ts +49 -0
  145. package/dist/components/navigation/NavLinks/NavLinksGroup.js +185 -0
  146. package/dist/components/navigation/NavLinks/NavLinksGroup.js.map +1 -0
  147. package/dist/components/navigation/NavLinks/context.d.ts +28 -0
  148. package/dist/components/navigation/NavLinks/context.js +13 -0
  149. package/dist/components/navigation/NavLinks/context.js.map +1 -0
  150. package/dist/components/navigation/NavLinks/index.d.ts +2 -0
  151. package/dist/components/navigation/NavLinks/treeKeys.d.ts +8 -0
  152. package/dist/components/navigation/NavLinks/treeKeys.js +57 -0
  153. package/dist/components/navigation/NavLinks/treeKeys.js.map +1 -0
  154. package/dist/components/overlays/CommandPalette/CommandPalette.d.ts +120 -0
  155. package/dist/components/overlays/CommandPalette/CommandPalette.js +386 -0
  156. package/dist/components/overlays/CommandPalette/CommandPalette.js.map +1 -0
  157. package/dist/components/overlays/CommandPalette/CommandPalette.module.js +31 -0
  158. package/dist/components/overlays/CommandPalette/CommandPalette.module.js.map +1 -0
  159. package/dist/components/overlays/CommandPalette/index.d.ts +2 -0
  160. package/dist/components/overlays/CommandPalette/shortcut.d.ts +28 -0
  161. package/dist/components/overlays/CommandPalette/shortcut.js +104 -0
  162. package/dist/components/overlays/CommandPalette/shortcut.js.map +1 -0
  163. package/dist/index.d.ts +40 -4
  164. package/dist/index.js +123 -100
  165. package/dist/styles.css +1 -1
  166. package/dist/themes/fiesta.css +1 -1
  167. package/dist/themes/flightdeck.css +1 -1
  168. package/dist/themes/ledger.css +1 -1
  169. package/dist/themes/monograph.css +1 -1
  170. package/dist/themes/riso.css +1 -1
  171. package/package.json +1 -1
  172. package/skills/custom-theme/SKILL.md +32 -0
  173. package/skills/custom-theme/references/colour.md +20 -0
  174. package/skills/custom-theme/references/motion.md +9 -0
  175. package/skills/custom-theme/references/tokens.md +35 -0
  176. package/skills/design-rules/SKILL.md +5 -2
  177. package/skills/setup-and-theming/SKILL.md +1 -1
  178. package/skills/setup-and-theming/references/paper.md +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Collapsible.js","names":["styles","Collapsible_module_default","CollapsibleContext","createContext","useCollapsible","context","useContext","part","CollapsibleRoot","forwardRef","openProp","rest","uncontrolled","setUncontrolled","useState","defaultOpen","open","contentId","useId","change","useLatest","onOpenChange","toggle","useCallback","next","cx","className","disabled","CollapsibleTrigger","props","onClick","event","asChild","Slot","ChevronDownIcon","CollapsibleContent","Collapse","Collapsible"],"sources":["../../../../src/components/navigation/Collapsible/Collapsible.tsx"],"sourcesContent":["import {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useId,\n useState,\n type ButtonHTMLAttributes,\n type HTMLAttributes,\n type MouseEvent,\n} from 'react'\nimport { Slot } from 'radix-ui'\nimport { cx } from '#utils/cx'\nimport { ChevronDownIcon } from '#icons'\nimport { Collapse, type CollapseProps } from '#components/layout/Collapse'\nimport { useLatest } from '#components/inputs/internal/useLatest'\nimport styles from './Collapsible.module.css'\n\ninterface CollapsibleContextValue {\n open: boolean\n disabled: boolean\n contentId: string\n toggle: () => void\n}\n\nconst CollapsibleContext = createContext<CollapsibleContextValue | null>(null)\n\nfunction useCollapsible(part: string): CollapsibleContextValue {\n const context = useContext(CollapsibleContext)\n if (!context) throw new Error(`Collapsible.${part} must be inside <Collapsible>.`)\n return context\n}\n\nexport interface CollapsibleProps extends HTMLAttributes<HTMLDivElement> {\n /** Controlled open state. */\n open?: boolean\n /** Uncontrolled initial state. Default `false`. */\n defaultOpen?: boolean\n /** Runs when the trigger asks to open or close. */\n onOpenChange?: (open: boolean) => void\n /** The trigger does nothing. */\n disabled?: boolean\n}\n\nconst CollapsibleRoot = forwardRef<HTMLDivElement, CollapsibleProps>(function Collapsible(\n {\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n disabled = false,\n className,\n children,\n ...rest\n },\n ref,\n) {\n const [uncontrolled, setUncontrolled] = useState(defaultOpen)\n const open = openProp ?? uncontrolled\n const contentId = useId()\n const change = useLatest(onOpenChange)\n const toggle = useCallback(() => {\n const next = !open\n if (openProp === undefined) setUncontrolled(next)\n change.current?.(next)\n }, [open, openProp, change])\n\n return (\n <CollapsibleContext.Provider value={{ open, disabled, contentId, toggle }}>\n <div\n ref={ref}\n className={cx(styles.root, className)}\n data-state={open ? 'open' : 'closed'}\n data-disabled={disabled || undefined}\n {...rest}\n >\n {children}\n </div>\n </CollapsibleContext.Provider>\n )\n})\n\nexport interface CollapsibleTriggerProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n /**\n * Render the single child (your own `Button`, a row, a heading's button) as the trigger\n * instead of Kiln's label-and-chevron button. It gets `aria-expanded`, `aria-controls` and the\n * click handler.\n */\n asChild?: boolean\n}\n\nconst CollapsibleTrigger = forwardRef<HTMLButtonElement, CollapsibleTriggerProps>(\n function CollapsibleTrigger({ asChild = false, onClick, className, children, ...rest }, ref) {\n const { open, disabled, contentId, toggle } = useCollapsible('Trigger')\n const props = {\n ref,\n type: 'button' as const,\n 'aria-expanded': open,\n 'aria-controls': contentId,\n 'data-state': open ? 'open' : 'closed',\n disabled: disabled || undefined,\n onClick: (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event)\n if (!event.defaultPrevented) toggle()\n },\n ...rest,\n }\n if (asChild) {\n return (\n <Slot.Root className={className} {...props}>\n {children}\n </Slot.Root>\n )\n }\n return (\n <button className={cx(styles.trigger, className)} {...props}>\n <span className={styles.label}>{children}</span>\n <ChevronDownIcon className={styles.chevron} />\n </button>\n )\n },\n)\n\n/** `Collapse`'s props except `open`, which comes from the `Collapsible`. */\nexport type CollapsibleContentProps = Omit<CollapseProps, 'open'>\n\nconst CollapsibleContent = forwardRef<HTMLDivElement, CollapsibleContentProps>(\n function CollapsibleContent({ className, ...rest }, ref) {\n const { open, contentId } = useCollapsible('Content')\n return (\n <Collapse\n ref={ref}\n id={contentId}\n open={open}\n className={cx(styles.content, className)}\n {...rest}\n />\n )\n },\n)\n\n/**\n * A trigger that shows and hides one block of content: \"Advanced options\", \"Show the formula\".\n *\n * @remarks\n * `Collapsible` is a button and the content it opens, wired together: the button gets\n * `aria-expanded` and `aria-controls`, and the content animates with the same height motion as\n * {@link Collapse | Collapse}. Unlike {@link Accordion | Accordion} it brings no heading, no\n * divider and no list of items, so it fits inside a form, a card or a sidebar.\n *\n * The default trigger is a label and a chevron that turns. To use your own control, such as a\n * ghost `Button`, pass it to `Collapsible.Trigger` with `asChild`.\n *\n * @privateRemarks\n * Not Radix Collapsible: its Content owns mounting and `hidden`, so it can't run Collapse's\n * height motion. The context here is the same contract (open, contentId, toggle) with Kiln's\n * Collapse underneath.\n *\n * <Collapsible>\n * <Collapsible.Trigger>Advanced options</Collapsible.Trigger>\n * <Collapsible.Content>…</Collapsible.Content>\n * </Collapsible>\n */\nexport const Collapsible = Object.assign(CollapsibleRoot, {\n Root: CollapsibleRoot,\n Trigger: CollapsibleTrigger,\n Content: CollapsibleContent,\n})\n"],"mappings":";;;;;;;;;AAyBA,IAAME,IAAqBC,EAA8C,IAAI;AAE7E,SAASC,EAAe,GAAuC;CAC7D,IAAMC,IAAUC,EAAWJ,CAAkB;CAC7C,IAAI,CAACG,GAAS,MAAU,MAAM,eAAeE,EAAK,+BAA+B;CACjF,OAAOF;AACT;AAaA,IAAMG,IAAkBC,EAA6C,SACnE,EACE,MAAMC,GACN,iBAAc,IACd,iBACA,cAAW,IACX,cACA,aACA,GAAGC,KAEL,GACA;CACA,IAAM,CAACC,GAAcC,KAAmBC,EAASC,CAAW,GACtDC,IAAON,KAAYE,GACnBK,IAAYC,EAAM,GAClBC,IAASC,EAAUC,CAAY,GAC/BC,IAASC,QAAkB;EAC/B,IAAMC,IAAO,CAACR;EAEd,AADIN,MAAa,KAAA,KAAWG,EAAgBW,CAAI,GAChDL,EAAO,UAAUK,CAAI;CACvB,GAAG;EAACR;EAAMN;EAAUS;CAAM,CAAC;CAE3B,OACE,kBAACjB,EAAmB,UAApB;EAA6B,OAAO;GAAE;GAAM;GAAU;GAAW;EAAO;EACtE,UAAA,kBAAC,OAAD;GACO;GACL,WAAWuB,EAAGzB,EAAO,MAAM0B,CAAS;GACpC,cAAYV,IAAO,SAAS;GAC5B,iBAAeW,KAAY,KAAA;GAC3B,GAAIhB;GAEH;EACE,CAAA;CACsB,CAAA;AAEjC,CAAC,GAWKiB,IAAqBnB,EACzB,SAA4B,EAAE,aAAU,IAAO,YAAS,cAAW,aAAU,GAAGE,KAAQ,GAAK;CAC3F,IAAM,EAAE,SAAM,aAAU,cAAW,cAAWP,EAAe,SAAS,GAChEyB,IAAQ;EACZ;EACA,MAAM;EACN,iBAAiBb;EACjB,iBAAiBC;EACjB,cAAcD,IAAO,SAAS;EAC9B,UAAUW,KAAY,KAAA;EACtB,UAAU,MAAyC;GAEjD,AADAG,IAAUC,CAAK,GACVA,EAAM,oBAAkBT,EAAO;EACtC;EACA,GAAGX;CACL;CAQA,OAPIqB,IAEA,kBAACC,EAAK,MAAN;EAAsB;EAAW,GAAIJ;EAClC;CACQ,CAAA,IAIb,kBAAC,UAAD;EAAQ,WAAWJ,EAAGzB,EAAO,SAAS0B,CAAS;EAAG,GAAIG;EAAtD,UAAA,CACE,kBAAC,QAAD;GAAM,WAAW7B,EAAO;GAAQ;EAAe,CAAA,GAC/C,kBAACkC,GAAD,EAAiB,WAAWlC,EAAO,QAAU,CAAA,CACvC;;AAEZ,CACF,GAKMmC,IAAqB1B,EACzB,SAA4B,EAAE,cAAW,GAAGE,KAAQ,GAAK;CACvD,IAAM,EAAE,SAAM,iBAAcP,EAAe,SAAS;CACpD,OACE,kBAACgC,GAAD;EACO;EACL,IAAInB;EACE;EACN,WAAWQ,EAAGzB,EAAO,SAAS0B,CAAS;EACvC,GAAIf;CACL,CAAA;AAEL,CACF,GAwBa0B,IAAc,OAAO,OAAO7B,GAAiB;CACxD,MAAMA;CACN,SAASoB;CACT,SAASO;AACX,CAAC"}
@@ -0,0 +1,11 @@
1
+ //#region src/components/navigation/Collapsible/Collapsible.module.css
2
+ var e = "kiln-Collapsible__root", t = "kiln-Collapsible__trigger", n = "kiln-Collapsible__label", r = "kiln-Collapsible__chevron", i = {
3
+ root: e,
4
+ trigger: t,
5
+ label: n,
6
+ chevron: r
7
+ };
8
+ //#endregion
9
+ export { r as chevron, i as default, n as label, e as root, t as trigger };
10
+
11
+ //# sourceMappingURL=Collapsible.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Collapsible.module.js","names":["root","trigger","label","chevron","Collapsible_module_default"],"sources":["../../../../src/components/navigation/Collapsible/Collapsible.module.css"],"sourcesContent":["/*\n * Component tokens: none. The default trigger matches a small Accordion trigger: a label in the\n * label role with a chevron after it, so a Collapsible inside a form reads like the rest of the\n * page's disclosures.\n */\n.root {\n min-inline-size: 0;\n}\n\n.trigger {\n display: inline-flex;\n align-items: center;\n gap: var(--space-2);\n min-block-size: max(2.75rem, var(--control-sm));\n padding: 0;\n border: 0;\n background: transparent;\n color: var(--color-ink);\n font-family: var(--font-text);\n font-size: var(--text-sm);\n font-weight: var(--label-weight);\n letter-spacing: var(--label-tracking);\n text-transform: var(--label-transform);\n line-height: var(--leading-snug);\n text-align: start;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n}\n\n.trigger:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: 2px;\n border-radius: var(--radius-field);\n}\n\n.trigger:disabled {\n cursor: not-allowed;\n opacity: 0.45;\n}\n\n.label {\n min-inline-size: 0;\n}\n\n.chevron {\n flex: none;\n font-size: 1rem;\n color: var(--color-ink-subtle);\n transition:\n rotate var(--dur-2) var(--ease-spring),\n color var(--dur-1) var(--ease-out);\n}\n@media (hover: hover) {\n .trigger:hover:not(:disabled) .chevron {\n color: var(--color-ink);\n }\n}\n.trigger[data-state='open'] .chevron {\n rotate: 180deg;\n color: var(--color-ink);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .chevron {\n transition: none;\n }\n}\n"],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export { Collapsible } from './Collapsible.js';
2
+ export type { CollapsibleProps, CollapsibleTriggerProps, CollapsibleContentProps, } from './Collapsible.js';
@@ -1,8 +1,8 @@
1
- import { type AnchorHTMLAttributes, type HTMLAttributes } from 'react';
1
+ import { type AnchorHTMLAttributes, type HTMLAttributes, type ReactNode } from 'react';
2
2
  import { type Space } from '../../../utils/tokens.js';
3
3
  import { type VisibilityProps } from '../../../utils/visibility.js';
4
- export type NavLinksOrientation = 'horizontal' | 'vertical';
5
- export type NavLinksSize = 'sm' | 'md';
4
+ import { type NavLinksOrientation, type NavLinksSize } from './context.js';
5
+ export type { NavLinksOrientation, NavLinksSize } from './context.js';
6
6
  export interface NavLinksProps extends HTMLAttributes<HTMLElement>, VisibilityProps {
7
7
  /** Names the landmark. Required when a page has more than one nav. Default "Main". */
8
8
  label?: string;
@@ -24,6 +24,10 @@ export interface NavLinksItemProps extends AnchorHTMLAttributes<HTMLAnchorElemen
24
24
  active?: boolean;
25
25
  /** Render the single child (e.g. a router `<Link>`) instead of an `<a>`. */
26
26
  asChild?: boolean;
27
+ /** A glyph before the label. Decorative: the label names the link. */
28
+ icon?: ReactNode;
29
+ /** A count or status after the label, such as a `Badge` or a `StatusDot`. */
30
+ trailing?: ReactNode;
27
31
  }
28
32
  /**
29
33
  * A labelled navigation landmark with a list of links. Horizontal for headers, vertical for
@@ -34,10 +38,34 @@ export interface NavLinksItemProps extends AnchorHTMLAttributes<HTMLAnchorElemen
34
38
  * under the label (horizontal) or an accent dot beside it (vertical), and gets
35
39
  * `aria-current="page"`. These docs' header and sidebar are `NavLinks`.
36
40
  *
41
+ * ## Groups
42
+ *
43
+ * `NavLinksGroup` nests links under a section, and groups nest inside groups. In a vertical nav a
44
+ * group is a button that opens its links below it, indented on a guide rule; in a horizontal nav
45
+ * it opens them in a panel under the button, so a top bar gets one level of nesting. It's a
46
+ * separate import, so a nav without groups doesn't load the panel and animation code they use.
47
+ *
48
+ * A group that holds the current page opens by itself and shows a ring while it's closed, and the
49
+ * current link keeps `aria-current="page"`. Give a group `href` (or a router `link`) when the
50
+ * section has a landing page of its own: the label becomes a link, with a separate button to open
51
+ * and close it. Items and groups take an `icon` and a `trailing` count or status. Labels stay at
52
+ * body size at every depth.
53
+ *
54
+ * With groups in a vertical nav, the arrow keys work as in a tree: Up and Down move between the
55
+ * visible links, Right opens a group and Left closes it or steps out to its parent, Home and End
56
+ * jump to either end, and typing a letter jumps to the next link starting with it. Tab still
57
+ * reaches every link.
58
+ *
59
+ * `BottomNav` stays flat: a phone's tab bar has room for four or five destinations and nothing
60
+ * under them.
61
+ *
37
62
  * @privateRemarks
38
63
  * Site / app navigation: a labelled `<nav>` with a list of links. The current page is
39
64
  * marked with a short accent bar under the label (horizontal) or an accent dot beside
40
- * it (vertical) — a mark, not a glowing pill.
65
+ * it (vertical) — a mark, not a glowing pill. A flat NavLinks (no groups) renders exactly what
66
+ * it did before groups existed and ignores arrow keys. NavLinksGroup isn't attached as
67
+ * `NavLinks.Group`: `Object.assign` keeps every part in the bundle, and a group brings Popover and
68
+ * Collapse, so a flat nav would pay for them (ADR 0044).
41
69
  */
42
70
  export declare const NavLinks: import("react").ForwardRefExoticComponent<NavLinksProps & import("react").RefAttributes<HTMLElement>> & {
43
71
  Item: import("react").ForwardRefExoticComponent<NavLinksItemProps & import("react").RefAttributes<HTMLAnchorElement>>;
@@ -1,57 +1,87 @@
1
1
  import { cx as e } from "../../../utils/cx.js";
2
2
  import { space as t } from "../../../utils/tokens.js";
3
3
  import { visibilityClass as n } from "../../../utils/visibility.js";
4
- import r from "./NavLinks.module.js";
5
- import { createContext as i, forwardRef as a, useContext as o } from "react";
6
- import { jsx as s } from "react/jsx-runtime";
7
- import { Slot as c } from "radix-ui";
4
+ import { NavLinksContext as r, NavLinksGroupContext as i } from "./context.js";
5
+ import a from "./NavLinks.module.js";
6
+ import { forwardRef as o, useContext as s, useEffect as c, useId as l, useLayoutEffect as u, useRef as d } from "react";
7
+ import { jsx as f, jsxs as p } from "react/jsx-runtime";
8
+ import { Slot as m } from "radix-ui";
8
9
  //#region src/components/navigation/NavLinks/NavLinks.tsx
9
- var l = i({
10
- orientation: "horizontal",
11
- size: "md"
12
- }), u = a(function({ label: i = "Main", orientation: a = "horizontal", gap: o, size: c = "md", hideBelow: u, hideAbove: d, className: f, children: p, ...m }, h) {
13
- return /* @__PURE__ */ s(l.Provider, {
10
+ var h = typeof document > "u" ? c : u, g = o(function({ label: i = "Main", orientation: o = "horizontal", gap: s, size: c = "md", hideBelow: l, hideAbove: u, className: p, onKeyDown: m, children: h, ...g }, _) {
11
+ let v = d(null);
12
+ return /* @__PURE__ */ f(r.Provider, {
14
13
  value: {
15
- orientation: a,
16
- size: c
14
+ orientation: o,
15
+ size: c,
16
+ treeKeysRef: v
17
17
  },
18
- children: /* @__PURE__ */ s("nav", {
19
- ref: h,
18
+ children: /* @__PURE__ */ f("nav", {
19
+ ref: _,
20
20
  "aria-label": i,
21
- className: e(r.nav, n({
22
- hideBelow: u,
23
- hideAbove: d
24
- }), f),
25
- "data-orientation": a,
21
+ className: e(a.nav, n({
22
+ hideBelow: l,
23
+ hideAbove: u
24
+ }), p),
25
+ "data-orientation": o,
26
26
  "data-size": c,
27
- ...m,
28
- children: /* @__PURE__ */ s("ul", {
29
- className: r.list,
27
+ onKeyDown: (e) => {
28
+ m?.(e), !e.defaultPrevented && o === "vertical" && v.current?.(e);
29
+ },
30
+ ...g,
31
+ children: /* @__PURE__ */ f("ul", {
32
+ className: a.list,
30
33
  role: "list",
31
- style: o === void 0 ? void 0 : { "--_gap": t(o) },
32
- children: p
34
+ style: s === void 0 ? void 0 : { "--_gap": t(s) },
35
+ children: h
33
36
  })
34
37
  })
35
38
  });
36
- }), d = a(function({ active: t = !1, asChild: i = !1, hideBelow: a, hideAbove: u, className: d, ...f }, p) {
37
- let { orientation: m, size: h } = o(l), g = i ? c.Root : "a";
38
- return /* @__PURE__ */ s("li", {
39
- className: e(r.item, n({
40
- hideBelow: a,
41
- hideAbove: u
39
+ }), _ = o(function({ active: t = !1, asChild: o = !1, icon: c, trailing: u, hideBelow: d, hideAbove: g, className: _, children: v, ...y }, b) {
40
+ let { orientation: x, size: S } = s(r), C = s(i), w = l();
41
+ h(() => {
42
+ if (C) return C.report(w, t), () => {
43
+ C.report(w, !1);
44
+ };
45
+ }, [
46
+ C,
47
+ w,
48
+ t
49
+ ]);
50
+ let T = o ? m.Root : "a", E = {
51
+ ref: b,
52
+ className: e(a.link, _),
53
+ "data-orientation": x,
54
+ "data-size": S,
55
+ "data-active": t || void 0,
56
+ "aria-current": t ? "page" : void 0,
57
+ ...y
58
+ };
59
+ return /* @__PURE__ */ f("li", {
60
+ className: e(a.item, n({
61
+ hideBelow: d,
62
+ hideAbove: g
42
63
  })),
43
- children: /* @__PURE__ */ s(g, {
44
- ref: p,
45
- className: e(r.link, d),
46
- "data-orientation": m,
47
- "data-size": h,
48
- "data-active": t || void 0,
49
- "aria-current": t ? "page" : void 0,
50
- ...f
64
+ children: c === void 0 && u === void 0 ? /* @__PURE__ */ f(T, {
65
+ ...E,
66
+ children: v
67
+ }) : /* @__PURE__ */ p(T, {
68
+ ...E,
69
+ children: [
70
+ c === void 0 ? null : /* @__PURE__ */ f("span", {
71
+ className: a.icon,
72
+ "aria-hidden": "true",
73
+ children: c
74
+ }),
75
+ o ? /* @__PURE__ */ f(m.Slottable, { children: v }) : v,
76
+ u === void 0 ? null : /* @__PURE__ */ f("span", {
77
+ className: a.trailing,
78
+ children: u
79
+ })
80
+ ]
51
81
  })
52
82
  });
53
- }), f = Object.assign(u, { Item: d });
83
+ }), v = Object.assign(g, { Item: _ });
54
84
  //#endregion
55
- export { f as NavLinks };
85
+ export { v as NavLinks };
56
86
 
57
87
  //# sourceMappingURL=NavLinks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"NavLinks.js","names":["styles","NavLinks_module_default","NavLinksContext","createContext","NavLinksRoot","forwardRef","rest","label","cx","visibilityClass","className","orientation","size","gap","space","NavLinksItem","useContext","Comp","asChild","Slot","active","NavLinks"],"sources":["../../../../src/components/navigation/NavLinks/NavLinks.tsx"],"sourcesContent":["import {\n createContext,\n forwardRef,\n useContext,\n type AnchorHTMLAttributes,\n type CSSProperties,\n type HTMLAttributes,\n} from 'react'\nimport { Slot } from 'radix-ui'\nimport { cx } from '#utils/cx'\nimport { space, type Space } from '#utils/tokens'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\nimport styles from './NavLinks.module.css'\n\nexport type NavLinksOrientation = 'horizontal' | 'vertical'\nexport type NavLinksSize = 'sm' | 'md'\n\nexport interface NavLinksProps extends HTMLAttributes<HTMLElement>, VisibilityProps {\n /** Names the landmark. Required when a page has more than one nav. Default \"Main\". */\n label?: string\n /** `horizontal` (default) for headers; `vertical` for sidebars and footers. */\n orientation?: NavLinksOrientation\n /**\n * Space between links, as a step on the space scale. Default `5` horizontal, `0`\n * vertical (vertical items are already a full control tall).\n */\n gap?: Space\n /**\n * `md` (default), body-size labels for page headers; `sm`, one step smaller, for sidebars,\n * dense toolbars, footers and sub-navs. Neither drops to the caption steps.\n */\n size?: NavLinksSize\n}\n\ninterface NavLinksContextValue {\n orientation: NavLinksOrientation\n size: NavLinksSize\n}\n\nconst NavLinksContext = createContext<NavLinksContextValue>({\n orientation: 'horizontal',\n size: 'md',\n})\n\nconst NavLinksRoot = forwardRef<HTMLElement, NavLinksProps>(function NavLinks(\n {\n label = 'Main',\n orientation = 'horizontal',\n gap,\n size = 'md',\n hideBelow,\n hideAbove,\n className,\n children,\n ...rest\n },\n ref,\n) {\n return (\n <NavLinksContext.Provider value={{ orientation, size }}>\n <nav\n ref={ref}\n aria-label={label}\n className={cx(styles.nav, visibilityClass({ hideBelow, hideAbove }), className)}\n data-orientation={orientation}\n data-size={size}\n {...rest}\n >\n <ul\n className={styles.list}\n role=\"list\"\n style={gap === undefined ? undefined : ({ '--_gap': space(gap) } as CSSProperties)}\n >\n {children}\n </ul>\n </nav>\n </NavLinksContext.Provider>\n )\n})\n\nexport interface NavLinksItemProps\n extends AnchorHTMLAttributes<HTMLAnchorElement>, VisibilityProps {\n /** The current page. Sets `aria-current=\"page\"` and draws the marker. */\n active?: boolean\n /** Render the single child (e.g. a router `<Link>`) instead of an `<a>`. */\n asChild?: boolean\n}\n\n/**\n * One link. For router links: `<NavLinks.Item asChild active={isActive}><Link to=\"/work\">Work</Link></NavLinks.Item>`.\n * `hideBelow`/`hideAbove` hide the whole list item (e.g. a secondary link on phones).\n */\nconst NavLinksItem = forwardRef<HTMLAnchorElement, NavLinksItemProps>(function NavLinksItem(\n { active = false, asChild = false, hideBelow, hideAbove, className, ...rest },\n ref,\n) {\n const { orientation, size } = useContext(NavLinksContext)\n const Comp = asChild ? Slot.Root : 'a'\n return (\n <li className={cx(styles.item, visibilityClass({ hideBelow, hideAbove }))}>\n <Comp\n ref={ref}\n className={cx(styles.link, className)}\n data-orientation={orientation}\n data-size={size}\n data-active={active || undefined}\n aria-current={active ? 'page' : undefined}\n {...rest}\n />\n </li>\n )\n})\n\n/**\n * A labelled navigation landmark with a list of links. Horizontal for headers, vertical for\n * sidebars.\n *\n * @remarks\n * `NavLinks` is a `<nav>` with a list of links. The current page is marked with a short accent bar\n * under the label (horizontal) or an accent dot beside it (vertical), and gets\n * `aria-current=\"page\"`. These docs' header and sidebar are `NavLinks`.\n *\n * @privateRemarks\n * Site / app navigation: a labelled `<nav>` with a list of links. The current page is\n * marked with a short accent bar under the label (horizontal) or an accent dot beside\n * it (vertical) — a mark, not a glowing pill.\n */\nexport const NavLinks = Object.assign(NavLinksRoot, { Item: NavLinksItem })\n"],"mappings":";;;;;;;;AAuCA,IAAME,IAAkBC,EAAoC;CAC1D,aAAa;CACb,MAAM;AACR,CAAC,GAEKC,IAAeC,EAAuC,SAC1D,EACE,WAAQ,QACR,iBAAc,cACd,QACA,UAAO,MACP,cACA,cACA,cACA,aACA,GAAGC,KAEL,GACA;CACA,OACE,kBAACJ,EAAgB,UAAjB;EAA0B,OAAO;GAAE;GAAa;EAAK;EACnD,UAAA,kBAAC,OAAD;GACO;GACL,cAAYK;GACZ,WAAWC,EAAGR,EAAO,KAAKS,EAAgB;IAAE;IAAW;GAAU,CAAC,GAAGC,CAAS;GAC9E,oBAAkBC;GAClB,aAAWC;GACX,GAAIN;GAEJ,UAAA,kBAAC,MAAD;IACE,WAAWN,EAAO;IAClB,MAAK;IACL,OAAOa,MAAQ,KAAA,IAAY,KAAA,IAAa,EAAE,UAAUC,EAAMD,CAAG,EAAE;IAE9D;GACC,CAAA;EACD,CAAA;CACmB,CAAA;AAE9B,CAAC,GAcKE,IAAeV,EAAiD,SACpE,EAAE,YAAS,IAAO,aAAU,IAAO,cAAW,cAAW,cAAW,GAAGC,KACvE,GACA;CACA,IAAM,EAAE,gBAAa,YAASU,EAAWd,CAAe,GAClDe,IAAOC,IAAUC,EAAK,OAAO;CACnC,OACE,kBAAC,MAAD;EAAI,WAAWX,EAAGR,EAAO,MAAMS,EAAgB;GAAE;GAAW;EAAU,CAAC,CAAC;EACtE,UAAA,kBAACQ,GAAD;GACO;GACL,WAAWT,EAAGR,EAAO,MAAMU,CAAS;GACpC,oBAAkBC;GAClB,aAAWC;GACX,eAAaQ,KAAU,KAAA;GACvB,gBAAcA,IAAS,SAAS,KAAA;GAChC,GAAId;EACL,CAAA;CACC,CAAA;AAER,CAAC,GAgBYe,IAAW,OAAO,OAAOjB,GAAc,EAAE,MAAMW,EAAa,CAAC"}
1
+ {"version":3,"file":"NavLinks.js","names":["styles","NavLinks_module_default","useIsomorphicLayoutEffect","useEffect","useLayoutEffect","NavLinksRoot","forwardRef","rest","treeKeysRef","useRef","NavLinksContext","label","cx","visibilityClass","className","orientation","size","onKeyDown","event","gap","space","NavLinksItem","useContext","group","NavLinksGroupContext","id","useId","active","Comp","asChild","Slot","link","icon","trailing","children","NavLinks"],"sources":["../../../../src/components/navigation/NavLinks/NavLinks.tsx"],"sourcesContent":["import {\n forwardRef,\n useContext,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n type AnchorHTMLAttributes,\n type CSSProperties,\n type HTMLAttributes,\n type ReactNode,\n} from 'react'\nimport { Slot } from 'radix-ui'\nimport { cx } from '#utils/cx'\nimport { space, type Space } from '#utils/tokens'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\nimport {\n NavLinksContext,\n NavLinksGroupContext,\n type NavLinksOrientation,\n type NavLinksSize,\n type TreeKeyDown,\n} from './context'\nimport styles from './NavLinks.module.css'\n\nexport type { NavLinksOrientation, NavLinksSize } from './context'\n\nconst useIsomorphicLayoutEffect = typeof document === 'undefined' ? useEffect : useLayoutEffect\n\nexport interface NavLinksProps extends HTMLAttributes<HTMLElement>, VisibilityProps {\n /** Names the landmark. Required when a page has more than one nav. Default \"Main\". */\n label?: string\n /** `horizontal` (default) for headers; `vertical` for sidebars and footers. */\n orientation?: NavLinksOrientation\n /**\n * Space between links, as a step on the space scale. Default `5` horizontal, `0`\n * vertical (vertical items are already a full control tall).\n */\n gap?: Space\n /**\n * `md` (default), body-size labels for page headers; `sm`, one step smaller, for sidebars,\n * dense toolbars, footers and sub-navs. Neither drops to the caption steps.\n */\n size?: NavLinksSize\n}\n\nconst NavLinksRoot = forwardRef<HTMLElement, NavLinksProps>(function NavLinks(\n {\n label = 'Main',\n orientation = 'horizontal',\n gap,\n size = 'md',\n hideBelow,\n hideAbove,\n className,\n onKeyDown,\n children,\n ...rest\n },\n ref,\n) {\n // The arrow keys of a nav with groups. Its groups set the handler, so a flat nav doesn't carry\n // the code.\n const treeKeysRef = useRef<TreeKeyDown | null>(null)\n return (\n <NavLinksContext.Provider value={{ orientation, size, treeKeysRef }}>\n {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- arrow keys pressed on the links and group buttons inside bubble here; the nav itself is never focused */}\n <nav\n ref={ref}\n aria-label={label}\n className={cx(styles.nav, visibilityClass({ hideBelow, hideAbove }), className)}\n data-orientation={orientation}\n data-size={size}\n onKeyDown={(event) => {\n onKeyDown?.(event)\n if (!event.defaultPrevented && orientation === 'vertical') treeKeysRef.current?.(event)\n }}\n {...rest}\n >\n <ul\n className={styles.list}\n role=\"list\"\n style={gap === undefined ? undefined : ({ '--_gap': space(gap) } as CSSProperties)}\n >\n {children}\n </ul>\n </nav>\n </NavLinksContext.Provider>\n )\n})\n\nexport interface NavLinksItemProps\n extends AnchorHTMLAttributes<HTMLAnchorElement>, VisibilityProps {\n /** The current page. Sets `aria-current=\"page\"` and draws the marker. */\n active?: boolean\n /** Render the single child (e.g. a router `<Link>`) instead of an `<a>`. */\n asChild?: boolean\n /** A glyph before the label. Decorative: the label names the link. */\n icon?: ReactNode\n /** A count or status after the label, such as a `Badge` or a `StatusDot`. */\n trailing?: ReactNode\n}\n\n/**\n * One link. For router links: `<NavLinks.Item asChild active={isActive}><Link to=\"/work\">Work</Link></NavLinks.Item>`.\n * `hideBelow`/`hideAbove` hide the whole list item (e.g. a secondary link on phones).\n */\nconst NavLinksItem = forwardRef<HTMLAnchorElement, NavLinksItemProps>(function NavLinksItem(\n {\n active = false,\n asChild = false,\n icon,\n trailing,\n hideBelow,\n hideAbove,\n className,\n children,\n ...rest\n },\n ref,\n) {\n const { orientation, size } = useContext(NavLinksContext)\n const group = useContext(NavLinksGroupContext)\n const id = useId()\n // Inside a group, say whether this is the current page, so the group can show it holds it.\n useIsomorphicLayoutEffect(() => {\n if (!group) return\n group.report(id, active)\n return () => {\n group.report(id, false)\n }\n }, [group, id, active])\n\n const Comp = asChild ? Slot.Root : 'a'\n const link = {\n ref,\n className: cx(styles.link, className),\n 'data-orientation': orientation,\n 'data-size': size,\n 'data-active': active || undefined,\n 'aria-current': active ? ('page' as const) : undefined,\n ...rest,\n }\n return (\n <li className={cx(styles.item, visibilityClass({ hideBelow, hideAbove }))}>\n {icon === undefined && trailing === undefined ? (\n <Comp {...link}>{children}</Comp>\n ) : (\n // Separate children, not a fragment: with asChild, Slot finds the Slottable among them.\n <Comp {...link}>\n {icon !== undefined ? (\n <span className={styles.icon} aria-hidden=\"true\">\n {icon}\n </span>\n ) : null}\n {asChild ? <Slot.Slottable>{children}</Slot.Slottable> : children}\n {trailing !== undefined ? <span className={styles.trailing}>{trailing}</span> : null}\n </Comp>\n )}\n </li>\n )\n})\n\n/**\n * A labelled navigation landmark with a list of links. Horizontal for headers, vertical for\n * sidebars.\n *\n * @remarks\n * `NavLinks` is a `<nav>` with a list of links. The current page is marked with a short accent bar\n * under the label (horizontal) or an accent dot beside it (vertical), and gets\n * `aria-current=\"page\"`. These docs' header and sidebar are `NavLinks`.\n *\n * ## Groups\n *\n * `NavLinksGroup` nests links under a section, and groups nest inside groups. In a vertical nav a\n * group is a button that opens its links below it, indented on a guide rule; in a horizontal nav\n * it opens them in a panel under the button, so a top bar gets one level of nesting. It's a\n * separate import, so a nav without groups doesn't load the panel and animation code they use.\n *\n * A group that holds the current page opens by itself and shows a ring while it's closed, and the\n * current link keeps `aria-current=\"page\"`. Give a group `href` (or a router `link`) when the\n * section has a landing page of its own: the label becomes a link, with a separate button to open\n * and close it. Items and groups take an `icon` and a `trailing` count or status. Labels stay at\n * body size at every depth.\n *\n * With groups in a vertical nav, the arrow keys work as in a tree: Up and Down move between the\n * visible links, Right opens a group and Left closes it or steps out to its parent, Home and End\n * jump to either end, and typing a letter jumps to the next link starting with it. Tab still\n * reaches every link.\n *\n * `BottomNav` stays flat: a phone's tab bar has room for four or five destinations and nothing\n * under them.\n *\n * @privateRemarks\n * Site / app navigation: a labelled `<nav>` with a list of links. The current page is\n * marked with a short accent bar under the label (horizontal) or an accent dot beside\n * it (vertical) — a mark, not a glowing pill. A flat NavLinks (no groups) renders exactly what\n * it did before groups existed and ignores arrow keys. NavLinksGroup isn't attached as\n * `NavLinks.Group`: `Object.assign` keeps every part in the bundle, and a group brings Popover and\n * Collapse, so a flat nav would pay for them (ADR 0044).\n */\nexport const NavLinks = Object.assign(NavLinksRoot, { Item: NavLinksItem })\n"],"mappings":";;;;;;;;;AA2BA,IAAME,IAA4B,OAAO,WAAa,MAAcC,IAAYC,GAmB1EC,IAAeC,EAAuC,SAC1D,EACE,WAAQ,QACR,iBAAc,cACd,QACA,UAAO,MACP,cACA,cACA,cACA,cACA,aACA,GAAGC,KAEL,GACA;CAGA,IAAMC,IAAcC,EAA2B,IAAI;CACnD,OACE,kBAACC,EAAgB,UAAjB;EAA0B,OAAO;GAAE;GAAa;GAAM;EAAY;EAEhE,UAAA,kBAAC,OAAD;GACO;GACL,cAAYC;GACZ,WAAWC,EAAGZ,EAAO,KAAKa,EAAgB;IAAE;IAAW;GAAU,CAAC,GAAGC,CAAS;GAC9E,oBAAkBC;GAClB,aAAWC;GACX,YAAY,MAAU;IAEpB,AADAC,IAAYC,CAAK,GACb,CAACA,EAAM,oBAAoBH,MAAgB,cAAYP,EAAY,UAAUU,CAAK;GACxF;GACA,GAAIX;GAEJ,UAAA,kBAAC,MAAD;IACE,WAAWP,EAAO;IAClB,MAAK;IACL,OAAOmB,MAAQ,KAAA,IAAY,KAAA,IAAa,EAAE,UAAUC,EAAMD,CAAG,EAAE;IAE9D;GACC,CAAA;EACD,CAAA;CACmB,CAAA;AAE9B,CAAC,GAkBKE,IAAef,EAAiD,SACpE,EACE,YAAS,IACT,aAAU,IACV,SACA,aACA,cACA,cACA,cACA,aACA,GAAGC,KAEL,GACA;CACA,IAAM,EAAE,gBAAa,YAASe,EAAWZ,CAAe,GAClDa,IAAQD,EAAWE,CAAoB,GACvCC,IAAKC,EAAM;CAEjB,QAAgC;EAC9B,IAAKH,GAEL,OADAA,EAAM,OAAOE,GAAIE,CAAM,SACV;GACX,EAAM,OAAOF,GAAI,EAAK;EACxB;CACF,GAAG;EAACF;EAAOE;EAAIE;CAAM,CAAC;CAEtB,IAAMC,IAAOC,IAAUC,EAAK,OAAO,KAC7BC,IAAO;EACX;EACA,WAAWnB,EAAGZ,EAAO,MAAMc,CAAS;EACpC,oBAAoBC;EACpB,aAAaC;EACb,eAAeW,KAAU,KAAA;EACzB,gBAAgBA,IAAU,SAAmB,KAAA;EAC7C,GAAGpB;CACL;CACA,OACE,kBAAC,MAAD;EAAI,WAAWK,EAAGZ,EAAO,MAAMa,EAAgB;GAAE;GAAW;EAAU,CAAC,CAAC;EACrE,UAAAmB,MAAS,KAAA,KAAaC,MAAa,KAAA,IAClC,kBAACL,GAAD;GAAM,GAAIG;GAAO;EAAe,CAAA,IAGhC,kBAACH,GAAD;GAAM,GAAIG;GAAV,UAAA;IACGC,MAAS,KAAA,IAIN,OAHF,kBAAC,QAAD;KAAM,WAAWhC,EAAO;KAAM,eAAY;KACvC,UAAAgC;IACG,CAAA;IAEPH,IAAU,kBAACC,EAAK,WAAN,EAAiB,YAAyB,CAAA,IAAII;IACxDD,MAAa,KAAA,IAAkE,OAAtD,kBAAC,QAAD;KAAM,WAAWjC,EAAO;KAAW,UAAAiC;IAAe,CAAA;GACxE;;CAEN,CAAA;AAER,CAAC,GAwCYE,IAAW,OAAO,OAAO9B,GAAc,EAAE,MAAMgB,EAAa,CAAC"}
@@ -1,11 +1,21 @@
1
1
  //#region src/components/navigation/NavLinks/NavLinks.module.css
2
- var e = "kiln-NavLinks__nav", t = "kiln-NavLinks__list", n = "kiln-NavLinks__item", r = "kiln-NavLinks__link", i = {
2
+ var e = "kiln-NavLinks__nav", t = "kiln-NavLinks__list", n = "kiln-NavLinks__item", r = "kiln-NavLinks__link", i = "kiln-NavLinks__icon", a = "kiln-NavLinks__label", o = "kiln-NavLinks__trailing", s = "kiln-NavLinks__group", c = "kiln-NavLinks__groupButton", l = "kiln-NavLinks__groupRow", u = "kiln-NavLinks__toggle", d = "kiln-NavLinks__chevron", f = "kiln-NavLinks__sublist", p = "kiln-NavLinks__flyout", m = {
3
3
  nav: e,
4
4
  list: t,
5
5
  item: n,
6
- link: r
6
+ link: r,
7
+ icon: i,
8
+ label: a,
9
+ trailing: o,
10
+ group: s,
11
+ groupButton: c,
12
+ groupRow: l,
13
+ toggle: u,
14
+ chevron: d,
15
+ sublist: f,
16
+ flyout: p
7
17
  };
8
18
  //#endregion
9
- export { i as default, n as item, r as link, t as list, e as nav };
19
+ export { d as chevron, m as default, p as flyout, s as group, c as groupButton, l as groupRow, i as icon, n as item, a as label, r as link, t as list, e as nav, f as sublist, u as toggle, o as trailing };
10
20
 
11
21
  //# sourceMappingURL=NavLinks.module.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"NavLinks.module.js","names":["nav","list","item","link","NavLinks_module_default"],"sources":["../../../../src/components/navigation/NavLinks/NavLinks.module.css"],"sourcesContent":["/*\n * Component tokens:\n * --navlinks-marker current-page marker colour, default var(--color-accent)\n * --navlinks-gap space between links, default var(--space-5) horizontal / 0 vertical\n * (the `gap` prop wins over it)\n */\n.nav {\n min-inline-size: 0;\n}\n\n.list {\n /* Defined here so an ancestor's --_gap can never leak in; the `gap` prop overrides inline. */\n --_gap: var(--navlinks-gap, var(--space-5));\n\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n column-gap: var(--_gap);\n row-gap: var(--space-1);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.nav[data-orientation='vertical'] .list {\n --_gap: var(--navlinks-gap, 0px);\n flex-direction: column;\n flex-wrap: nowrap;\n align-items: stretch;\n gap: var(--_gap);\n}\n\n.item {\n display: flex;\n margin: 0;\n padding: 0;\n}\n\n.link {\n --_marker: var(--navlinks-marker, var(--color-accent));\n\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: 0.5em;\n min-block-size: var(--control-md);\n padding: 0;\n color: var(--color-ink-muted);\n font-family: var(--font-text);\n /* Body size: people read and scan nav labels, so they never drop to the caption steps. */\n font-size: var(--text-md);\n font-weight: var(--label-weight);\n letter-spacing: var(--label-tracking);\n text-transform: var(--label-transform);\n line-height: 1.2;\n text-decoration: none;\n white-space: nowrap;\n -webkit-tap-highlight-color: transparent;\n transition: color var(--dur-1) var(--ease-out);\n}\n\n@media (hover: hover) {\n .link:hover {\n color: var(--color-ink);\n }\n}\n\n.link[data-active] {\n color: var(--color-ink);\n}\n\n.link[data-size='sm'] {\n min-block-size: var(--control-sm);\n font-size: var(--text-sm);\n}\n\n.link:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: 2px;\n border-radius: 2px;\n}\n\n/* Horizontal: a bar under the label, the width of the word, sitting a little below it. */\n.link[data-orientation='horizontal']::after {\n content: '';\n position: absolute;\n inset-inline: 0;\n inset-block-end: calc(50% - 0.6lh - var(--space-2));\n block-size: max(2px, var(--border-width-strong));\n border-radius: 1px;\n background: var(--_marker);\n scale: 0 1;\n transform-origin: left;\n transition: scale var(--dur-2) var(--ease-spring);\n}\n.link[data-orientation='horizontal'][data-active]::after {\n scale: 1 1;\n}\n\n/* Vertical: a dot in a reserved gutter, so labels never shift. */\n.link[data-orientation='vertical'] {\n inline-size: 100%;\n padding-inline-start: var(--space-4);\n}\n.link[data-orientation='vertical'][data-size='sm'] {\n padding-inline-start: var(--space-3);\n}\n.link[data-orientation='vertical']::before {\n content: '';\n position: absolute;\n inset-inline-start: 0;\n inset-block-start: 50%;\n inline-size: 0.375rem;\n block-size: 0.375rem;\n margin-block-start: -0.1875rem;\n border-radius: 50%;\n background: var(--_marker);\n scale: 0;\n transition: scale var(--dur-2) var(--ease-spring);\n}\n.link[data-orientation='vertical'][data-active]::before {\n scale: 1;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .link::before,\n .link::after {\n transition: none;\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"NavLinks.module.js","names":["nav","list","item","link","icon","label","trailing","group","groupButton","groupRow","toggle","chevron","sublist","flyout","NavLinks_module_default"],"sources":["../../../../src/components/navigation/NavLinks/NavLinks.module.css"],"sourcesContent":["/*\n * Component tokens:\n * --navlinks-marker current-page marker colour, default var(--color-accent)\n * --navlinks-gap space between links, default var(--space-5) horizontal / 0 vertical\n * (the `gap` prop wins over it)\n * --navlinks-indent how far each level of a group is indented, default var(--space-3)\n * --navlinks-guide the guide rule down a group's links, default var(--color-line)\n */\n.nav {\n min-inline-size: 0;\n}\n\n.list {\n /* Defined here so an ancestor's --_gap can never leak in; the `gap` prop overrides inline. */\n --_gap: var(--navlinks-gap, var(--space-5));\n\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n column-gap: var(--_gap);\n row-gap: var(--space-1);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.nav[data-orientation='vertical'] .list {\n --_gap: var(--navlinks-gap, 0px);\n flex-direction: column;\n flex-wrap: nowrap;\n align-items: stretch;\n gap: var(--_gap);\n}\n\n.item {\n display: flex;\n margin: 0;\n padding: 0;\n}\n\n.link {\n --_marker: var(--navlinks-marker, var(--color-accent));\n\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: 0.5em;\n min-block-size: var(--control-md);\n padding: 0;\n color: var(--color-ink-muted);\n font-family: var(--font-text);\n /* Body size: people read and scan nav labels, so they never drop to the caption steps. */\n font-size: var(--text-md);\n font-weight: var(--label-weight);\n letter-spacing: var(--label-tracking);\n text-transform: var(--label-transform);\n line-height: 1.2;\n text-decoration: none;\n white-space: nowrap;\n -webkit-tap-highlight-color: transparent;\n transition: color var(--dur-1) var(--ease-out);\n}\n\n@media (hover: hover) {\n .link:hover {\n color: var(--color-ink);\n }\n}\n\n.link[data-active] {\n color: var(--color-ink);\n}\n\n.link[data-size='sm'] {\n min-block-size: var(--control-sm);\n font-size: var(--text-sm);\n}\n\n.link:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: 2px;\n border-radius: 2px;\n}\n\n/* Horizontal: a bar under the label, the width of the word, sitting a little below it. */\n.link[data-orientation='horizontal']::after {\n content: '';\n position: absolute;\n inset-inline: 0;\n inset-block-end: calc(50% - 0.6lh - var(--space-2));\n block-size: max(2px, var(--border-width-strong));\n border-radius: 1px;\n background: var(--_marker);\n scale: 0 1;\n transform-origin: left;\n transition: scale var(--dur-2) var(--ease-spring);\n}\n.link[data-orientation='horizontal'][data-active]::after {\n scale: 1 1;\n}\n\n/* Vertical: a dot in a reserved gutter, so labels never shift. */\n.link[data-orientation='vertical'] {\n inline-size: 100%;\n padding-inline-start: var(--space-4);\n}\n.link[data-orientation='vertical'][data-size='sm'] {\n padding-inline-start: var(--space-3);\n}\n.link[data-orientation='vertical']::before {\n content: '';\n position: absolute;\n inset-inline-start: 0;\n inset-block-start: 50%;\n inline-size: 0.375rem;\n block-size: 0.375rem;\n margin-block-start: -0.1875rem;\n border-radius: 50%;\n background: var(--_marker);\n scale: 0;\n transition: scale var(--dur-2) var(--ease-spring);\n}\n.link[data-orientation='vertical'][data-active]::before {\n scale: 1;\n}\n\n/* ── Icons and trailing counts on items and groups ───────────────────────── */\n.icon {\n display: inline-flex;\n flex: none;\n color: var(--color-ink-subtle);\n font-size: 1.25em;\n}\n.link[data-active] .icon,\n.link[data-contains-active] .icon {\n color: inherit;\n}\n.label {\n min-inline-size: 0;\n}\n.trailing {\n display: inline-flex;\n flex: none;\n align-items: center;\n gap: var(--space-2);\n font-variant-numeric: tabular-nums lining-nums;\n}\n.link[data-orientation='vertical'] .trailing {\n margin-inline-start: auto;\n}\n\n/* ── Groups ────────────────────────────────────────────────────────────── */\n.group {\n flex-direction: column;\n}\n.nav[data-orientation='horizontal'] .group {\n flex-direction: row;\n}\n\n/* The group's button is a .link with the button chrome taken off. */\n.groupButton {\n border: 0;\n background: transparent;\n text-align: start;\n cursor: pointer;\n}\n.link[data-contains-active] {\n color: var(--color-ink);\n}\n\n/* A linked group: the label is a link, the chevron its own button. */\n.groupRow {\n display: flex;\n align-items: center;\n min-inline-size: 0;\n}\n.groupRow > .link {\n flex: 1 1 auto;\n min-inline-size: 0;\n}\n.toggle {\n display: inline-grid;\n flex: none;\n place-items: center;\n inline-size: var(--control-sm);\n block-size: var(--control-sm);\n padding: 0;\n border: 0;\n border-radius: var(--radius-action);\n background: transparent;\n color: var(--color-ink-muted);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n}\n@media (hover: hover) {\n .toggle:hover {\n color: var(--color-ink);\n }\n}\n.toggle:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: 2px;\n}\n\n.chevron {\n flex: none;\n font-size: 1rem;\n color: var(--color-ink-subtle);\n transition: rotate var(--dur-2) var(--ease-spring);\n}\n.groupButton .chevron {\n margin-inline-start: auto;\n}\n/* With a trailing count, the count takes the free space and the chevron follows it. */\n.groupButton .trailing ~ .chevron {\n margin-inline-start: 0;\n}\n.nav[data-orientation='horizontal'] .groupButton .chevron {\n margin-inline-start: 0;\n}\n[data-state='open'] > .chevron {\n rotate: 180deg;\n}\n\n/* Closed and holding the current page: a ring where the dot would be. */\n.link[data-orientation='vertical'][data-contains-active][data-state='closed']::before {\n background: transparent;\n box-shadow: inset 0 0 0 1.5px var(--_marker);\n scale: 1;\n}\n/* Horizontal: the bar under a group that holds the current page. */\n.link[data-orientation='horizontal'][data-contains-active]::after {\n scale: 1 1;\n}\n\n/* Nested links: indented, on a guide rule, labels at the same size as the level above. */\n.sublist {\n display: flex;\n flex-direction: column;\n margin: 0;\n margin-inline-start: calc(var(--space-4) - var(--border-width));\n padding: 0;\n padding-inline-start: var(--navlinks-indent, var(--space-3));\n border-inline-start: var(--border-width) solid var(--navlinks-guide, var(--color-line));\n list-style: none;\n}\n.nav[data-size='sm'] .sublist {\n margin-inline-start: calc(var(--space-3) - var(--border-width));\n}\n\n/* Horizontal groups open in a panel: the links listed down it, with no guide rule. */\n.flyout {\n min-inline-size: 12rem;\n padding-block: var(--space-2);\n}\n.sublist[data-flyout] {\n margin-inline-start: 0;\n padding-inline: var(--space-3);\n border-inline-start: 0;\n}\n.sublist[data-flyout] .sublist {\n margin-inline-start: calc(var(--space-4) - var(--border-width));\n padding-inline: var(--navlinks-indent, var(--space-3)) 0;\n border-inline-start: var(--border-width) solid var(--navlinks-guide, var(--color-line));\n}\n\n@media (prefers-reduced-motion: reduce) {\n .link::before,\n .link::after,\n .chevron {\n transition: none;\n }\n}\n"],"mappings":""}
@@ -0,0 +1,49 @@
1
+ import { type HTMLAttributes, type ReactElement, type ReactNode } from 'react';
2
+ import { type VisibilityProps } from '../../../utils/visibility.js';
3
+ export interface NavLinksGroupProps extends Omit<HTMLAttributes<HTMLLIElement>, 'children'>, VisibilityProps {
4
+ /** The group's name: "Settings", "Reports". */
5
+ label: ReactNode;
6
+ /** The group's links, `NavLinks.Item`s, and further groups. */
7
+ children: ReactNode;
8
+ /** A glyph before the label. Decorative: the label names the group. */
9
+ icon?: ReactNode;
10
+ /** A count or status after the label, such as a `Badge` or a `StatusDot`. */
11
+ trailing?: ReactNode;
12
+ /** Controlled open state. */
13
+ open?: boolean;
14
+ /**
15
+ * Uncontrolled initial state. Default: open when the group holds the current page. An
16
+ * uncontrolled group also opens when the current page moves into it.
17
+ */
18
+ defaultOpen?: boolean;
19
+ /** Runs when the group's toggle asks to open or close. */
20
+ onOpenChange?: (open: boolean) => void;
21
+ /**
22
+ * Makes the label a link to the group's own page (a section's landing page), with a separate
23
+ * button beside it to open and close the group.
24
+ */
25
+ href?: string;
26
+ /**
27
+ * Like `href`, for a router: the link element to render the label as, such as
28
+ * `<Link to="/settings" />`. The label becomes its content.
29
+ */
30
+ link?: ReactElement;
31
+ /** The group's own page (`href` or `link`) is the current page. */
32
+ active?: boolean;
33
+ /**
34
+ * Accessible name of the open-and-close button beside a linked label. Default: the label's own
35
+ * text.
36
+ */
37
+ toggleLabel?: string;
38
+ }
39
+ /**
40
+ * A group of links in a `NavLinks`: a section with child pages, and further groups.
41
+ *
42
+ * @privateRemarks
43
+ * Vertical: a disclosure button and a nested list in a Collapse (the disclosure-navigation
44
+ * pattern, not a tree role, so links stay links). Horizontal: the same button opens a Popover with
45
+ * the links listed vertically inside. While it's closed, the links stay mounted in a `hidden` copy so
46
+ * items wrapped in your own components still report in and the group can show it holds the current
47
+ * page. Not DropdownMenu: ARIA menus are for commands and drop link semantics such as aria-current.
48
+ */
49
+ export declare const NavLinksGroup: import("react").ForwardRefExoticComponent<NavLinksGroupProps & import("react").RefAttributes<HTMLLIElement>>;
@@ -0,0 +1,185 @@
1
+ import { cx as e } from "../../../utils/cx.js";
2
+ import { ChevronDownIcon as t } from "../../../icons/icons.js";
3
+ import { visibilityClass as n } from "../../../utils/visibility.js";
4
+ import { useLatest as ee } from "../../inputs/internal/useLatest.js";
5
+ import { Collapse as r } from "../../layout/Collapse/Collapse.js";
6
+ import { Popover as i } from "../../overlays/Popover/Popover.js";
7
+ import { NavLinksContext as a, NavLinksGroupContext as o, holdsActive as te } from "./context.js";
8
+ import s from "./NavLinks.module.js";
9
+ import { onTreeKeyDown as ne } from "./treeKeys.js";
10
+ import { cloneElement as c, forwardRef as l, useCallback as u, useContext as d, useEffect as f, useId as p, useLayoutEffect as m, useMemo as re, useState as h } from "react";
11
+ import { Fragment as ie, jsx as g, jsxs as _ } from "react/jsx-runtime";
12
+ import { Slot as v } from "radix-ui";
13
+ //#region src/components/navigation/NavLinks/NavLinksGroup.tsx
14
+ var y = typeof document > "u" ? f : m;
15
+ function b() {
16
+ return /* @__PURE__ */ g(t, {
17
+ className: s.chevron,
18
+ "aria-hidden": !0
19
+ });
20
+ }
21
+ var x = l(function({ label: t, children: l, icon: m, trailing: x, open: S, defaultOpen: ae, onOpenChange: oe, href: C, link: w, active: T = !1, toggleLabel: E, hideBelow: se, hideAbove: ce, className: D, ...O }, k) {
22
+ let { orientation: A, size: j, treeKeysRef: M } = d(a);
23
+ f(() => {
24
+ M && (M.current = ne);
25
+ }, [M]);
26
+ let N = d(o), P = p(), F = `${P}-label`, I = `${P}-panel`, [L, le] = h([]), R = u((e, t) => {
27
+ le((n) => t ? n.includes(e) ? n : [...n, e] : n.includes(e) ? n.filter((t) => t !== e) : n);
28
+ }, []), z = re(() => ({ report: R }), [R]), B = L.length > 0 || te(l);
29
+ y(() => {
30
+ if (N) return N.report(P, T || B), () => {
31
+ N.report(P, !1);
32
+ };
33
+ }, [
34
+ N,
35
+ P,
36
+ T,
37
+ B
38
+ ]);
39
+ let V = A === "vertical", [H, U] = h(ae ?? (V && B)), [ue, de] = h(B);
40
+ ue !== B && (de(B), B && V && S === void 0 && U(!0));
41
+ let W = S ?? H, G = ee(oe), K = u((e) => {
42
+ S === void 0 && U(e), G.current?.(e);
43
+ }, [S, G]), q = C !== void 0 || w !== void 0, J = {
44
+ "data-orientation": A,
45
+ "data-size": j
46
+ }, Y = /* @__PURE__ */ _(ie, { children: [
47
+ m ? /* @__PURE__ */ g("span", {
48
+ className: s.icon,
49
+ "aria-hidden": "true",
50
+ children: m
51
+ }) : null,
52
+ /* @__PURE__ */ g("span", {
53
+ className: s.label,
54
+ children: t
55
+ }),
56
+ x === void 0 ? null : /* @__PURE__ */ g("span", {
57
+ className: s.trailing,
58
+ children: x
59
+ })
60
+ ] }), X = q ? /* @__PURE__ */ g(v.Root, {
61
+ id: F,
62
+ className: s.link,
63
+ ...J,
64
+ "data-active": T || void 0,
65
+ "data-contains-active": B || void 0,
66
+ "data-state": W ? "open" : "closed",
67
+ "aria-current": T ? "page" : void 0,
68
+ children: w ? c(w, void 0, Y) : /* @__PURE__ */ g("a", {
69
+ href: C,
70
+ children: Y
71
+ })
72
+ }) : null, Z = {
73
+ "aria-expanded": W,
74
+ "data-state": W ? "open" : "closed",
75
+ "data-contains-active": B || void 0
76
+ }, Q = e(s.item, s.group, n({
77
+ hideBelow: se,
78
+ hideAbove: ce
79
+ }), D);
80
+ if (!V) {
81
+ let t = /* @__PURE__ */ g(a.Provider, {
82
+ value: {
83
+ orientation: "vertical",
84
+ size: j
85
+ },
86
+ children: /* @__PURE__ */ g(o.Provider, {
87
+ value: z,
88
+ children: /* @__PURE__ */ g("ul", {
89
+ className: s.sublist,
90
+ role: "list",
91
+ "data-flyout": "",
92
+ children: l
93
+ })
94
+ })
95
+ });
96
+ return /* @__PURE__ */ _("li", {
97
+ ref: k,
98
+ className: Q,
99
+ "data-navlinks-group": "",
100
+ ...O,
101
+ children: [/* @__PURE__ */ _(i, {
102
+ open: W,
103
+ onOpenChange: K,
104
+ children: [q ? /* @__PURE__ */ _("span", {
105
+ className: s.groupRow,
106
+ "data-navlinks-row": "",
107
+ children: [X, /* @__PURE__ */ g(i.Trigger, {
108
+ className: s.toggle,
109
+ ...J,
110
+ ...Z,
111
+ "aria-label": E,
112
+ "aria-labelledby": E ? void 0 : F,
113
+ children: /* @__PURE__ */ g(b, {})
114
+ })]
115
+ }) : /* @__PURE__ */ _(i.Trigger, {
116
+ id: F,
117
+ className: e(s.link, s.groupButton),
118
+ ...J,
119
+ ...Z,
120
+ children: [Y, /* @__PURE__ */ g(b, {})]
121
+ }), /* @__PURE__ */ g(i.Content, {
122
+ align: "start",
123
+ className: s.flyout,
124
+ "aria-labelledby": F,
125
+ onClick: (e) => {
126
+ e.target.closest("a") && K(!1);
127
+ },
128
+ children: t
129
+ })]
130
+ }), W ? null : /* @__PURE__ */ g("div", {
131
+ hidden: !0,
132
+ children: t
133
+ })]
134
+ });
135
+ }
136
+ let $ = () => {
137
+ K(!W);
138
+ };
139
+ return /* @__PURE__ */ _("li", {
140
+ ref: k,
141
+ className: Q,
142
+ "data-navlinks-group": "",
143
+ ...O,
144
+ children: [q ? /* @__PURE__ */ _("span", {
145
+ className: s.groupRow,
146
+ "data-navlinks-row": "",
147
+ children: [X, /* @__PURE__ */ g("button", {
148
+ type: "button",
149
+ className: s.toggle,
150
+ ...J,
151
+ ...Z,
152
+ "aria-controls": I,
153
+ "aria-label": E,
154
+ "aria-labelledby": E ? void 0 : F,
155
+ onClick: $,
156
+ children: /* @__PURE__ */ g(b, {})
157
+ })]
158
+ }) : /* @__PURE__ */ _("button", {
159
+ type: "button",
160
+ id: F,
161
+ className: e(s.link, s.groupButton),
162
+ ...J,
163
+ ...Z,
164
+ "aria-controls": I,
165
+ onClick: $,
166
+ children: [Y, /* @__PURE__ */ g(b, {})]
167
+ }), /* @__PURE__ */ g(r, {
168
+ open: W,
169
+ id: I,
170
+ "data-navlinks-panel": "",
171
+ children: /* @__PURE__ */ g(o.Provider, {
172
+ value: z,
173
+ children: /* @__PURE__ */ g("ul", {
174
+ className: s.sublist,
175
+ role: "list",
176
+ children: l
177
+ })
178
+ })
179
+ })]
180
+ });
181
+ });
182
+ //#endregion
183
+ export { x as NavLinksGroup };
184
+
185
+ //# sourceMappingURL=NavLinksGroup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavLinksGroup.js","names":["styles","_Fragment","Popover","Popover$1","NavLinks_module_default","Fragment$1","useIsomorphicLayoutEffect","useEffect","useLayoutEffect","Chevron","ChevronDownIcon","NavLinksGroup","forwardRef","openProp","rest","useContext","NavLinksContext","treeKeysRef","onTreeKeyDown","parent","NavLinksGroupContext","id","useId","labelId","panelId","reported","setReported","useState","report","useCallback","isActive","prev","child","entry","groupContext","useMemo","containsActive","holdsActive","children","active","vertical","orientation","uncontrolled","setUncontrolled","defaultOpen","held","setHeld","open","change","useLatest","onOpenChange","setOpen","next","linked","href","link","shared","size","labelContent","icon","label","trailing","linkElement","Slot","cloneElement","toggleState","item","cx","visibilityClass","className","flyout","toggleLabel","event","toggle","Collapse"],"sources":["../../../../src/components/navigation/NavLinks/NavLinksGroup.tsx"],"sourcesContent":["import {\n cloneElement,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useState,\n type HTMLAttributes,\n type ReactElement,\n type ReactNode,\n} from 'react'\nimport { Slot } from 'radix-ui'\nimport { cx } from '#utils/cx'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\nimport { ChevronDownIcon } from '#icons'\nimport { Collapse } from '#components/layout/Collapse'\nimport { Popover } from '#components/overlays/Popover'\nimport { useLatest } from '#components/inputs/internal/useLatest'\nimport {\n NavLinksContext,\n NavLinksGroupContext,\n holdsActive,\n type NavLinksGroupContextValue,\n} from './context'\nimport { onTreeKeyDown } from './treeKeys'\nimport styles from './NavLinks.module.css'\n\nconst useIsomorphicLayoutEffect = typeof document === 'undefined' ? useEffect : useLayoutEffect\n\nexport interface NavLinksGroupProps\n extends Omit<HTMLAttributes<HTMLLIElement>, 'children'>, VisibilityProps {\n /** The group's name: \"Settings\", \"Reports\". */\n label: ReactNode\n /** The group's links, `NavLinks.Item`s, and further groups. */\n children: ReactNode\n /** A glyph before the label. Decorative: the label names the group. */\n icon?: ReactNode\n /** A count or status after the label, such as a `Badge` or a `StatusDot`. */\n trailing?: ReactNode\n /** Controlled open state. */\n open?: boolean\n /**\n * Uncontrolled initial state. Default: open when the group holds the current page. An\n * uncontrolled group also opens when the current page moves into it.\n */\n defaultOpen?: boolean\n /** Runs when the group's toggle asks to open or close. */\n onOpenChange?: (open: boolean) => void\n /**\n * Makes the label a link to the group's own page (a section's landing page), with a separate\n * button beside it to open and close the group.\n */\n href?: string\n /**\n * Like `href`, for a router: the link element to render the label as, such as\n * `<Link to=\"/settings\" />`. The label becomes its content.\n */\n link?: ReactElement\n /** The group's own page (`href` or `link`) is the current page. */\n active?: boolean\n /**\n * Accessible name of the open-and-close button beside a linked label. Default: the label's own\n * text.\n */\n toggleLabel?: string\n}\n\nfunction Chevron() {\n return <ChevronDownIcon className={styles.chevron} aria-hidden />\n}\n\n/**\n * A group of links in a `NavLinks`: a section with child pages, and further groups.\n *\n * @privateRemarks\n * Vertical: a disclosure button and a nested list in a Collapse (the disclosure-navigation\n * pattern, not a tree role, so links stay links). Horizontal: the same button opens a Popover with\n * the links listed vertically inside. While it's closed, the links stay mounted in a `hidden` copy so\n * items wrapped in your own components still report in and the group can show it holds the current\n * page. Not DropdownMenu: ARIA menus are for commands and drop link semantics such as aria-current.\n */\nexport const NavLinksGroup = forwardRef<HTMLLIElement, NavLinksGroupProps>(function NavLinksGroup(\n {\n label,\n children,\n icon,\n trailing,\n open: openProp,\n defaultOpen,\n onOpenChange,\n href,\n link,\n active = false,\n toggleLabel,\n hideBelow,\n hideAbove,\n className,\n ...rest\n },\n ref,\n) {\n const { orientation, size, treeKeysRef } = useContext(NavLinksContext)\n // A nav with a group gets the tree-style arrow keys. Every group sets the same handler, and it\n // does nothing once the nav has no groups, so none of them clears it.\n useEffect(() => {\n if (treeKeysRef) treeKeysRef.current = onTreeKeyDown\n }, [treeKeysRef])\n const parent = useContext(NavLinksGroupContext)\n const id = useId()\n const labelId = `${id}-label`\n const panelId = `${id}-panel`\n\n // Which descendants have said they're the current page (see NavLinks.Item).\n const [reported, setReported] = useState<string[]>([])\n const report = useCallback((child: string, isActive: boolean) => {\n setReported((prev) =>\n isActive\n ? prev.includes(child)\n ? prev\n : [...prev, child]\n : prev.includes(child)\n ? prev.filter((entry) => entry !== child)\n : prev,\n )\n }, [])\n const groupContext = useMemo<NavLinksGroupContextValue>(() => ({ report }), [report])\n const containsActive = reported.length > 0 || holdsActive(children)\n\n // Tell the groups around this one, so a closed parent can show it holds the current page.\n useIsomorphicLayoutEffect(() => {\n if (!parent) return\n parent.report(id, active || containsActive)\n return () => {\n parent.report(id, false)\n }\n }, [parent, id, active, containsActive])\n\n const vertical = orientation === 'vertical'\n const [uncontrolled, setUncontrolled] = useState(defaultOpen ?? (vertical && containsActive))\n // An uncontrolled vertical group opens when the current page moves into it.\n const [held, setHeld] = useState(containsActive)\n if (held !== containsActive) {\n setHeld(containsActive)\n if (containsActive && vertical && openProp === undefined) setUncontrolled(true)\n }\n const open = openProp ?? uncontrolled\n const change = useLatest(onOpenChange)\n const setOpen = useCallback(\n (next: boolean) => {\n if (openProp === undefined) setUncontrolled(next)\n change.current?.(next)\n },\n [openProp, change],\n )\n\n const linked = href !== undefined || link !== undefined\n const shared = {\n 'data-orientation': orientation,\n 'data-size': size,\n }\n const labelContent = (\n <>\n {icon ? (\n <span className={styles.icon} aria-hidden=\"true\">\n {icon}\n </span>\n ) : null}\n <span className={styles.label}>{label}</span>\n {trailing !== undefined ? <span className={styles.trailing}>{trailing}</span> : null}\n </>\n )\n const linkElement = linked ? (\n <Slot.Root\n id={labelId}\n className={styles.link}\n {...shared}\n data-active={active || undefined}\n data-contains-active={containsActive || undefined}\n data-state={open ? 'open' : 'closed'}\n aria-current={active ? 'page' : undefined}\n >\n {link ? cloneElement(link, undefined, labelContent) : <a href={href}>{labelContent}</a>}\n </Slot.Root>\n ) : null\n const toggleState = {\n 'aria-expanded': open,\n 'data-state': open ? 'open' : 'closed',\n 'data-contains-active': containsActive || undefined,\n }\n\n const item = cx(styles.item, styles.group, visibilityClass({ hideBelow, hideAbove }), className)\n\n if (!vertical) {\n const flyout = (\n <NavLinksContext.Provider value={{ orientation: 'vertical', size }}>\n <NavLinksGroupContext.Provider value={groupContext}>\n <ul className={styles.sublist} role=\"list\" data-flyout=\"\">\n {children}\n </ul>\n </NavLinksGroupContext.Provider>\n </NavLinksContext.Provider>\n )\n return (\n <li ref={ref} className={item} data-navlinks-group=\"\" {...rest}>\n <Popover open={open} onOpenChange={setOpen}>\n {linked ? (\n <span className={styles.groupRow} data-navlinks-row=\"\">\n {linkElement}\n <Popover.Trigger\n className={styles.toggle}\n {...shared}\n {...toggleState}\n aria-label={toggleLabel}\n aria-labelledby={toggleLabel ? undefined : labelId}\n >\n <Chevron />\n </Popover.Trigger>\n </span>\n ) : (\n <Popover.Trigger\n id={labelId}\n className={cx(styles.link, styles.groupButton)}\n {...shared}\n {...toggleState}\n >\n {labelContent}\n <Chevron />\n </Popover.Trigger>\n )}\n <Popover.Content\n align=\"start\"\n className={styles.flyout}\n aria-labelledby={labelId}\n onClick={(event) => {\n // Following a link closes the flyout, even when the router keeps the page.\n if ((event.target as Element).closest('a')) setOpen(false)\n }}\n >\n {flyout}\n </Popover.Content>\n </Popover>\n {open ? null : <div hidden>{flyout}</div>}\n </li>\n )\n }\n\n const toggle = () => {\n setOpen(!open)\n }\n\n return (\n <li ref={ref} className={item} data-navlinks-group=\"\" {...rest}>\n {linked ? (\n <span className={styles.groupRow} data-navlinks-row=\"\">\n {linkElement}\n <button\n type=\"button\"\n className={styles.toggle}\n {...shared}\n {...toggleState}\n aria-controls={panelId}\n aria-label={toggleLabel}\n aria-labelledby={toggleLabel ? undefined : labelId}\n onClick={toggle}\n >\n <Chevron />\n </button>\n </span>\n ) : (\n <button\n type=\"button\"\n id={labelId}\n className={cx(styles.link, styles.groupButton)}\n {...shared}\n {...toggleState}\n aria-controls={panelId}\n onClick={toggle}\n >\n {labelContent}\n <Chevron />\n </button>\n )}\n <Collapse open={open} id={panelId} data-navlinks-panel=\"\">\n <NavLinksGroupContext.Provider value={groupContext}>\n <ul className={styles.sublist} role=\"list\">\n {children}\n </ul>\n </NavLinksGroupContext.Provider>\n </Collapse>\n </li>\n )\n})\n"],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { type KeyboardEvent, type ReactNode } from 'react';
2
+ export type NavLinksOrientation = 'horizontal' | 'vertical';
3
+ export type NavLinksSize = 'sm' | 'md';
4
+ /** The tree-style arrow keys of a vertical nav with groups. */
5
+ export type TreeKeyDown = (event: KeyboardEvent<HTMLElement>) => void;
6
+ export interface NavLinksContextValue {
7
+ orientation: NavLinksOrientation;
8
+ size: NavLinksSize;
9
+ /**
10
+ * Where a group puts the arrow-key handler for the nav it's in. Groups bring it, so a nav
11
+ * without them doesn't carry it.
12
+ */
13
+ treeKeysRef?: {
14
+ current: TreeKeyDown | null;
15
+ };
16
+ }
17
+ export declare const NavLinksContext: import("react").Context<NavLinksContextValue>;
18
+ /** How an item or a nested group tells the groups around it that it's the current page. */
19
+ export interface NavLinksGroupContextValue {
20
+ report: (id: string, active: boolean) => void;
21
+ }
22
+ export declare const NavLinksGroupContext: import("react").Context<NavLinksGroupContextValue | null>;
23
+ /**
24
+ * Whether any element under `children` is marked `active`, read from the elements themselves, so
25
+ * the server render already knows which groups hold the current page. Items wrapped in your own
26
+ * components are caught on the client, when they report in.
27
+ */
28
+ export declare function holdsActive(children: ReactNode): boolean;