@evinvest/uikit 0.30.0 → 0.31.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 (204) hide show
  1. package/README.md +266 -72
  2. package/dist/{chunk-3IPB4QQJ.js → chunk-35SPIU4U.js} +10 -6
  3. package/dist/chunk-35SPIU4U.js.map +1 -0
  4. package/dist/components/account-list.js +128 -0
  5. package/dist/components/account-list.js.map +1 -0
  6. package/dist/components/account-menu-profile.js +114 -0
  7. package/dist/components/account-menu-profile.js.map +1 -0
  8. package/dist/components/account-menu.js +50 -23
  9. package/dist/components/account-menu.js.map +1 -1
  10. package/dist/components/account-settings-dialog.js +84 -0
  11. package/dist/components/account-settings-dialog.js.map +1 -0
  12. package/dist/components/alert-dialog.js +3 -2
  13. package/dist/components/alert-dialog.js.map +1 -1
  14. package/dist/components/animated-number-columns.js +105 -0
  15. package/dist/components/animated-number-columns.js.map +1 -0
  16. package/dist/components/animated-number.js +157 -0
  17. package/dist/components/animated-number.js.map +1 -0
  18. package/dist/components/app-shell.js +9 -0
  19. package/dist/components/app-shell.js.map +1 -1
  20. package/dist/components/aside.js +140 -0
  21. package/dist/components/aside.js.map +1 -0
  22. package/dist/components/badge.js +1 -1
  23. package/dist/components/badge.js.map +1 -1
  24. package/dist/components/button.js +3 -11
  25. package/dist/components/button.js.map +1 -1
  26. package/dist/components/chat-composer.js +141 -0
  27. package/dist/components/chat-composer.js.map +1 -0
  28. package/dist/components/chat-message.js +195 -0
  29. package/dist/components/chat-message.js.map +1 -0
  30. package/dist/components/chat-panel.js +137 -0
  31. package/dist/components/chat-panel.js.map +1 -0
  32. package/dist/components/chat-stream-text.js +69 -0
  33. package/dist/components/chat-stream-text.js.map +1 -0
  34. package/dist/components/chat-typing.js +26 -0
  35. package/dist/components/chat-typing.js.map +1 -0
  36. package/dist/components/command-palette.js +119 -0
  37. package/dist/components/command-palette.js.map +1 -0
  38. package/dist/components/command.js +3 -1
  39. package/dist/components/command.js.map +1 -1
  40. package/dist/components/confirm-dialog.js +123 -0
  41. package/dist/components/confirm-dialog.js.map +1 -0
  42. package/dist/components/context-menu.js +53 -5
  43. package/dist/components/context-menu.js.map +1 -1
  44. package/dist/components/dialog.js +3 -2
  45. package/dist/components/dialog.js.map +1 -1
  46. package/dist/components/drawer.js +3 -1
  47. package/dist/components/drawer.js.map +1 -1
  48. package/dist/components/dropdown-menu.js +64 -5
  49. package/dist/components/dropdown-menu.js.map +1 -1
  50. package/dist/components/form-row.js +51 -0
  51. package/dist/components/form-row.js.map +1 -0
  52. package/dist/components/header-controls.js +149 -0
  53. package/dist/components/header-controls.js.map +1 -0
  54. package/dist/components/icon-button.js +35 -0
  55. package/dist/components/icon-button.js.map +1 -0
  56. package/dist/components/key-value.js +68 -0
  57. package/dist/components/key-value.js.map +1 -0
  58. package/dist/components/menubar.js +39 -5
  59. package/dist/components/menubar.js.map +1 -1
  60. package/dist/components/native-select.js +3 -4
  61. package/dist/components/native-select.js.map +1 -1
  62. package/dist/components/nav-collapse.js +142 -0
  63. package/dist/components/nav-collapse.js.map +1 -0
  64. package/dist/components/nav-model.js.map +1 -1
  65. package/dist/components/navigation-menu.js +4 -5
  66. package/dist/components/navigation-menu.js.map +1 -1
  67. package/dist/components/page-frame.js +2 -5
  68. package/dist/components/page-frame.js.map +1 -1
  69. package/dist/components/popover-form.js +60 -0
  70. package/dist/components/popover-form.js.map +1 -0
  71. package/dist/components/resource-error.js +1 -1
  72. package/dist/components/resource-error.js.map +1 -1
  73. package/dist/components/section-nav.js +73 -20
  74. package/dist/components/section-nav.js.map +1 -1
  75. package/dist/components/select.js +16 -6
  76. package/dist/components/select.js.map +1 -1
  77. package/dist/components/settings.js +94 -0
  78. package/dist/components/settings.js.map +1 -0
  79. package/dist/components/sheet.js +3 -2
  80. package/dist/components/sheet.js.map +1 -1
  81. package/dist/components/shell-nav.js +80 -38
  82. package/dist/components/shell-nav.js.map +1 -1
  83. package/dist/components/smart-header.js +53 -0
  84. package/dist/components/smart-header.js.map +1 -0
  85. package/dist/components/status-item.js +42 -0
  86. package/dist/components/status-item.js.map +1 -0
  87. package/dist/components/system-banner.js +22 -12
  88. package/dist/components/system-banner.js.map +1 -1
  89. package/dist/components/top-bar.js +4 -3
  90. package/dist/components/top-bar.js.map +1 -1
  91. package/dist/components/ui-icons.js +77 -0
  92. package/dist/components/ui-icons.js.map +1 -0
  93. package/dist/components/use-count-tween.js +42 -0
  94. package/dist/components/use-count-tween.js.map +1 -0
  95. package/dist/components/workspace-tab.js +140 -0
  96. package/dist/components/workspace-tab.js.map +1 -0
  97. package/dist/generated/accent.js +2 -2
  98. package/dist/generated/accent.js.map +1 -1
  99. package/dist/generated/account-list.js +35 -0
  100. package/dist/generated/account-list.js.map +1 -0
  101. package/dist/generated/account-settings-dialog.js +27 -0
  102. package/dist/generated/account-settings-dialog.js.map +1 -0
  103. package/dist/generated/alert-dialog.js +1 -1
  104. package/dist/generated/alert-dialog.js.map +1 -1
  105. package/dist/generated/alert.js +2 -2
  106. package/dist/generated/alert.js.map +1 -1
  107. package/dist/generated/aside.js +36 -0
  108. package/dist/generated/aside.js.map +1 -0
  109. package/dist/generated/badge.js +7 -6
  110. package/dist/generated/badge.js.map +1 -1
  111. package/dist/generated/band.js +1 -1
  112. package/dist/generated/band.js.map +1 -1
  113. package/dist/generated/breadcrumb.js +2 -2
  114. package/dist/generated/breadcrumb.js.map +1 -1
  115. package/dist/generated/button-group.js +2 -2
  116. package/dist/generated/button-group.js.map +1 -1
  117. package/dist/generated/button.js +4 -4
  118. package/dist/generated/button.js.map +1 -1
  119. package/dist/generated/card.js +6 -6
  120. package/dist/generated/card.js.map +1 -1
  121. package/dist/generated/chat-composer.js +21 -0
  122. package/dist/generated/chat-composer.js.map +1 -0
  123. package/dist/generated/chat-message.js +86 -0
  124. package/dist/generated/chat-message.js.map +1 -0
  125. package/dist/generated/chat-typing.js +9 -0
  126. package/dist/generated/chat-typing.js.map +1 -0
  127. package/dist/generated/command-palette.js +17 -0
  128. package/dist/generated/command-palette.js.map +1 -0
  129. package/dist/generated/command.js +2 -2
  130. package/dist/generated/command.js.map +1 -1
  131. package/dist/generated/confirm-dialog.js +28 -0
  132. package/dist/generated/confirm-dialog.js.map +1 -0
  133. package/dist/generated/context-menu.js +1 -1
  134. package/dist/generated/context-menu.js.map +1 -1
  135. package/dist/generated/date-time-picker.js +1 -1
  136. package/dist/generated/date-time-picker.js.map +1 -1
  137. package/dist/generated/dialog.js +1 -1
  138. package/dist/generated/dialog.js.map +1 -1
  139. package/dist/generated/drawer.js +1 -1
  140. package/dist/generated/drawer.js.map +1 -1
  141. package/dist/generated/dropdown-menu.js +1 -1
  142. package/dist/generated/dropdown-menu.js.map +1 -1
  143. package/dist/generated/form-row.js +17 -0
  144. package/dist/generated/form-row.js.map +1 -0
  145. package/dist/generated/header-controls.js +56 -0
  146. package/dist/generated/header-controls.js.map +1 -0
  147. package/dist/generated/icon-button.js +13 -0
  148. package/dist/generated/icon-button.js.map +1 -0
  149. package/dist/generated/key-value.js +15 -0
  150. package/dist/generated/key-value.js.map +1 -0
  151. package/dist/generated/menubar.js +1 -1
  152. package/dist/generated/menubar.js.map +1 -1
  153. package/dist/generated/navigation-menu.js +1 -1
  154. package/dist/generated/navigation-menu.js.map +1 -1
  155. package/dist/generated/popover-form.js +13 -0
  156. package/dist/generated/popover-form.js.map +1 -0
  157. package/dist/generated/section-label.js +10 -0
  158. package/dist/generated/section-label.js.map +1 -0
  159. package/dist/generated/select.js +15 -3
  160. package/dist/generated/select.js.map +1 -1
  161. package/dist/generated/settings.js +36 -0
  162. package/dist/generated/settings.js.map +1 -0
  163. package/dist/generated/sheet.js +1 -1
  164. package/dist/generated/sheet.js.map +1 -1
  165. package/dist/generated/slider.js +1 -1
  166. package/dist/generated/slider.js.map +1 -1
  167. package/dist/generated/smart-header.js +11 -0
  168. package/dist/generated/smart-header.js.map +1 -0
  169. package/dist/generated/sonner.js +4 -4
  170. package/dist/generated/sonner.js.map +1 -1
  171. package/dist/generated/status-item.js +17 -0
  172. package/dist/generated/status-item.js.map +1 -0
  173. package/dist/generated/system-banner.js +33 -0
  174. package/dist/generated/system-banner.js.map +1 -0
  175. package/dist/generated/top-bar.js +9 -0
  176. package/dist/generated/top-bar.js.map +1 -0
  177. package/dist/generated/workspace-tab.js +17 -0
  178. package/dist/generated/workspace-tab.js.map +1 -0
  179. package/dist/index.d.ts +852 -156
  180. package/dist/index.js +21 -1
  181. package/dist/lib/cn.js +8 -1
  182. package/dist/lib/cn.js.map +1 -1
  183. package/dist/lib/number-parts.js +90 -0
  184. package/dist/lib/number-parts.js.map +1 -0
  185. package/dist/palette-cli.js +1 -1
  186. package/dist/palette.d.ts +6 -4
  187. package/dist/palette.js +3 -1
  188. package/dist/primitives/dismissable-layer.js +76 -4
  189. package/dist/primitives/dismissable-layer.js.map +1 -1
  190. package/dist/primitives/focus-scope.js +20 -5
  191. package/dist/primitives/focus-scope.js.map +1 -1
  192. package/dist/primitives/use-floating.js +16 -1
  193. package/dist/primitives/use-floating.js.map +1 -1
  194. package/dist/primitives/use-reduced-motion.js +20 -0
  195. package/dist/primitives/use-reduced-motion.js.map +1 -0
  196. package/package.json +1 -1
  197. package/styles/ev.css +281 -49
  198. package/styles/theme.css +335 -39
  199. package/styles/tokens.css +643 -103
  200. package/dist/chunk-3IPB4QQJ.js.map +0 -1
  201. package/dist/components/terminal.js +0 -206
  202. package/dist/components/terminal.js.map +0 -1
  203. package/dist/generated/terminal.js +0 -70
  204. package/dist/generated/terminal.js.map +0 -1
@@ -2,7 +2,16 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { cn } from "../lib/cn.js";
5
- import { SELECT_CONTENT_BOUNDS, SELECT_ITEM, selectTriggerSizeClasses } from "../generated/select.js";
5
+ import {
6
+ SELECT_CONTENT_BOUNDS,
7
+ SELECT_CONTENT_SURFACE,
8
+ SELECT_ITEM,
9
+ SELECT_LABEL,
10
+ SELECT_LIST,
11
+ SELECT_SEPARATOR,
12
+ SELECT_TRIGGER_BASE,
13
+ selectTriggerSizeClasses
14
+ } from "../generated/select.js";
6
15
  import { useControllableState } from "../primitives/use-controllable-state.js";
7
16
  import { useFloating } from "../primitives/use-floating.js";
8
17
  import { DismissableLayerScope, useDismissableLayer } from "../primitives/dismissable-layer.js";
@@ -138,7 +147,7 @@ function SelectTrigger({
138
147
  },
139
148
  className: cn(
140
149
  // The placeholder mark is on `SelectValue`'s span, not on this button.
141
- "border-input [&_[data-placeholder]]:text-ink-soft [&_svg:not([class*='text-'])]:text-ink-soft focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-accent-error/20 aria-invalid:border-accent-error flex w-fit items-center justify-between gap-2 rounded-[var(--control-radius)] border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
150
+ SELECT_TRIGGER_BASE,
142
151
  selectTriggerSizeClasses[size],
143
152
  className
144
153
  ),
@@ -228,12 +237,13 @@ function SelectContent({
228
237
  }
229
238
  },
230
239
  className: cn(
231
- "bg-popover text-ink data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-border shadow-md",
240
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 origin-(--radix-select-content-transform-origin)",
241
+ SELECT_CONTENT_SURFACE,
232
242
  SELECT_CONTENT_BOUNDS,
233
243
  className
234
244
  ),
235
245
  ...props,
236
- children: /* @__PURE__ */ jsx("div", { className: "p-1", children: /* @__PURE__ */ jsx(DismissableLayerScope, { layer: dismissRef, children }) })
246
+ children: /* @__PURE__ */ jsx("div", { className: SELECT_LIST, children: /* @__PURE__ */ jsx(DismissableLayerScope, { layer: dismissRef, children }) })
237
247
  }
238
248
  ) });
239
249
  }
@@ -302,7 +312,7 @@ function SelectLabel({ className, ...props }) {
302
312
  "div",
303
313
  {
304
314
  "data-slot": "select-label",
305
- className: cn("text-ink-soft px-2 py-1.5 text-xs", className),
315
+ className: cn(SELECT_LABEL, className),
306
316
  ...props
307
317
  }
308
318
  );
@@ -312,7 +322,7 @@ function SelectSeparator({ className, ...props }) {
312
322
  "div",
313
323
  {
314
324
  "data-slot": "select-separator",
315
- className: cn("bg-border pointer-events-none -mx-1 my-1 h-px", className),
325
+ className: cn(SELECT_SEPARATOR, className),
316
326
  ...props
317
327
  }
318
328
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/select.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { SELECT_CONTENT_BOUNDS, SELECT_ITEM, selectTriggerSizeClasses, type SelectTriggerSize } from \"../generated/select\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { useFloating } from \"../primitives/use-floating\";\nimport { DismissableLayerScope, useDismissableLayer } from \"../primitives/dismissable-layer\";\nimport { usePresence } from \"../primitives/presence\";\nimport { mergeRefs } from \"../primitives/merge-refs\";\nimport { Portal } from \"../primitives/portal\";\nimport { walkElements } from \"../primitives/walk-elements\";\nimport { markLabelable } from \"../primitives/labelable\";\nimport { useFieldControlId } from \"./field-context\";\nimport { SelectChevron } from \"./select-chevron\";\nimport { landOnChosen, tabFromTrigger, useListboxKeys } from \"./select-listbox\";\n\nexport type { SelectTriggerSize };\n\ninterface SelectContextValue {\n value: string;\n resetValue: string | undefined;\n setValue: (next: string) => void;\n open: boolean;\n setOpen: (next: boolean) => void;\n /**\n * Closes the list. `restoreFocus`: hand focus back to the trigger — after a\n * key or a choice, never after a click elsewhere, where it would land on the\n * trigger for a moment and fire its blur (a field's validation) on the way out.\n */\n close: (restoreFocus: boolean) => void;\n /** Set by `close(true)`, read once the list is gone. */\n restoreFocusRef: React.RefObject<boolean>;\n anchorRef: React.RefObject<HTMLElement | null>;\n /** The listbox's id, for the trigger's `aria-controls`. */\n contentId: string;\n /** What `SelectValue` shows for a value: the matching `SelectItem`'s children. */\n labelOf: (value: string) => string;\n registerLabel: (value: string, label: string) => void;\n}\n\nconst SelectContext = React.createContext<SelectContextValue | null>(null);\n\nfunction useSelect(): SelectContextValue {\n const ctx = React.useContext(SelectContext);\n if (!ctx) throw new Error(\"Select parts must be used within <Select>\");\n return ctx;\n}\n\nexport interface SelectProps {\n value?: string;\n defaultValue?: string;\n /** What a reset would put back: its option is marked, beneath the chosen one's mark. */\n resetValue?: string;\n onValueChange?: (value: string) => void;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: React.ReactNode;\n}\n\nexport function Select({\n value,\n defaultValue = \"\",\n resetValue,\n onValueChange,\n open,\n defaultOpen = false,\n onOpenChange,\n children,\n}: SelectProps) {\n const [currentValue, setValue] = useControllableState<string>({\n ...(value !== undefined ? { value } : {}),\n defaultValue,\n ...(onValueChange ? { onChange: onValueChange } : {}),\n });\n const [isOpen, setOpen] = useControllableState<boolean>({\n ...(open !== undefined ? { value: open } : {}),\n defaultValue: defaultOpen,\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n const anchorRef = React.useRef<HTMLElement | null>(null);\n const contentId = React.useId();\n const restoreFocusRef = React.useRef(false);\n const close = React.useCallback(\n (restoreFocus: boolean) => {\n restoreFocusRef.current = restoreFocus;\n setOpen(false);\n },\n [setOpen],\n );\n // The items live in a closed popover, so they are not mounted when the\n // trigger first renders — on the server least of all. Their labels are read\n // off the element tree instead; items hidden behind a component of the\n // caller's register once they have mounted (i.e. after the first open), and\n // a label learnt that way re-renders the trigger once.\n const declared = React.useMemo(() => collectItemLabels(children), [children]);\n const mounted = React.useRef(new Map<string, string>());\n const [, bump] = React.useReducer((n: number) => n + 1, 0);\n const labelOf = (v: string) => declared.get(v) ?? mounted.current.get(v) ?? v;\n const registerLabel = React.useCallback(\n (v: string, label: string) => {\n if (mounted.current.get(v) === label) return;\n mounted.current.set(v, label);\n if (!declared.has(v)) bump();\n },\n [declared],\n );\n return (\n <SelectContext.Provider\n value={{\n value: currentValue,\n resetValue,\n setValue,\n open: isOpen,\n setOpen,\n close,\n restoreFocusRef,\n anchorRef,\n contentId,\n labelOf,\n registerLabel,\n }}\n >\n {children}\n </SelectContext.Provider>\n );\n}\n\nfunction collectItemLabels(node: React.ReactNode): Map<string, string> {\n const labels = new Map<string, string>();\n walkElements(node, (element) => {\n if (element.type !== SelectItem) return true;\n const { value, textValue, children } = element.props as Partial<SelectItemProps>;\n if (typeof value === \"string\") labels.set(value, itemLabel(value, textValue, children));\n return false;\n });\n return labels;\n}\n\n/**\n * What the trigger shows for an item: its `textValue`, else the text of its\n * children. Text, not the children themselves — an item's markup rendered a\n * second time in the trigger would duplicate any id inside it.\n */\nfunction itemLabel(value: string, textValue: string | undefined, children: unknown): string {\n return textValue ?? (textOf(children).trim() || value);\n}\n\nfunction textOf(node: unknown): string {\n if (typeof node === \"string\" || typeof node === \"number\") return String(node);\n if (Array.isArray(node)) return node.map(textOf).join(\"\");\n if (React.isValidElement<{ children?: unknown }>(node)) return textOf(node.props.children);\n return \"\";\n}\n\nexport interface SelectTriggerProps extends React.ComponentProps<\"button\"> {\n size?: SelectTriggerSize;\n}\n\nexport function SelectTrigger({\n className,\n size = \"md\",\n onClick,\n onKeyDown,\n children,\n id,\n ...props\n}: SelectTriggerProps) {\n const { open, setOpen, close, anchorRef, contentId } = useSelect();\n return (\n <button\n type=\"button\"\n role=\"combobox\"\n id={useFieldControlId(id)}\n data-slot=\"select-trigger\"\n data-size={size}\n data-state={open ? \"open\" : \"closed\"}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-controls={open ? contentId : undefined}\n ref={anchorRef as React.Ref<HTMLButtonElement>}\n onClick={(e) => {\n onClick?.(e);\n if (open) close(true);\n else setOpen(true);\n }}\n onKeyDown={(e) => {\n onKeyDown?.(e);\n // A native select opens on the arrows too; Enter and Space already click the button.\n if (!e.defaultPrevented && !open && (e.key === \"ArrowDown\" || e.key === \"ArrowUp\")) {\n e.preventDefault();\n setOpen(true);\n }\n }}\n className={cn(\n // The placeholder mark is on `SelectValue`'s span, not on this button.\n \"border-input [&_[data-placeholder]]:text-ink-soft [&_svg:not([class*='text-'])]:text-ink-soft focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-accent-error/20 aria-invalid:border-accent-error flex w-fit items-center justify-between gap-2 rounded-[var(--control-radius)] border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n selectTriggerSizeClasses[size],\n className,\n )}\n {...(props as Record<string, unknown>)}\n >\n {children}\n <SelectChevron className=\"size-4 opacity-50\" />\n </button>\n );\n}\n\nmarkLabelable(SelectTrigger);\n\nexport interface SelectValueProps extends React.ComponentProps<\"span\"> {\n placeholder?: string;\n}\n\n/**\n * The chosen option's label — the `textValue` of the `SelectItem` whose `value`\n * matches, else the text of its children — or `placeholder` while nothing is\n * chosen. `children`, when given,\n * replace the label outright.\n */\nexport function SelectValue({ className, placeholder, children, ...props }: SelectValueProps) {\n const { value, labelOf } = useSelect();\n const isEmpty = value === \"\";\n return (\n <span\n data-slot=\"select-value\"\n {...(isEmpty ? { \"data-placeholder\": \"true\" } : {})}\n className={className}\n {...(props as Record<string, unknown>)}\n >\n {isEmpty ? placeholder : (children ?? labelOf(value))}\n </span>\n );\n}\n\nexport interface SelectContentProps extends React.ComponentProps<\"div\"> {\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n align?: \"start\" | \"center\" | \"end\";\n sideOffset?: number;\n}\n\nexport function SelectContent({\n className,\n side: sideProp = \"bottom\",\n align = \"start\",\n sideOffset = 4,\n children,\n ...props\n}: SelectContentProps) {\n const { open, setOpen, close, restoreFocusRef, anchorRef, contentId } = useSelect();\n const { isPresent, ref: presRef } = usePresence(open);\n const listRef = React.useRef<HTMLDivElement | null>(null);\n const [name, setName] = React.useState<{ \"aria-labelledby\"?: string; \"aria-label\"?: string }>({});\n // Before placement measures the list: the trigger's width as\n // `--select-trigger-width` (the floor of `SELECT_CONTENT_BOUNDS`), written to\n // the element like the floating offset, and the list's name — the trigger's\n // label, as the combobox is named.\n React.useLayoutEffect(() => {\n const anchor = anchorRef.current;\n if (!open || !(anchor instanceof HTMLElement)) return;\n listRef.current?.style.setProperty(\"--select-trigger-width\", `${anchor.offsetWidth}px`);\n setName(listName(anchor));\n }, [open, anchorRef]);\n const { floatingRef, style, side } = useFloating({\n anchorRef,\n open,\n side: sideProp,\n align,\n offset: sideOffset,\n });\n const dismissRef = useDismissableLayer({\n enabled: open,\n onDismiss: (event) => close(event.type === \"keydown\"),\n exclude: [anchorRef],\n });\n const onListKey = useListboxKeys(listRef);\n React.useEffect(() => {\n if (open) landOnChosen(listRef.current);\n }, [open]);\n React.useEffect(() => {\n if (open || !restoreFocusRef.current) return;\n restoreFocusRef.current = false;\n anchorRef.current?.focus();\n }, [open, anchorRef, restoreFocusRef]);\n if (!isPresent) return null;\n return (\n <Portal>\n <div\n role=\"listbox\"\n id={contentId}\n {...name}\n data-slot=\"select-content\"\n data-state={open ? \"open\" : \"closed\"}\n data-side={side}\n ref={mergeRefs(floatingRef, dismissRef, presRef, listRef)}\n style={style}\n tabIndex={-1}\n onKeyDown={(e) => {\n if (onListKey(e)) return;\n if (e.key === \"Escape\") {\n // The list's Escape is the list's: a consumer's `onKeyDown` around\n // it hears the key through React's tree, portal or not.\n e.stopPropagation();\n close(true);\n } else if (e.key === \"Tab\") {\n tabFromTrigger(e, anchorRef.current);\n setOpen(false);\n }\n }}\n className={cn(\n \"bg-popover text-ink data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-border shadow-md\",\n SELECT_CONTENT_BOUNDS,\n className,\n )}\n {...(props as Record<string, unknown>)}\n >\n <div className=\"p-1\"><DismissableLayerScope layer={dismissRef}>{children}</DismissableLayerScope></div>\n </div>\n </Portal>\n );\n}\n\n/** The trigger's own name for the list: its `aria-labelledby`, else its `<label>`. */\nfunction listName(trigger: HTMLElement): { \"aria-labelledby\"?: string; \"aria-label\"?: string } {\n const by = trigger.getAttribute(\"aria-labelledby\");\n if (by) return { \"aria-labelledby\": by };\n const label = (trigger as HTMLButtonElement).labels?.[0];\n if (label?.id) return { \"aria-labelledby\": label.id };\n const text = label?.textContent?.trim() || trigger.getAttribute(\"aria-label\");\n return text ? { \"aria-label\": text } : {};\n}\n\nexport interface SelectItemProps extends React.ComponentProps<\"div\"> {\n value: string;\n /** What the trigger shows once this item is chosen; defaults to the text of `children`. */\n textValue?: string;\n}\n\nexport function SelectItem({ className, value, textValue, children, ...props }: SelectItemProps) {\n const { value: selectedValue, resetValue, setValue, close, registerLabel } = useSelect();\n const selected = selectedValue === value;\n const label = itemLabel(value, textValue, children);\n React.useEffect(() => registerLabel(value, label), [registerLabel, value, label]);\n const choose = () => {\n setValue(value);\n close(true);\n };\n return (\n <div\n role=\"option\"\n data-slot=\"select-item\"\n aria-selected={selected}\n {...(resetValue === value ? { \"data-reset\": \"\" } : {})}\n tabIndex={-1}\n onClick={choose}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n choose();\n }\n }}\n className={cn(SELECT_ITEM, className)}\n {...(props as Record<string, unknown>)}\n >\n {selected ? (\n <span className=\"absolute right-2 flex size-3.5 items-center justify-center\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4\"\n aria-hidden=\"true\"\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </span>\n ) : null}\n <span>{children}</span>\n </div>\n );\n}\n\nexport function SelectGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div role=\"group\" data-slot=\"select-group\" className={className} {...props} />;\n}\n\nexport function SelectLabel({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"select-label\"\n className={cn(\"text-ink-soft px-2 py-1.5 text-xs\", className)}\n {...props}\n />\n );\n}\n\nexport function SelectSeparator({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"select-separator\"\n className={cn(\"bg-border pointer-events-none -mx-1 my-1 h-px\", className)}\n {...props}\n />\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/select.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport {\n SELECT_CONTENT_BOUNDS,\n SELECT_CONTENT_SURFACE,\n SELECT_ITEM,\n SELECT_LABEL,\n SELECT_LIST,\n SELECT_SEPARATOR,\n SELECT_TRIGGER_BASE,\n selectTriggerSizeClasses,\n type SelectTriggerSize,\n} from \"../generated/select\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { useFloating } from \"../primitives/use-floating\";\nimport { DismissableLayerScope, useDismissableLayer } from \"../primitives/dismissable-layer\";\nimport { usePresence } from \"../primitives/presence\";\nimport { mergeRefs } from \"../primitives/merge-refs\";\nimport { Portal } from \"../primitives/portal\";\nimport { walkElements } from \"../primitives/walk-elements\";\nimport { markLabelable } from \"../primitives/labelable\";\nimport { useFieldControlId } from \"./field-context\";\nimport { SelectChevron } from \"./select-chevron\";\nimport { landOnChosen, tabFromTrigger, useListboxKeys } from \"./select-listbox\";\n\nexport type { SelectTriggerSize };\n\ninterface SelectContextValue {\n value: string;\n resetValue: string | undefined;\n setValue: (next: string) => void;\n open: boolean;\n setOpen: (next: boolean) => void;\n /**\n * Closes the list. `restoreFocus`: hand focus back to the trigger — after a\n * key or a choice, never after a click elsewhere, where it would land on the\n * trigger for a moment and fire its blur (a field's validation) on the way out.\n */\n close: (restoreFocus: boolean) => void;\n /** Set by `close(true)`, read once the list is gone. */\n restoreFocusRef: React.RefObject<boolean>;\n anchorRef: React.RefObject<HTMLElement | null>;\n /** The listbox's id, for the trigger's `aria-controls`. */\n contentId: string;\n /** What `SelectValue` shows for a value: the matching `SelectItem`'s children. */\n labelOf: (value: string) => string;\n registerLabel: (value: string, label: string) => void;\n}\n\nconst SelectContext = React.createContext<SelectContextValue | null>(null);\n\nfunction useSelect(): SelectContextValue {\n const ctx = React.useContext(SelectContext);\n if (!ctx) throw new Error(\"Select parts must be used within <Select>\");\n return ctx;\n}\n\nexport interface SelectProps {\n value?: string;\n defaultValue?: string;\n /** What a reset would put back: its option is marked, beneath the chosen one's mark. */\n resetValue?: string;\n onValueChange?: (value: string) => void;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: React.ReactNode;\n}\n\nexport function Select({\n value,\n defaultValue = \"\",\n resetValue,\n onValueChange,\n open,\n defaultOpen = false,\n onOpenChange,\n children,\n}: SelectProps) {\n const [currentValue, setValue] = useControllableState<string>({\n ...(value !== undefined ? { value } : {}),\n defaultValue,\n ...(onValueChange ? { onChange: onValueChange } : {}),\n });\n const [isOpen, setOpen] = useControllableState<boolean>({\n ...(open !== undefined ? { value: open } : {}),\n defaultValue: defaultOpen,\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n const anchorRef = React.useRef<HTMLElement | null>(null);\n const contentId = React.useId();\n const restoreFocusRef = React.useRef(false);\n const close = React.useCallback(\n (restoreFocus: boolean) => {\n restoreFocusRef.current = restoreFocus;\n setOpen(false);\n },\n [setOpen],\n );\n // The items live in a closed popover, so they are not mounted when the\n // trigger first renders — on the server least of all. Their labels are read\n // off the element tree instead; items hidden behind a component of the\n // caller's register once they have mounted (i.e. after the first open), and\n // a label learnt that way re-renders the trigger once.\n const declared = React.useMemo(() => collectItemLabels(children), [children]);\n const mounted = React.useRef(new Map<string, string>());\n const [, bump] = React.useReducer((n: number) => n + 1, 0);\n const labelOf = (v: string) => declared.get(v) ?? mounted.current.get(v) ?? v;\n const registerLabel = React.useCallback(\n (v: string, label: string) => {\n if (mounted.current.get(v) === label) return;\n mounted.current.set(v, label);\n if (!declared.has(v)) bump();\n },\n [declared],\n );\n return (\n <SelectContext.Provider\n value={{\n value: currentValue,\n resetValue,\n setValue,\n open: isOpen,\n setOpen,\n close,\n restoreFocusRef,\n anchorRef,\n contentId,\n labelOf,\n registerLabel,\n }}\n >\n {children}\n </SelectContext.Provider>\n );\n}\n\nfunction collectItemLabels(node: React.ReactNode): Map<string, string> {\n const labels = new Map<string, string>();\n walkElements(node, (element) => {\n if (element.type !== SelectItem) return true;\n const { value, textValue, children } = element.props as Partial<SelectItemProps>;\n if (typeof value === \"string\") labels.set(value, itemLabel(value, textValue, children));\n return false;\n });\n return labels;\n}\n\n/**\n * What the trigger shows for an item: its `textValue`, else the text of its\n * children. Text, not the children themselves — an item's markup rendered a\n * second time in the trigger would duplicate any id inside it.\n */\nfunction itemLabel(value: string, textValue: string | undefined, children: unknown): string {\n return textValue ?? (textOf(children).trim() || value);\n}\n\nfunction textOf(node: unknown): string {\n if (typeof node === \"string\" || typeof node === \"number\") return String(node);\n if (Array.isArray(node)) return node.map(textOf).join(\"\");\n if (React.isValidElement<{ children?: unknown }>(node)) return textOf(node.props.children);\n return \"\";\n}\n\nexport interface SelectTriggerProps extends React.ComponentProps<\"button\"> {\n size?: SelectTriggerSize;\n}\n\nexport function SelectTrigger({\n className,\n size = \"md\",\n onClick,\n onKeyDown,\n children,\n id,\n ...props\n}: SelectTriggerProps) {\n const { open, setOpen, close, anchorRef, contentId } = useSelect();\n return (\n <button\n type=\"button\"\n role=\"combobox\"\n id={useFieldControlId(id)}\n data-slot=\"select-trigger\"\n data-size={size}\n data-state={open ? \"open\" : \"closed\"}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-controls={open ? contentId : undefined}\n ref={anchorRef as React.Ref<HTMLButtonElement>}\n onClick={(e) => {\n onClick?.(e);\n if (open) close(true);\n else setOpen(true);\n }}\n onKeyDown={(e) => {\n onKeyDown?.(e);\n // A native select opens on the arrows too; Enter and Space already click the button.\n if (!e.defaultPrevented && !open && (e.key === \"ArrowDown\" || e.key === \"ArrowUp\")) {\n e.preventDefault();\n setOpen(true);\n }\n }}\n className={cn(\n // The placeholder mark is on `SelectValue`'s span, not on this button.\n SELECT_TRIGGER_BASE,\n selectTriggerSizeClasses[size],\n className,\n )}\n {...(props as Record<string, unknown>)}\n >\n {children}\n <SelectChevron className=\"size-4 opacity-50\" />\n </button>\n );\n}\n\nmarkLabelable(SelectTrigger);\n\nexport interface SelectValueProps extends React.ComponentProps<\"span\"> {\n placeholder?: string;\n}\n\n/**\n * The chosen option's label — the `textValue` of the `SelectItem` whose `value`\n * matches, else the text of its children — or `placeholder` while nothing is\n * chosen. `children`, when given,\n * replace the label outright.\n */\nexport function SelectValue({ className, placeholder, children, ...props }: SelectValueProps) {\n const { value, labelOf } = useSelect();\n const isEmpty = value === \"\";\n return (\n <span\n data-slot=\"select-value\"\n {...(isEmpty ? { \"data-placeholder\": \"true\" } : {})}\n className={className}\n {...(props as Record<string, unknown>)}\n >\n {isEmpty ? placeholder : (children ?? labelOf(value))}\n </span>\n );\n}\n\nexport interface SelectContentProps extends React.ComponentProps<\"div\"> {\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n align?: \"start\" | \"center\" | \"end\";\n sideOffset?: number;\n}\n\nexport function SelectContent({\n className,\n side: sideProp = \"bottom\",\n align = \"start\",\n sideOffset = 4,\n children,\n ...props\n}: SelectContentProps) {\n const { open, setOpen, close, restoreFocusRef, anchorRef, contentId } = useSelect();\n const { isPresent, ref: presRef } = usePresence(open);\n const listRef = React.useRef<HTMLDivElement | null>(null);\n const [name, setName] = React.useState<{ \"aria-labelledby\"?: string; \"aria-label\"?: string }>({});\n // Before placement measures the list: the trigger's width as\n // `--select-trigger-width` (the floor of `SELECT_CONTENT_BOUNDS`), written to\n // the element like the floating offset, and the list's name — the trigger's\n // label, as the combobox is named.\n React.useLayoutEffect(() => {\n const anchor = anchorRef.current;\n if (!open || !(anchor instanceof HTMLElement)) return;\n listRef.current?.style.setProperty(\"--select-trigger-width\", `${anchor.offsetWidth}px`);\n setName(listName(anchor));\n }, [open, anchorRef]);\n const { floatingRef, style, side } = useFloating({\n anchorRef,\n open,\n side: sideProp,\n align,\n offset: sideOffset,\n });\n const dismissRef = useDismissableLayer({\n enabled: open,\n onDismiss: (event) => close(event.type === \"keydown\"),\n exclude: [anchorRef],\n });\n const onListKey = useListboxKeys(listRef);\n React.useEffect(() => {\n if (open) landOnChosen(listRef.current);\n }, [open]);\n React.useEffect(() => {\n if (open || !restoreFocusRef.current) return;\n restoreFocusRef.current = false;\n anchorRef.current?.focus();\n }, [open, anchorRef, restoreFocusRef]);\n if (!isPresent) return null;\n return (\n <Portal>\n <div\n role=\"listbox\"\n id={contentId}\n {...name}\n data-slot=\"select-content\"\n data-state={open ? \"open\" : \"closed\"}\n data-side={side}\n ref={mergeRefs(floatingRef, dismissRef, presRef, listRef)}\n style={style}\n tabIndex={-1}\n onKeyDown={(e) => {\n if (onListKey(e)) return;\n if (e.key === \"Escape\") {\n // The list's Escape is the list's: a consumer's `onKeyDown` around\n // it hears the key through React's tree, portal or not.\n e.stopPropagation();\n close(true);\n } else if (e.key === \"Tab\") {\n tabFromTrigger(e, anchorRef.current);\n setOpen(false);\n }\n }}\n className={cn(\n \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 origin-(--radix-select-content-transform-origin)\",\n SELECT_CONTENT_SURFACE,\n SELECT_CONTENT_BOUNDS,\n className,\n )}\n {...(props as Record<string, unknown>)}\n >\n <div className={SELECT_LIST}><DismissableLayerScope layer={dismissRef}>{children}</DismissableLayerScope></div>\n </div>\n </Portal>\n );\n}\n\n/** The trigger's own name for the list: its `aria-labelledby`, else its `<label>`. */\nfunction listName(trigger: HTMLElement): { \"aria-labelledby\"?: string; \"aria-label\"?: string } {\n const by = trigger.getAttribute(\"aria-labelledby\");\n if (by) return { \"aria-labelledby\": by };\n const label = (trigger as HTMLButtonElement).labels?.[0];\n if (label?.id) return { \"aria-labelledby\": label.id };\n const text = label?.textContent?.trim() || trigger.getAttribute(\"aria-label\");\n return text ? { \"aria-label\": text } : {};\n}\n\nexport interface SelectItemProps extends React.ComponentProps<\"div\"> {\n value: string;\n /** What the trigger shows once this item is chosen; defaults to the text of `children`. */\n textValue?: string;\n}\n\nexport function SelectItem({ className, value, textValue, children, ...props }: SelectItemProps) {\n const { value: selectedValue, resetValue, setValue, close, registerLabel } = useSelect();\n const selected = selectedValue === value;\n const label = itemLabel(value, textValue, children);\n React.useEffect(() => registerLabel(value, label), [registerLabel, value, label]);\n const choose = () => {\n setValue(value);\n close(true);\n };\n return (\n <div\n role=\"option\"\n data-slot=\"select-item\"\n aria-selected={selected}\n {...(resetValue === value ? { \"data-reset\": \"\" } : {})}\n tabIndex={-1}\n onClick={choose}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n choose();\n }\n }}\n className={cn(SELECT_ITEM, className)}\n {...(props as Record<string, unknown>)}\n >\n {selected ? (\n <span className=\"absolute right-2 flex size-3.5 items-center justify-center\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4\"\n aria-hidden=\"true\"\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </span>\n ) : null}\n <span>{children}</span>\n </div>\n );\n}\n\nexport function SelectGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div role=\"group\" data-slot=\"select-group\" className={className} {...props} />;\n}\n\nexport function SelectLabel({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"select-label\"\n className={cn(SELECT_LABEL, className)}\n {...props}\n />\n );\n}\n\nexport function SelectSeparator({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"select-separator\"\n className={cn(SELECT_SEPARATOR, className)}\n {...props}\n />\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,94 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../lib/cn.js";
5
+ import {
6
+ SETTINGS_LAYOUT,
7
+ SETTINGS_MAIN,
8
+ SETTINGS_NAV,
9
+ SETTINGS_ROW_INLINE,
10
+ SETTINGS_SECTION_ACTION,
11
+ SETTINGS_SECTION_BASE,
12
+ SETTINGS_SECTION_BODY,
13
+ SETTINGS_SECTION_BODY_FLUSH,
14
+ SETTINGS_SECTION_BODY_PADDED,
15
+ SETTINGS_SECTION_DESCRIPTION,
16
+ SETTINGS_SECTION_HEADER,
17
+ SETTINGS_SECTION_HEADER_FLUSH,
18
+ SETTINGS_SECTION_HEADING,
19
+ SETTINGS_SECTION_TITLE,
20
+ settingsSectionToneClasses
21
+ } from "../generated/settings.js";
22
+ import { FormRow } from "./form-row.js";
23
+ function SettingsLayout({ nav, className, children, ...props }) {
24
+ return /* @__PURE__ */ jsxs(
25
+ "div",
26
+ {
27
+ "data-slot": "settings-layout",
28
+ className: cn(
29
+ SETTINGS_LAYOUT,
30
+ className
31
+ ),
32
+ ...props,
33
+ children: [
34
+ /* @__PURE__ */ jsx("div", { "data-slot": "settings-nav", className: SETTINGS_NAV, children: nav }),
35
+ /* @__PURE__ */ jsx("div", { "data-slot": "settings-main", className: SETTINGS_MAIN, children })
36
+ ]
37
+ }
38
+ );
39
+ }
40
+ function SettingsSection({
41
+ title,
42
+ description,
43
+ action,
44
+ tone = "default",
45
+ flush = false,
46
+ className,
47
+ children,
48
+ ...props
49
+ }) {
50
+ const headingId = React.useId();
51
+ return /* @__PURE__ */ jsxs(
52
+ "section",
53
+ {
54
+ "data-slot": "settings-section",
55
+ "data-tone": tone,
56
+ "aria-labelledby": headingId,
57
+ className: cn(
58
+ SETTINGS_SECTION_BASE,
59
+ settingsSectionToneClasses[tone],
60
+ className
61
+ ),
62
+ ...props,
63
+ children: [
64
+ /* @__PURE__ */ jsxs("header", { className: cn(SETTINGS_SECTION_HEADER, flush && SETTINGS_SECTION_HEADER_FLUSH), children: [
65
+ /* @__PURE__ */ jsxs("div", { className: SETTINGS_SECTION_HEADING, children: [
66
+ /* @__PURE__ */ jsx("h3", { id: headingId, className: SETTINGS_SECTION_TITLE, children: title }),
67
+ description != null && /* @__PURE__ */ jsx("p", { className: SETTINGS_SECTION_DESCRIPTION, children: description })
68
+ ] }),
69
+ action != null && /* @__PURE__ */ jsx("div", { className: SETTINGS_SECTION_ACTION, children: action })
70
+ ] }),
71
+ children != null && /* @__PURE__ */ jsx("div", { className: cn(SETTINGS_SECTION_BODY, flush ? SETTINGS_SECTION_BODY_FLUSH : SETTINGS_SECTION_BODY_PADDED), children })
72
+ ]
73
+ }
74
+ );
75
+ }
76
+ function SettingsRow({ inline = false, className, ...props }) {
77
+ return /* @__PURE__ */ jsx(
78
+ FormRow,
79
+ {
80
+ "data-inline": inline,
81
+ className: cn(
82
+ inline && SETTINGS_ROW_INLINE,
83
+ className
84
+ ),
85
+ ...props
86
+ }
87
+ );
88
+ }
89
+ export {
90
+ SettingsLayout,
91
+ SettingsRow,
92
+ SettingsSection
93
+ };
94
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/settings.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport {\n SETTINGS_LAYOUT,\n SETTINGS_MAIN,\n SETTINGS_NAV,\n SETTINGS_ROW_INLINE,\n SETTINGS_SECTION_ACTION,\n SETTINGS_SECTION_BASE,\n SETTINGS_SECTION_BODY,\n SETTINGS_SECTION_BODY_FLUSH,\n SETTINGS_SECTION_BODY_PADDED,\n SETTINGS_SECTION_DESCRIPTION,\n SETTINGS_SECTION_HEADER,\n SETTINGS_SECTION_HEADER_FLUSH,\n SETTINGS_SECTION_HEADING,\n SETTINGS_SECTION_TITLE,\n settingsSectionToneClasses,\n type SettingsSectionTone,\n} from \"../generated/settings\";\nimport { FormRow, type FormRowProps } from \"./form-row\";\n\nexport interface SettingsLayoutProps extends Omit<React.ComponentProps<\"div\">, \"title\"> {\n /** The section rail (`SectionNav`), 256 wide from `lg` up and on top below it. */\n nav: React.ReactNode;\n}\n\n/**\n * Rail on the left, cards in the main column; centres a 1408 px measure. The column is a\n * div: `AppShell` already renders the page's `<main>`.\n */\nexport function SettingsLayout({ nav, className, children, ...props }: SettingsLayoutProps) {\n return (\n <div\n data-slot=\"settings-layout\"\n className={cn(\n SETTINGS_LAYOUT,\n className,\n )}\n {...props}\n >\n <div data-slot=\"settings-nav\" className={SETTINGS_NAV}>\n {nav}\n </div>\n <div data-slot=\"settings-main\" className={SETTINGS_MAIN}>\n {children}\n </div>\n </div>\n );\n}\n\nexport interface SettingsSectionProps extends Omit<React.ComponentProps<\"section\">, \"title\"> {\n title: React.ReactNode;\n description?: React.ReactNode;\n /** Top-right control, e.g. a \"Create team\" button. */\n action?: React.ReactNode;\n /** `danger` draws the destructive-zone edge. */\n tone?: SettingsSectionTone;\n /** Rows run edge to edge with their hairlines; otherwise the body is padded. */\n flush?: boolean;\n}\n\n/** A settings card: title, a line saying what it is for, then the body. */\nexport function SettingsSection({\n title,\n description,\n action,\n tone = \"default\",\n flush = false,\n className,\n children,\n ...props\n}: SettingsSectionProps) {\n const headingId = React.useId();\n return (\n <section\n data-slot=\"settings-section\"\n data-tone={tone}\n aria-labelledby={headingId}\n className={cn(\n SETTINGS_SECTION_BASE,\n settingsSectionToneClasses[tone],\n className,\n )}\n {...props}\n >\n <header className={cn(SETTINGS_SECTION_HEADER, flush && SETTINGS_SECTION_HEADER_FLUSH)}>\n <div className={SETTINGS_SECTION_HEADING}>\n <h3 id={headingId} className={SETTINGS_SECTION_TITLE}>\n {title}\n </h3>\n {description != null && <p className={SETTINGS_SECTION_DESCRIPTION}>{description}</p>}\n </div>\n {action != null && <div className={SETTINGS_SECTION_ACTION}>{action}</div>}\n </header>\n {children != null && (\n <div className={cn(SETTINGS_SECTION_BODY, flush ? SETTINGS_SECTION_BODY_FLUSH : SETTINGS_SECTION_BODY_PADDED)}>\n {children}\n </div>\n )}\n </section>\n );\n}\n\nexport interface SettingsRowProps extends FormRowProps {\n /** Keeps label and control on one line at every width, control at the end: for a switch or a button. */\n inline?: boolean;\n}\n\n/** A `FormRow` for settings; `inline` suits toggles. */\nexport function SettingsRow({ inline = false, className, ...props }: SettingsRowProps) {\n return (\n <FormRow\n data-inline={inline}\n className={cn(\n inline && SETTINGS_ROW_INLINE,\n className,\n )}\n {...props}\n />\n );\n}\n"],"mappings":";AAmCI,SAQE,KARF;AAjCJ,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,eAAkC;AAWpC,SAAS,eAAe,EAAE,KAAK,WAAW,UAAU,GAAG,MAAM,GAAwB;AAC1F,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,4BAAC,SAAI,aAAU,gBAAe,WAAW,cACtC,eACH;AAAA,QACA,oBAAC,SAAI,aAAU,iBAAgB,WAAW,eACvC,UACH;AAAA;AAAA;AAAA,EACF;AAEJ;AAcO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAyB;AACvB,QAAM,YAAY,MAAM,MAAM;AAC9B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,aAAW;AAAA,MACX,mBAAiB;AAAA,MACjB,WAAW;AAAA,QACT;AAAA,QACA,2BAA2B,IAAI;AAAA,QAC/B;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,6BAAC,YAAO,WAAW,GAAG,yBAAyB,SAAS,6BAA6B,GACnF;AAAA,+BAAC,SAAI,WAAW,0BACd;AAAA,gCAAC,QAAG,IAAI,WAAW,WAAW,wBAC3B,iBACH;AAAA,YACC,eAAe,QAAQ,oBAAC,OAAE,WAAW,8BAA+B,uBAAY;AAAA,aACnF;AAAA,UACC,UAAU,QAAQ,oBAAC,SAAI,WAAW,yBAA0B,kBAAO;AAAA,WACtE;AAAA,QACC,YAAY,QACX,oBAAC,SAAI,WAAW,GAAG,uBAAuB,QAAQ,8BAA8B,4BAA4B,GACzG,UACH;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAQO,SAAS,YAAY,EAAE,SAAS,OAAO,WAAW,GAAG,MAAM,GAAqB;AACrF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAa;AAAA,MACb,WAAW;AAAA,QACT,UAAU;AAAA,QACV;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
@@ -3,7 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { cn } from "../lib/cn.js";
5
5
  import { useControllableState } from "../primitives/use-controllable-state.js";
6
- import { DismissableLayerScope, useDismissableLayer } from "../primitives/dismissable-layer.js";
6
+ import { DismissableLayerScope, useDismissableLayer, useModalScrimStyle } from "../primitives/dismissable-layer.js";
7
7
  import { useFocusScope } from "../primitives/focus-scope.js";
8
8
  import { usePresence } from "../primitives/presence.js";
9
9
  import { mergeRefs } from "../primitives/merge-refs.js";
@@ -104,9 +104,10 @@ function SheetContent({
104
104
  exclude: [triggerRef],
105
105
  modal: true
106
106
  });
107
+ const scrimStyle = useModalScrimStyle(dismissRef);
107
108
  if (!isPresent) return null;
108
109
  return /* @__PURE__ */ jsxs(Portal, { children: [
109
- /* @__PURE__ */ jsx(SheetOverlay, {}),
110
+ /* @__PURE__ */ jsx(SheetOverlay, { style: scrimStyle }),
110
111
  /* @__PURE__ */ jsxs(
111
112
  "div",
112
113
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/sheet.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { DismissableLayerScope, useDismissableLayer } from \"../primitives/dismissable-layer\";\nimport { type InitialFocus, useFocusScope } from \"../primitives/focus-scope\";\nimport { usePresence } from \"../primitives/presence\";\nimport { mergeRefs } from \"../primitives/merge-refs\";\nimport { Portal } from \"../primitives/portal\";\nimport { Slot } from \"../primitives/slot\";\nimport {\n SHEET_CLOSE,\n SHEET_CONTENT,\n SHEET_DESCRIPTION,\n SHEET_FOOTER,\n SHEET_HEADER,\n SHEET_OVERLAY,\n SHEET_SIDE_BOTTOM,\n SHEET_SIDE_LEFT,\n SHEET_SIDE_RIGHT,\n SHEET_SIDE_TOP,\n SHEET_TITLE,\n} from \"../generated/sheet\";\n\ninterface SheetContextValue {\n open: boolean;\n setOpen: (next: boolean) => void;\n triggerRef: React.RefObject<HTMLElement | null>;\n}\n\nconst SheetContext = React.createContext<SheetContextValue | null>(null);\n\nfunction useSheet(): SheetContextValue {\n const ctx = React.useContext(SheetContext);\n if (!ctx) throw new Error(\"Sheet parts must be used within <Sheet>\");\n return ctx;\n}\n\nexport interface SheetProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: React.ReactNode;\n}\n\nexport function Sheet({ open, defaultOpen = false, onOpenChange, children }: SheetProps) {\n const [isOpen, setOpen] = useControllableState<boolean>({\n ...(open !== undefined ? { value: open } : {}),\n defaultValue: defaultOpen,\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n const triggerRef = React.useRef<HTMLElement | null>(null);\n return (\n <SheetContext.Provider value={{ open: isOpen, setOpen, triggerRef }}>\n {children}\n </SheetContext.Provider>\n );\n}\n\nexport interface SheetTriggerProps extends React.ComponentProps<\"button\"> {\n asChild?: boolean;\n}\n\nexport function SheetTrigger({ asChild = false, onClick, ...props }: SheetTriggerProps) {\n const { open, setOpen, triggerRef } = useSheet();\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n data-slot=\"sheet-trigger\"\n aria-expanded={open}\n ref={triggerRef as React.Ref<HTMLButtonElement>}\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n setOpen(true);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport interface SheetOverlayProps extends React.ComponentProps<\"div\"> {}\n\nexport function SheetOverlay({ className, ...props }: SheetOverlayProps) {\n const { open } = useSheet();\n // No onClick: the content's dismissable layer already closes on the\n // pointer-down that lands here, and a second path called onOpenChange(false)\n // twice for one press whenever the overlay was still hit by the click\n // (a host that closes later, CSS without the closed-state pointer-events).\n return (\n <div\n data-slot=\"sheet-overlay\"\n data-state={open ? \"open\" : \"closed\"}\n className={cn(SHEET_OVERLAY, className)}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nconst sheetSideClasses = {\n right: SHEET_SIDE_RIGHT,\n left: SHEET_SIDE_LEFT,\n top: SHEET_SIDE_TOP,\n bottom: SHEET_SIDE_BOTTOM,\n} as const;\n\nconst XIcon = (props: React.SVGProps<SVGSVGElement>) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n);\n\nexport interface SheetContentProps extends React.ComponentProps<\"div\"> {\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n /**\n * Where focus lands on open. Default `container`: a sheet is a panel of\n * content, and its first Tab stop is as likely an action as a field — the\n * panel itself takes focus and the first Tab reaches the first control.\n */\n initialFocus?: InitialFocus;\n}\n\nexport function SheetContent({\n className,\n children,\n side = \"right\",\n initialFocus = \"container\",\n ...props\n}: SheetContentProps) {\n const { open, setOpen, triggerRef } = useSheet();\n const { isPresent, ref: presRef } = usePresence(open);\n const focusRef = useFocusScope(open, { initialFocus, returnFocusTo: triggerRef });\n const dismissRef = useDismissableLayer({\n enabled: open,\n onDismiss: () => setOpen(false),\n exclude: [triggerRef],\n modal: true,\n });\n if (!isPresent) return null;\n return (\n <Portal>\n <SheetOverlay />\n <div\n data-slot=\"sheet-content\"\n role=\"dialog\"\n aria-modal=\"true\"\n data-state={open ? \"open\" : \"closed\"}\n ref={mergeRefs(focusRef, dismissRef, presRef as React.Ref<HTMLDivElement>)}\n className={cn(SHEET_CONTENT, sheetSideClasses[side], className)}\n {...(props as Record<string, unknown>)}\n >\n <DismissableLayerScope layer={dismissRef}>{children}</DismissableLayerScope>\n <button\n type=\"button\"\n data-slot=\"sheet-close\"\n onClick={() => setOpen(false)}\n className={SHEET_CLOSE}\n >\n <XIcon className=\"size-4\" />\n <span className=\"sr-only\">Close</span>\n </button>\n </div>\n </Portal>\n );\n}\n\nexport interface SheetCloseProps extends React.ComponentProps<\"button\"> {\n asChild?: boolean;\n}\n\nexport function SheetClose({ asChild = false, onClick, ...props }: SheetCloseProps) {\n const { setOpen } = useSheet();\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n data-slot=\"sheet-close\"\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n setOpen(false);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport function SheetHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sheet-header\"\n className={cn(SHEET_HEADER, className)}\n {...props}\n />\n );\n}\n\nexport function SheetFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sheet-footer\"\n className={cn(SHEET_FOOTER, className)}\n {...props}\n />\n );\n}\n\nexport function SheetTitle({ className, ...props }: React.ComponentProps<\"h2\">) {\n return (\n <h2\n data-slot=\"sheet-title\"\n className={cn(SHEET_TITLE, className)}\n {...props}\n />\n );\n}\n\nexport function SheetDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n return (\n <p\n data-slot=\"sheet-description\"\n className={cn(SHEET_DESCRIPTION, className)}\n {...props}\n />\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/sheet.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { DismissableLayerScope, useDismissableLayer, useModalScrimStyle } from \"../primitives/dismissable-layer\";\nimport { type InitialFocus, useFocusScope } from \"../primitives/focus-scope\";\nimport { usePresence } from \"../primitives/presence\";\nimport { mergeRefs } from \"../primitives/merge-refs\";\nimport { Portal } from \"../primitives/portal\";\nimport { Slot } from \"../primitives/slot\";\nimport {\n SHEET_CLOSE,\n SHEET_CONTENT,\n SHEET_DESCRIPTION,\n SHEET_FOOTER,\n SHEET_HEADER,\n SHEET_OVERLAY,\n SHEET_SIDE_BOTTOM,\n SHEET_SIDE_LEFT,\n SHEET_SIDE_RIGHT,\n SHEET_SIDE_TOP,\n SHEET_TITLE,\n} from \"../generated/sheet\";\n\ninterface SheetContextValue {\n open: boolean;\n setOpen: (next: boolean) => void;\n triggerRef: React.RefObject<HTMLElement | null>;\n}\n\nconst SheetContext = React.createContext<SheetContextValue | null>(null);\n\nfunction useSheet(): SheetContextValue {\n const ctx = React.useContext(SheetContext);\n if (!ctx) throw new Error(\"Sheet parts must be used within <Sheet>\");\n return ctx;\n}\n\nexport interface SheetProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: React.ReactNode;\n}\n\nexport function Sheet({ open, defaultOpen = false, onOpenChange, children }: SheetProps) {\n const [isOpen, setOpen] = useControllableState<boolean>({\n ...(open !== undefined ? { value: open } : {}),\n defaultValue: defaultOpen,\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n const triggerRef = React.useRef<HTMLElement | null>(null);\n return (\n <SheetContext.Provider value={{ open: isOpen, setOpen, triggerRef }}>\n {children}\n </SheetContext.Provider>\n );\n}\n\nexport interface SheetTriggerProps extends React.ComponentProps<\"button\"> {\n asChild?: boolean;\n}\n\nexport function SheetTrigger({ asChild = false, onClick, ...props }: SheetTriggerProps) {\n const { open, setOpen, triggerRef } = useSheet();\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n data-slot=\"sheet-trigger\"\n aria-expanded={open}\n ref={triggerRef as React.Ref<HTMLButtonElement>}\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n setOpen(true);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport interface SheetOverlayProps extends React.ComponentProps<\"div\"> {}\n\nexport function SheetOverlay({ className, ...props }: SheetOverlayProps) {\n const { open } = useSheet();\n // No onClick: the content's dismissable layer already closes on the\n // pointer-down that lands here, and a second path called onOpenChange(false)\n // twice for one press whenever the overlay was still hit by the click\n // (a host that closes later, CSS without the closed-state pointer-events).\n return (\n <div\n data-slot=\"sheet-overlay\"\n data-state={open ? \"open\" : \"closed\"}\n className={cn(SHEET_OVERLAY, className)}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nconst sheetSideClasses = {\n right: SHEET_SIDE_RIGHT,\n left: SHEET_SIDE_LEFT,\n top: SHEET_SIDE_TOP,\n bottom: SHEET_SIDE_BOTTOM,\n} as const;\n\nconst XIcon = (props: React.SVGProps<SVGSVGElement>) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n);\n\nexport interface SheetContentProps extends React.ComponentProps<\"div\"> {\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n /**\n * Where focus lands on open. Default `container`: a sheet is a panel of\n * content, and its first Tab stop is as likely an action as a field — the\n * panel itself takes focus and the first Tab reaches the first control.\n */\n initialFocus?: InitialFocus;\n}\n\nexport function SheetContent({\n className,\n children,\n side = \"right\",\n initialFocus = \"container\",\n ...props\n}: SheetContentProps) {\n const { open, setOpen, triggerRef } = useSheet();\n const { isPresent, ref: presRef } = usePresence(open);\n const focusRef = useFocusScope(open, { initialFocus, returnFocusTo: triggerRef });\n const dismissRef = useDismissableLayer({\n enabled: open,\n onDismiss: () => setOpen(false),\n exclude: [triggerRef],\n modal: true,\n });\n const scrimStyle = useModalScrimStyle(dismissRef);\n if (!isPresent) return null;\n return (\n <Portal>\n <SheetOverlay style={scrimStyle} />\n <div\n data-slot=\"sheet-content\"\n role=\"dialog\"\n aria-modal=\"true\"\n data-state={open ? \"open\" : \"closed\"}\n ref={mergeRefs(focusRef, dismissRef, presRef as React.Ref<HTMLDivElement>)}\n className={cn(SHEET_CONTENT, sheetSideClasses[side], className)}\n {...(props as Record<string, unknown>)}\n >\n <DismissableLayerScope layer={dismissRef}>{children}</DismissableLayerScope>\n <button\n type=\"button\"\n data-slot=\"sheet-close\"\n onClick={() => setOpen(false)}\n className={SHEET_CLOSE}\n >\n <XIcon className=\"size-4\" />\n <span className=\"sr-only\">Close</span>\n </button>\n </div>\n </Portal>\n );\n}\n\nexport interface SheetCloseProps extends React.ComponentProps<\"button\"> {\n asChild?: boolean;\n}\n\nexport function SheetClose({ asChild = false, onClick, ...props }: SheetCloseProps) {\n const { setOpen } = useSheet();\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n data-slot=\"sheet-close\"\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n setOpen(false);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport function SheetHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sheet-header\"\n className={cn(SHEET_HEADER, className)}\n {...props}\n />\n );\n}\n\nexport function SheetFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sheet-footer\"\n className={cn(SHEET_FOOTER, className)}\n {...props}\n />\n );\n}\n\nexport function SheetTitle({ className, ...props }: React.ComponentProps<\"h2\">) {\n return (\n <h2\n data-slot=\"sheet-title\"\n className={cn(SHEET_TITLE, className)}\n {...props}\n />\n );\n}\n\nexport function SheetDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n return (\n <p\n data-slot=\"sheet-description\"\n className={cn(SHEET_DESCRIPTION, className)}\n {...props}\n />\n );\n}\n"],"mappings":"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","names":[]}
@@ -4,7 +4,11 @@ import * as React from "react";
4
4
  import { cn } from "../lib/cn.js";
5
5
  import { NavBadge } from "./nav-badge.js";
6
6
  import { labelTitle, NavLink } from "./nav-link.js";
7
- import { resolveActiveNavItem } from "./nav-model.js";
7
+ import { NavCollapse, NavGroupHeader, useNavGroupOpen } from "./nav-collapse.js";
8
+ import {
9
+ navItemMatch,
10
+ resolveActiveNavItem
11
+ } from "./nav-model.js";
8
12
  import { usePendingNavigation, useSlideMarker } from "./use-shell-nav.js";
9
13
  const NAV_FOCUS = "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-secondary";
10
14
  const ROW = "flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors";
@@ -19,6 +23,7 @@ function ShellNav({
19
23
  onItemIntent,
20
24
  onNavigate,
21
25
  labels = {},
26
+ collapsible = false,
22
27
  className,
23
28
  ...props
24
29
  }) {
@@ -31,6 +36,9 @@ function ShellNav({
31
36
  group,
32
37
  currentId,
33
38
  pendingId,
39
+ pathname,
40
+ labels,
41
+ collapsible,
34
42
  row: { linkComponent, markPending, onItemIntent, onNavigate, renderItem, badgeLabel: labels.badge }
35
43
  },
36
44
  group.id
@@ -58,48 +66,82 @@ function ShellNavSection({
58
66
  group,
59
67
  currentId,
60
68
  pendingId,
61
- row
69
+ pathname,
70
+ row,
71
+ labels,
72
+ collapsible
62
73
  }) {
63
- const root = React.useRef(null);
74
+ const body = React.useRef(null);
64
75
  const labelId = React.useId();
76
+ const regionId = React.useId();
65
77
  const at = group.items.some((i) => i.id === currentId) ? currentId ?? null : null;
66
- useSlideMarker(root, at);
67
- return /* @__PURE__ */ jsxs(
68
- "div",
69
- {
70
- ref: root,
71
- "data-slot": "shell-nav-section",
72
- className: cn("relative isolate flex flex-col gap-1", group.label !== void 0 && "pl-1"),
73
- children: [
74
- at !== null && /* @__PURE__ */ jsx(
75
- "span",
76
- {
77
- "data-slot": "shell-nav-marker",
78
- "aria-hidden": true,
79
- className: "pointer-events-none absolute left-0 top-0 -z-10 rounded-lg bg-primary"
80
- }
81
- ),
82
- group.label !== void 0 && /* @__PURE__ */ jsx("p", { id: labelId, className: "mb-0.5 text-xs font-semibold uppercase tracking-widest text-ink-soft", children: group.label }),
83
- /* @__PURE__ */ jsx(
84
- "ul",
78
+ const [open, setOpen] = useNavGroupOpen(group, at !== null);
79
+ useSlideMarker(body, at);
80
+ const folds = (group.collapsible ?? collapsible) && group.label !== void 0;
81
+ const landing = group.href !== void 0 && navItemMatch({ id: group.id, href: group.href, label: "", match: "exact" }, pathname) !== null;
82
+ const content = /* @__PURE__ */ jsxs("div", { ref: body, className: cn("relative isolate", folds && "pt-1"), children: [
83
+ at !== null && /* @__PURE__ */ jsx(
84
+ "span",
85
+ {
86
+ "data-slot": "shell-nav-marker",
87
+ "aria-hidden": true,
88
+ className: "pointer-events-none absolute left-0 top-0 -z-10 rounded-lg bg-primary"
89
+ }
90
+ ),
91
+ /* @__PURE__ */ jsx(
92
+ "ul",
93
+ {
94
+ "aria-labelledby": group.label !== void 0 ? labelId : void 0,
95
+ className: cn("flex flex-col gap-1", folds && "pl-3"),
96
+ children: group.items.map((item) => /* @__PURE__ */ jsx("li", { className: "flex flex-col", children: /* @__PURE__ */ jsx(
97
+ ShellNavRow,
85
98
  {
86
- "aria-labelledby": group.label !== void 0 ? labelId : void 0,
87
- className: "flex flex-col gap-1",
88
- children: group.items.map((item) => /* @__PURE__ */ jsx("li", { className: "flex flex-col", children: /* @__PURE__ */ jsx(
89
- ShellNavRow,
90
- {
91
- item,
92
- state: { active: item.id === currentId, pending: item.id === pendingId },
93
- row
94
- }
95
- ) }, item.id))
99
+ item,
100
+ nested: folds,
101
+ state: { active: item.id === currentId, pending: item.id === pendingId },
102
+ row
96
103
  }
97
- )
98
- ]
99
- }
100
- );
104
+ ) }, item.id))
105
+ }
106
+ )
107
+ ] });
108
+ if (!folds) {
109
+ return /* @__PURE__ */ jsxs(
110
+ "div",
111
+ {
112
+ "data-slot": "shell-nav-section",
113
+ className: cn("flex flex-col gap-1", group.label !== void 0 && "pl-1"),
114
+ children: [
115
+ group.label !== void 0 && /* @__PURE__ */ jsx("p", { id: labelId, className: "mb-0.5 text-xs font-semibold uppercase tracking-widest text-ink-soft", children: group.label }),
116
+ content
117
+ ]
118
+ }
119
+ );
120
+ }
121
+ return /* @__PURE__ */ jsxs("div", { "data-slot": "shell-nav-section", "data-state": open ? "open" : "closed", className: "flex flex-col", children: [
122
+ /* @__PURE__ */ jsx(
123
+ NavGroupHeader,
124
+ {
125
+ label: group.label,
126
+ labelId,
127
+ regionId,
128
+ open,
129
+ onToggle: () => setOpen(!open),
130
+ href: group.href,
131
+ linkComponent: row.linkComponent,
132
+ current: landing,
133
+ labels
134
+ }
135
+ ),
136
+ /* @__PURE__ */ jsx(NavCollapse, { open, id: regionId, children: content })
137
+ ] });
101
138
  }
102
- function ShellNavRow({ item, state, row }) {
139
+ function ShellNavRow({
140
+ item,
141
+ state,
142
+ row,
143
+ nested
144
+ }) {
103
145
  const Icon = item.icon;
104
146
  const trailing = typeof item.trailing === "function" ? item.trailing(state) : item.trailing;
105
147
  return /* @__PURE__ */ jsx(
@@ -116,7 +158,7 @@ function ShellNavRow({ item, state, row }) {
116
158
  className: cn(
117
159
  ROW,
118
160
  NAV_FOCUS,
119
- state.active ? "font-semibold text-on-primary" : "font-medium text-ink hover:bg-hover",
161
+ state.active ? "font-semibold text-on-primary" : cn("font-medium hover:bg-hover", nested ? "text-ink-mid" : "text-ink"),
120
162
  item.disabled && "cursor-not-allowed opacity-50 hover:bg-transparent"
121
163
  ),
122
164
  children: row.renderItem ? row.renderItem(item, state) : /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/shell-nav.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { NavBadge } from \"./nav-badge\";\nimport { labelTitle, NavLink, type NavLinkCallbacks } from \"./nav-link\";\nimport { resolveActiveNavItem, type NavGroup, type NavItem, type NavItemState } from \"./nav-model\";\nimport { usePendingNavigation, useSlideMarker } from \"./use-shell-nav\";\n\nexport interface ShellNavLabels {\n /** Accessible name of the nav holding `groups`. Default `\"Main\"`. */\n primary?: string;\n /** Accessible name of the nav holding `footerGroups`. Default `\"Account\"`. */\n footer?: string;\n /** Spoken text of a numeric badge, given the real count. Default `\"{n} new\"`. */\n badge?: (count: number) => string;\n}\n\nexport interface ShellNavProps\n extends Omit<React.ComponentProps<\"aside\">, \"children\">,\n NavLinkCallbacks {\n groups: readonly NavGroup[];\n /** Pinned to the bottom of the rail (profile, notifications, settings). */\n footerGroups?: readonly NavGroup[];\n /** The router's current path — `usePathname()` in Next. */\n pathname: string;\n /** Your router's link (`next/link`); defaults to a plain `<a>`. */\n linkComponent?: React.ElementType;\n /** Above the groups — the brand lock-up. */\n header?: React.ReactNode;\n /** Below the footer groups — the signed-in account, a sign-out. */\n footer?: React.ReactNode;\n /** Replaces a row's content (icon, label, badge); the link, `aria-current` and the marker stay the kit's. */\n renderItem?: (item: NavItem, state: NavItemState) => React.ReactNode;\n labels?: ShellNavLabels;\n}\n\n// The ring is the solid token, never a tint — at 50% it composites under the 3:1\n// non-text floor. The offset keeps it legible around the marked row, whose fill\n// is that same hue.\nconst NAV_FOCUS =\n \"outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-secondary\";\nconst ROW = \"flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors\";\n\n/**\n * The app's left rail: data-driven groups, a footer block pinned to the bottom,\n * one sliding marker per group, and an optimistic mark on click so the row\n * answers the click rather than the router's round-trip.\n *\n * Exactly one row across the rail is current — the strongest claim on the path\n * (see {@link resolveActiveNavItem}) — so overlapping `prefix` items are safe.\n * Visibility (roles, flags) is the caller's: pass only the items to show.\n */\nexport function ShellNav({\n groups,\n footerGroups = [],\n pathname,\n linkComponent,\n header,\n footer,\n renderItem,\n onItemIntent,\n onNavigate,\n labels = {},\n className,\n ...props\n}: ShellNavProps) {\n const [pendingId, markPending] = usePendingNavigation(pathname);\n const all = [...groups, ...footerGroups].flatMap((g) => g.items);\n const currentId =\n (pendingId !== null && all.some((i) => i.id === pendingId) ? pendingId : undefined) ??\n resolveActiveNavItem(all, pathname)?.id;\n const section = (group: NavGroup) => (\n <ShellNavSection\n key={group.id}\n group={group}\n currentId={currentId}\n pendingId={pendingId}\n row={{ linkComponent, markPending, onItemIntent, onNavigate, renderItem, badgeLabel: labels.badge }}\n />\n );\n return (\n <aside\n data-slot=\"shell-nav\"\n className={cn(\n \"flex h-full w-(--shell-rail-w) flex-col gap-7 overflow-y-auto border-r border-border bg-secondary px-4.5 pb-5 pt-6\",\n className,\n )}\n {...props}\n >\n {header}\n <nav aria-label={labels.primary ?? \"Main\"} className=\"flex flex-col gap-4.5\">\n {groups.map(section)}\n </nav>\n <div className=\"flex-1\" />\n {footerGroups.length > 0 && (\n <nav aria-label={labels.footer ?? \"Account\"} className=\"flex flex-col gap-4.5\">\n {footerGroups.map(section)}\n </nav>\n )}\n {footer}\n </aside>\n );\n}\n\ninterface RowContext extends NavLinkCallbacks {\n linkComponent: React.ElementType | undefined;\n markPending: (id: string) => void;\n renderItem: ShellNavProps[\"renderItem\"];\n badgeLabel: ((count: number) => string) | undefined;\n}\n\n// One marker node per SECTION, mounted once and moved — not one per rail: a\n// marker travelling between groups would cross headings that have nothing to do\n// with either row. Crossing groups unmounts one marker and mounts another (a\n// fade). `isolate` is load-bearing: the marker sits on a negative z-index and\n// without its own stacking context would land behind the rail's background.\nfunction ShellNavSection({\n group,\n currentId,\n pendingId,\n row,\n}: {\n group: NavGroup;\n currentId: string | undefined;\n pendingId: string | null;\n row: RowContext;\n}) {\n const root = React.useRef<HTMLDivElement>(null);\n const labelId = React.useId();\n const at = group.items.some((i) => i.id === currentId) ? (currentId ?? null) : null;\n useSlideMarker(root, at);\n return (\n <div\n ref={root}\n data-slot=\"shell-nav-section\"\n className={cn(\"relative isolate flex flex-col gap-1\", group.label !== undefined && \"pl-1\")}\n >\n {/* First in DOM order: the pre-hydration fill in motion.css reaches the marked\n row through a sibling combinator, which only looks forward. */}\n {at !== null && (\n <span\n data-slot=\"shell-nav-marker\"\n aria-hidden\n className=\"pointer-events-none absolute left-0 top-0 -z-10 rounded-lg bg-primary\"\n />\n )}\n {group.label !== undefined && (\n <p id={labelId} className=\"mb-0.5 text-xs font-semibold uppercase tracking-widest text-ink-soft\">\n {group.label}\n </p>\n )}\n <ul\n aria-labelledby={group.label !== undefined ? labelId : undefined}\n className=\"flex flex-col gap-1\"\n >\n {group.items.map((item) => (\n <li key={item.id} className=\"flex flex-col\">\n <ShellNavRow\n item={item}\n state={{ active: item.id === currentId, pending: item.id === pendingId }}\n row={row}\n />\n </li>\n ))}\n </ul>\n </div>\n );\n}\n\nfunction ShellNavRow({ item, state, row }: { item: NavItem; state: NavItemState; row: RowContext }) {\n const Icon = item.icon;\n const trailing = typeof item.trailing === \"function\" ? item.trailing(state) : item.trailing;\n return (\n <NavLink\n item={item}\n current={state.active}\n pending={state.pending}\n linkComponent={row.linkComponent}\n markPending={row.markPending}\n onItemIntent={row.onItemIntent}\n onNavigate={row.onNavigate}\n title={labelTitle(item.label)}\n className={cn(\n ROW,\n NAV_FOCUS,\n state.active ? \"font-semibold text-on-primary\" : \"font-medium text-ink hover:bg-hover\",\n item.disabled && \"cursor-not-allowed opacity-50 hover:bg-transparent\",\n )}\n >\n {row.renderItem ? (\n row.renderItem(item, state)\n ) : (\n <>\n {Icon && <Icon className=\"size-4.5 shrink-0\" />}\n {/* `min-w-0`: without it the label refuses to shrink below its own text and\n the rail widens to the longest translation instead of truncating. */}\n <span className=\"min-w-0 flex-1 truncate\">{item.label}</span>\n {typeof item.badge === \"number\" ? (\n <NavBadge\n count={item.badge}\n active={state.active}\n {...(row.badgeLabel ? { label: row.badgeLabel } : {})}\n />\n ) : (\n item.badge\n )}\n {trailing}\n </>\n )}\n </NavLink>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/shell-nav.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { NavBadge } from \"./nav-badge\";\nimport { labelTitle, NavLink, type NavLinkCallbacks } from \"./nav-link\";\nimport { NavCollapse, NavGroupHeader, useNavGroupOpen, type NavCollapseLabels } from \"./nav-collapse\";\nimport {\n navItemMatch,\n resolveActiveNavItem,\n type NavGroup,\n type NavItem,\n type NavItemState,\n} from \"./nav-model\";\nimport { usePendingNavigation, useSlideMarker } from \"./use-shell-nav\";\n\nexport interface ShellNavLabels extends NavCollapseLabels {\n /** Accessible name of the nav holding `groups`. Default `\"Main\"`. */\n primary?: string;\n /** Accessible name of the nav holding `footerGroups`. Default `\"Account\"`. */\n footer?: string;\n /** Spoken text of a numeric badge, given the real count. Default `\"{n} new\"`. */\n badge?: (count: number) => string;\n}\n\nexport interface ShellNavProps\n extends Omit<React.ComponentProps<\"aside\">, \"children\">,\n NavLinkCallbacks {\n groups: readonly NavGroup[];\n /** Pinned to the bottom of the rail (profile, notifications, settings). */\n footerGroups?: readonly NavGroup[];\n /** The router's current path — `usePathname()` in Next. */\n pathname: string;\n /** Your router's link (`next/link`); defaults to a plain `<a>`. */\n linkComponent?: React.ElementType;\n /** Above the groups — the brand lock-up. */\n header?: React.ReactNode;\n /** Below the footer groups — the signed-in account, a sign-out. */\n footer?: React.ReactNode;\n /** Replaces a row's content (icon, label, badge); the link, `aria-current` and the marker stay the kit's. */\n renderItem?: (item: NavItem, state: NavItemState) => React.ReactNode;\n labels?: ShellNavLabels;\n /** Makes every labelled group collapsible; a group's own `collapsible` wins. Default `false`. */\n collapsible?: boolean;\n}\n\n// The ring is the solid token, never a tint — at 50% it composites under the 3:1\n// non-text floor. The offset keeps it legible around the marked row, whose fill\n// is that same hue.\nconst NAV_FOCUS =\n \"outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-secondary\";\nconst ROW = \"flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors\";\n\n/**\n * The app's left rail: data-driven groups, a footer block pinned to the bottom,\n * one sliding marker per group, and an optimistic mark on click so the row\n * answers the click rather than the router's round-trip.\n *\n * Exactly one row across the rail is current — the strongest claim on the path\n * (see {@link resolveActiveNavItem}) — so overlapping `prefix` items are safe.\n * Visibility (roles, flags) is the caller's: pass only the items to show.\n */\nexport function ShellNav({\n groups,\n footerGroups = [],\n pathname,\n linkComponent,\n header,\n footer,\n renderItem,\n onItemIntent,\n onNavigate,\n labels = {},\n collapsible = false,\n className,\n ...props\n}: ShellNavProps) {\n const [pendingId, markPending] = usePendingNavigation(pathname);\n const all = [...groups, ...footerGroups].flatMap((g) => g.items);\n const currentId =\n (pendingId !== null && all.some((i) => i.id === pendingId) ? pendingId : undefined) ??\n resolveActiveNavItem(all, pathname)?.id;\n const section = (group: NavGroup) => (\n <ShellNavSection\n key={group.id}\n group={group}\n currentId={currentId}\n pendingId={pendingId}\n pathname={pathname}\n labels={labels}\n collapsible={collapsible}\n row={{ linkComponent, markPending, onItemIntent, onNavigate, renderItem, badgeLabel: labels.badge }}\n />\n );\n return (\n <aside\n data-slot=\"shell-nav\"\n className={cn(\n \"flex h-full w-(--shell-rail-w) flex-col gap-7 overflow-y-auto border-r border-border bg-secondary px-4.5 pb-5 pt-6\",\n className,\n )}\n {...props}\n >\n {header}\n <nav aria-label={labels.primary ?? \"Main\"} className=\"flex flex-col gap-4.5\">\n {groups.map(section)}\n </nav>\n <div className=\"flex-1\" />\n {footerGroups.length > 0 && (\n <nav aria-label={labels.footer ?? \"Account\"} className=\"flex flex-col gap-4.5\">\n {footerGroups.map(section)}\n </nav>\n )}\n {footer}\n </aside>\n );\n}\n\ninterface RowContext extends NavLinkCallbacks {\n linkComponent: React.ElementType | undefined;\n markPending: (id: string) => void;\n renderItem: ShellNavProps[\"renderItem\"];\n badgeLabel: ((count: number) => string) | undefined;\n}\n\n// One marker node per SECTION, mounted once and moved — not one per rail: a\n// marker travelling between groups would cross headings that have nothing to do\n// with either row. Crossing groups unmounts one marker and mounts another (a\n// fade). `isolate` is load-bearing: the marker sits on a negative z-index and\n// without its own stacking context would land behind the rail's background.\n// The marker lives in the BODY, below the header, so a folding section clips it\n// together with its rows instead of leaving it painted over the next group.\nfunction ShellNavSection({\n group,\n currentId,\n pendingId,\n pathname,\n row,\n labels,\n collapsible,\n}: {\n group: NavGroup;\n currentId: string | undefined;\n pendingId: string | null;\n pathname: string;\n row: RowContext;\n labels: ShellNavLabels;\n collapsible: boolean;\n}) {\n const body = React.useRef<HTMLDivElement>(null);\n const labelId = React.useId();\n const regionId = React.useId();\n const at = group.items.some((i) => i.id === currentId) ? (currentId ?? null) : null;\n const [open, setOpen] = useNavGroupOpen(group, at !== null);\n useSlideMarker(body, at);\n const folds = (group.collapsible ?? collapsible) && group.label !== undefined;\n const landing =\n group.href !== undefined &&\n navItemMatch({ id: group.id, href: group.href, label: \"\", match: \"exact\" }, pathname) !== null;\n const content = (\n <div ref={body} className={cn(\"relative isolate\", folds && \"pt-1\")}>\n {/* First in DOM order: the pre-hydration fill in motion.css reaches the marked\n row through a sibling combinator, which only looks forward. */}\n {at !== null && (\n <span\n data-slot=\"shell-nav-marker\"\n aria-hidden\n className=\"pointer-events-none absolute left-0 top-0 -z-10 rounded-lg bg-primary\"\n />\n )}\n <ul\n aria-labelledby={group.label !== undefined ? labelId : undefined}\n className={cn(\"flex flex-col gap-1\", folds && \"pl-3\")}\n >\n {group.items.map((item) => (\n <li key={item.id} className=\"flex flex-col\">\n <ShellNavRow\n item={item}\n nested={folds}\n state={{ active: item.id === currentId, pending: item.id === pendingId }}\n row={row}\n />\n </li>\n ))}\n </ul>\n </div>\n );\n if (!folds) {\n return (\n <div\n data-slot=\"shell-nav-section\"\n className={cn(\"flex flex-col gap-1\", group.label !== undefined && \"pl-1\")}\n >\n {group.label !== undefined && (\n <p id={labelId} className=\"mb-0.5 text-xs font-semibold uppercase tracking-widest text-ink-soft\">\n {group.label}\n </p>\n )}\n {content}\n </div>\n );\n }\n return (\n <div data-slot=\"shell-nav-section\" data-state={open ? \"open\" : \"closed\"} className=\"flex flex-col\">\n <NavGroupHeader\n label={group.label}\n labelId={labelId}\n regionId={regionId}\n open={open}\n onToggle={() => setOpen(!open)}\n href={group.href}\n linkComponent={row.linkComponent}\n current={landing}\n labels={labels}\n />\n <NavCollapse open={open} id={regionId}>\n {content}\n </NavCollapse>\n </div>\n );\n}\n\nfunction ShellNavRow({\n item,\n state,\n row,\n nested,\n}: {\n item: NavItem;\n state: NavItemState;\n row: RowContext;\n nested: boolean;\n}) {\n const Icon = item.icon;\n const trailing = typeof item.trailing === \"function\" ? item.trailing(state) : item.trailing;\n return (\n <NavLink\n item={item}\n current={state.active}\n pending={state.pending}\n linkComponent={row.linkComponent}\n markPending={row.markPending}\n onItemIntent={row.onItemIntent}\n onNavigate={row.onNavigate}\n title={labelTitle(item.label)}\n className={cn(\n ROW,\n NAV_FOCUS,\n state.active\n ? \"font-semibold text-on-primary\"\n : cn(\"font-medium hover:bg-hover\", nested ? \"text-ink-mid\" : \"text-ink\"),\n item.disabled && \"cursor-not-allowed opacity-50 hover:bg-transparent\",\n )}\n >\n {row.renderItem ? (\n row.renderItem(item, state)\n ) : (\n <>\n {Icon && <Icon className=\"size-4.5 shrink-0\" />}\n {/* `min-w-0`: without it the label refuses to shrink below its own text and\n the rail widens to the longest translation instead of truncating. */}\n <span className=\"min-w-0 flex-1 truncate\">{item.label}</span>\n {typeof item.badge === \"number\" ? (\n <NavBadge\n count={item.badge}\n active={state.active}\n {...(row.badgeLabel ? { label: row.badgeLabel } : {})}\n />\n ) : (\n item.badge\n )}\n {trailing}\n </>\n )}\n </NavLink>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,53 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "../lib/cn.js";
4
+ import {
5
+ SMART_HEADER_BAR,
6
+ SMART_HEADER_BASE,
7
+ SMART_HEADER_CONTEXT,
8
+ SMART_HEADER_TOOLBAR
9
+ } from "../generated/smart-header.js";
10
+ import { TopBar } from "./top-bar.js";
11
+ function SmartHeader({
12
+ state = "default",
13
+ start,
14
+ end,
15
+ collapsed,
16
+ banner,
17
+ toolbar,
18
+ context,
19
+ palette,
20
+ className,
21
+ ...props
22
+ }) {
23
+ return /* @__PURE__ */ jsxs(
24
+ "div",
25
+ {
26
+ "data-slot": "smart-header",
27
+ "data-state": state,
28
+ className: cn(SMART_HEADER_BASE, className),
29
+ ...props,
30
+ children: [
31
+ state === "banner" && banner,
32
+ /* @__PURE__ */ jsx(
33
+ TopBar,
34
+ {
35
+ className: SMART_HEADER_BAR,
36
+ start: /* @__PURE__ */ jsxs(Fragment, { children: [
37
+ start,
38
+ state === "collapsed" && collapsed
39
+ ] }),
40
+ end
41
+ }
42
+ ),
43
+ state === "default" && toolbar != null && /* @__PURE__ */ jsx("div", { "data-slot": "smart-header-toolbar", className: SMART_HEADER_TOOLBAR, children: toolbar }),
44
+ state === "default" && context != null && /* @__PURE__ */ jsx("div", { "data-slot": "smart-header-context", className: SMART_HEADER_CONTEXT, children: context }),
45
+ state === "search" && palette != null && /* @__PURE__ */ jsx("div", { "data-slot": "smart-header-palette", children: palette })
46
+ ]
47
+ }
48
+ );
49
+ }
50
+ export {
51
+ SmartHeader
52
+ };
53
+ //# sourceMappingURL=smart-header.js.map