@devalok/shilp-sutra 0.38.0 → 0.39.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 (212) hide show
  1. package/MIGRATION.md +118 -0
  2. package/dist/_chunks/badge-group.js +4 -4
  3. package/dist/_chunks/badge-group.js.map +1 -1
  4. package/dist/_chunks/chat.js +14 -14
  5. package/dist/_chunks/chat.js.map +1 -1
  6. package/dist/_chunks/date-picker.js +9 -9
  7. package/dist/_chunks/date-picker.js.map +1 -1
  8. package/dist/_chunks/document-preview.js +2 -2
  9. package/dist/_chunks/document-preview.js.map +1 -1
  10. package/dist/_chunks/file-preview.js +11 -11
  11. package/dist/_chunks/file-preview.js.map +1 -1
  12. package/dist/_chunks/image-preview.js +2 -2
  13. package/dist/_chunks/image-preview.js.map +1 -1
  14. package/dist/_chunks/mention-suggestion.js +5 -5
  15. package/dist/_chunks/mention-suggestion.js.map +1 -1
  16. package/dist/_chunks/popover.js +3 -3
  17. package/dist/_chunks/popover.js.map +1 -1
  18. package/dist/_chunks/rich-chat-input.js +33 -33
  19. package/dist/_chunks/rich-chat-input.js.map +1 -1
  20. package/dist/_chunks/shared.js +5 -5
  21. package/dist/_chunks/shared.js.map +1 -1
  22. package/dist/_chunks/text.js +2 -2
  23. package/dist/_chunks/text.js.map +1 -1
  24. package/dist/_chunks/tree-view.js +1 -1
  25. package/dist/_chunks/tree-view.js.map +1 -1
  26. package/dist/_chunks/use-calendar.js +5 -5
  27. package/dist/_chunks/use-calendar.js.map +1 -1
  28. package/dist/ai/command-bar.js +10 -10
  29. package/dist/ai/command-bar.js.map +1 -1
  30. package/dist/ai/conversation.js +4 -4
  31. package/dist/ai/conversation.js.map +1 -1
  32. package/dist/composed/activity-feed.js +5 -5
  33. package/dist/composed/activity-feed.js.map +1 -1
  34. package/dist/composed/avatar-group.js +4 -4
  35. package/dist/composed/avatar-group.js.map +1 -1
  36. package/dist/composed/bulk-action-bar.js +1 -1
  37. package/dist/composed/bulk-action-bar.js.map +1 -1
  38. package/dist/composed/command-palette.js +5 -5
  39. package/dist/composed/command-palette.js.map +1 -1
  40. package/dist/composed/content-card.js +1 -1
  41. package/dist/composed/content-card.js.map +1 -1
  42. package/dist/composed/emoji-picker.js +4 -4
  43. package/dist/composed/emoji-picker.js.map +1 -1
  44. package/dist/composed/empty-state.js +1 -1
  45. package/dist/composed/empty-state.js.map +1 -1
  46. package/dist/composed/error-boundary.js +3 -3
  47. package/dist/composed/error-boundary.js.map +1 -1
  48. package/dist/composed/filter-bar.js +1 -1
  49. package/dist/composed/filter-bar.js.map +1 -1
  50. package/dist/composed/inline-edit.js +1 -1
  51. package/dist/composed/inline-edit.js.map +1 -1
  52. package/dist/composed/loading-skeleton.js +9 -9
  53. package/dist/composed/loading-skeleton.js.map +1 -1
  54. package/dist/composed/markdown-viewer.js +3 -3
  55. package/dist/composed/markdown-viewer.js.map +1 -1
  56. package/dist/composed/multi-select-popover.js +1 -1
  57. package/dist/composed/multi-select-popover.js.map +1 -1
  58. package/dist/composed/page-header.js +1 -1
  59. package/dist/composed/page-header.js.map +1 -1
  60. package/dist/composed/page-skeletons.js +15 -15
  61. package/dist/composed/page-skeletons.js.map +1 -1
  62. package/dist/composed/priority-indicator.js +3 -3
  63. package/dist/composed/priority-indicator.js.map +1 -1
  64. package/dist/composed/rich-text-editor.js +11 -11
  65. package/dist/composed/rich-text-editor.js.map +1 -1
  66. package/dist/composed/schedule-view.js +3 -3
  67. package/dist/composed/schedule-view.js.map +1 -1
  68. package/dist/composed/status-badge.js +3 -3
  69. package/dist/composed/status-badge.js.map +1 -1
  70. package/dist/shell/bottom-navbar.js +6 -6
  71. package/dist/shell/bottom-navbar.js.map +1 -1
  72. package/dist/shell/notification-center.js +8 -8
  73. package/dist/shell/notification-center.js.map +1 -1
  74. package/dist/shell/notification-preferences.js +1 -1
  75. package/dist/shell/notification-preferences.js.map +1 -1
  76. package/dist/shell/sidebar.js +6 -6
  77. package/dist/shell/sidebar.js.map +1 -1
  78. package/dist/shell/top-bar.js +4 -4
  79. package/dist/shell/top-bar.js.map +1 -1
  80. package/dist/tokens/semantic.css +82 -9
  81. package/dist/ui/accordion.js +1 -1
  82. package/dist/ui/accordion.js.map +1 -1
  83. package/dist/ui/alert-dialog.js +3 -3
  84. package/dist/ui/alert-dialog.js.map +1 -1
  85. package/dist/ui/alert.js +2 -2
  86. package/dist/ui/alert.js.map +1 -1
  87. package/dist/ui/autocomplete.js +2 -2
  88. package/dist/ui/autocomplete.js.map +1 -1
  89. package/dist/ui/avatar.js +11 -11
  90. package/dist/ui/avatar.js.map +1 -1
  91. package/dist/ui/badge-indicator.js +1 -1
  92. package/dist/ui/badge-indicator.js.map +1 -1
  93. package/dist/ui/banner.js +1 -1
  94. package/dist/ui/banner.js.map +1 -1
  95. package/dist/ui/breadcrumb.js +1 -1
  96. package/dist/ui/breadcrumb.js.map +1 -1
  97. package/dist/ui/button.d.ts +2 -2
  98. package/dist/ui/button.js +14 -14
  99. package/dist/ui/button.js.map +1 -1
  100. package/dist/ui/card.js +5 -5
  101. package/dist/ui/card.js.map +1 -1
  102. package/dist/ui/charts/index.js +5 -5
  103. package/dist/ui/charts/index.js.map +1 -1
  104. package/dist/ui/checkbox.js +1 -1
  105. package/dist/ui/checkbox.js.map +1 -1
  106. package/dist/ui/code.js +2 -2
  107. package/dist/ui/code.js.map +1 -1
  108. package/dist/ui/color-input.js +10 -10
  109. package/dist/ui/color-input.js.map +1 -1
  110. package/dist/ui/color-swatch.js +3 -3
  111. package/dist/ui/color-swatch.js.map +1 -1
  112. package/dist/ui/combobox.js +5 -5
  113. package/dist/ui/combobox.js.map +1 -1
  114. package/dist/ui/context-menu.js +6 -6
  115. package/dist/ui/context-menu.js.map +1 -1
  116. package/dist/ui/data-table-body.js +1 -1
  117. package/dist/ui/data-table-body.js.map +1 -1
  118. package/dist/ui/data-table-bulk-actions.js +2 -2
  119. package/dist/ui/data-table-bulk-actions.js.map +1 -1
  120. package/dist/ui/data-table-card.js +2 -2
  121. package/dist/ui/data-table-card.js.map +1 -1
  122. package/dist/ui/data-table-header.js +2 -2
  123. package/dist/ui/data-table-header.js.map +1 -1
  124. package/dist/ui/data-table-pagination.js +3 -3
  125. package/dist/ui/data-table-pagination.js.map +1 -1
  126. package/dist/ui/data-table-toolbar.js +1 -1
  127. package/dist/ui/data-table-toolbar.js.map +1 -1
  128. package/dist/ui/data-table.js +1 -1
  129. package/dist/ui/data-table.js.map +1 -1
  130. package/dist/ui/devalok-grain.d.ts +1 -1
  131. package/dist/ui/devalok-grain.js.map +1 -1
  132. package/dist/ui/dialog.js +2 -2
  133. package/dist/ui/dialog.js.map +1 -1
  134. package/dist/ui/dropdown-menu.js +6 -6
  135. package/dist/ui/dropdown-menu.js.map +1 -1
  136. package/dist/ui/file-upload.js +4 -4
  137. package/dist/ui/file-upload.js.map +1 -1
  138. package/dist/ui/hover-card.js +1 -1
  139. package/dist/ui/hover-card.js.map +1 -1
  140. package/dist/ui/icon-button.js +1 -1
  141. package/dist/ui/icon-button.js.map +1 -1
  142. package/dist/ui/input-otp.js +1 -1
  143. package/dist/ui/input-otp.js.map +1 -1
  144. package/dist/ui/input.js +2 -2
  145. package/dist/ui/input.js.map +1 -1
  146. package/dist/ui/link.js +1 -1
  147. package/dist/ui/link.js.map +1 -1
  148. package/dist/ui/menubar.js +8 -8
  149. package/dist/ui/menubar.js.map +1 -1
  150. package/dist/ui/navigation-menu.js +3 -3
  151. package/dist/ui/navigation-menu.js.map +1 -1
  152. package/dist/ui/number-input.js +3 -3
  153. package/dist/ui/number-input.js.map +1 -1
  154. package/dist/ui/pagination.js +1 -1
  155. package/dist/ui/pagination.js.map +1 -1
  156. package/dist/ui/progress.js +1 -1
  157. package/dist/ui/progress.js.map +1 -1
  158. package/dist/ui/radio.js +1 -1
  159. package/dist/ui/radio.js.map +1 -1
  160. package/dist/ui/segmented-control.js +3 -3
  161. package/dist/ui/segmented-control.js.map +1 -1
  162. package/dist/ui/select.js +3 -3
  163. package/dist/ui/select.js.map +1 -1
  164. package/dist/ui/sheet.js +2 -2
  165. package/dist/ui/sheet.js.map +1 -1
  166. package/dist/ui/sidebar.js +11 -11
  167. package/dist/ui/sidebar.js.map +1 -1
  168. package/dist/ui/skeleton.js +9 -9
  169. package/dist/ui/skeleton.js.map +1 -1
  170. package/dist/ui/slider.js +2 -2
  171. package/dist/ui/slider.js.map +1 -1
  172. package/dist/ui/split-button.js +7 -7
  173. package/dist/ui/split-button.js.map +1 -1
  174. package/dist/ui/stat-card.js +7 -7
  175. package/dist/ui/stat-card.js.map +1 -1
  176. package/dist/ui/status-dot.js +2 -2
  177. package/dist/ui/status-dot.js.map +1 -1
  178. package/dist/ui/stepper.js +2 -2
  179. package/dist/ui/stepper.js.map +1 -1
  180. package/dist/ui/switch.js +2 -2
  181. package/dist/ui/switch.js.map +1 -1
  182. package/dist/ui/tabs.js +3 -3
  183. package/dist/ui/tabs.js.map +1 -1
  184. package/dist/ui/textarea.js +1 -1
  185. package/dist/ui/textarea.js.map +1 -1
  186. package/dist/ui/toast.js +8 -8
  187. package/dist/ui/toast.js.map +1 -1
  188. package/dist/ui/toggle.js +1 -1
  189. package/dist/ui/toggle.js.map +1 -1
  190. package/dist/ui/tooltip.js +1 -1
  191. package/dist/ui/tooltip.js.map +1 -1
  192. package/docs/components/_header.md +90 -1
  193. package/docs/recipes/customize-brand.md +85 -4
  194. package/docs/recipes/index.md +1 -1
  195. package/llms-full.txt +91 -2
  196. package/llms.txt +9 -1
  197. package/package.json +2 -1
  198. package/skill/README.md +99 -0
  199. package/skill/SKILL.md +144 -0
  200. package/skill/install.sh +59 -0
  201. package/skill/references/components-full.md +6997 -0
  202. package/skill/references/components.md +673 -0
  203. package/skill/references/customize-brand.md +299 -0
  204. package/skill/references/server-components.md +211 -0
  205. package/skill/references/setup-astro.md +180 -0
  206. package/skill/references/setup-next-app-router.md +232 -0
  207. package/skill/references/setup-next-pages.md +125 -0
  208. package/skill/references/setup-remix.md +173 -0
  209. package/skill/references/setup-tanstack-start.md +145 -0
  210. package/skill/references/setup-vite.md +172 -0
  211. package/skill/references/troubleshoot.md +219 -0
  212. /package/{LICENSE → skill/LICENSE} +0 -0
package/dist/ui/tabs.js CHANGED
@@ -41,7 +41,7 @@ var g = c.createContext({
41
41
  variants: {
42
42
  variant: {
43
43
  line: "border-b border-surface-border-strong w-full gap-0",
44
- contained: "bg-surface-raised p-ds-02 rounded-ds-lg gap-ds-02"
44
+ contained: "bg-surface-raised p-ds-02 rounded-surface gap-ds-02"
45
45
  },
46
46
  size: {
47
47
  sm: "h-8",
@@ -72,7 +72,7 @@ var g = c.createContext({
72
72
  variant: {
73
73
  line: ["-mb-px", "text-surface-fg-muted hover:text-surface-fg"],
74
74
  contained: [
75
- "rounded-ds-md",
75
+ "rounded-control",
76
76
  "text-surface-fg-muted hover:text-surface-fg",
77
77
  "data-[state=active]:text-surface-fg"
78
78
  ]
@@ -131,7 +131,7 @@ var b = {
131
131
  children: [
132
132
  y === "contained" && w && /* @__PURE__ */ l(f.span, {
133
133
  layoutId: `${d.layoutId}-contained`,
134
- className: "absolute inset-0 rounded-ds-md bg-surface-overlay shadow-raised",
134
+ className: "absolute inset-0 rounded-control bg-surface-overlay shadow-raised",
135
135
  transition: i.smooth
136
136
  }),
137
137
  /* @__PURE__ */ l("span", {
@@ -1 +1 @@
1
- {"version":3,"file":"tabs.js","names":[],"sources":["../../src/ui/tabs.tsx"],"sourcesContent":["'use client'\n\nimport * as TabsPrimitive from '@primitives/react-tabs'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { LayoutGroup,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\n\n/* ── Active-value context (drives layoutId indicator) ────── */\ntype TabsOrientation = 'horizontal' | 'vertical'\nconst TabsValueContext = React.createContext<string | undefined>(undefined)\nconst TabsOrientationContext = React.createContext<TabsOrientation>('horizontal')\n\n/**\n * Tabs compound component — accessible tabbed navigation with keyboard support and two visual\n * variants (line underline and contained pill styles).\n *\n * **Parts (in composition order):**\n * - `Tabs` — manages active tab state (this root; takes `defaultValue`, `value`, `onValueChange`)\n * - `TabsList` — tab bar container (takes `variant=\"line\"|\"contained\"`, default `\"line\"`)\n * - `TabsTrigger` — individual tab button (requires `value`; inherits `variant` from TabsList via context)\n * - `TabsContent` — the panel shown when its tab is active (requires `value` matching a TabsTrigger)\n *\n * **Critical behavior:** `variant` set on `TabsList` propagates automatically via React context to\n * all `TabsTrigger` children. You do NOT need to repeat `variant` on each trigger — but you CAN\n * override it per-trigger if needed.\n *\n * The active tab indicator animates between tabs using Framer Motion `layoutId`. Line variant\n * shows a sliding underline; contained variant shows a sliding pill background.\n *\n * @compound\n * @example\n * // Default line variant:\n * <Tabs defaultValue=\"overview\">\n * <TabsList>\n * <TabsTrigger value=\"overview\">Overview</TabsTrigger>\n * <TabsTrigger value=\"activity\">Activity</TabsTrigger>\n * <TabsTrigger value=\"settings\">Settings</TabsTrigger>\n * </TabsList>\n * <TabsContent value=\"overview\">Overview content</TabsContent>\n * <TabsContent value=\"activity\">Activity content</TabsContent>\n * <TabsContent value=\"settings\">Settings content</TabsContent>\n * </Tabs>\n *\n * @example\n * // Contained pill variant (controlled):\n * const [tab, setTab] = useState('members')\n * <Tabs value={tab} onValueChange={setTab}>\n * <TabsList variant=\"contained\">\n * <TabsTrigger value=\"members\">Members</TabsTrigger>\n * <TabsTrigger value=\"roles\">Roles</TabsTrigger>\n * </TabsList>\n * <TabsContent value=\"members\">Members list here.</TabsContent>\n * <TabsContent value=\"roles\">Roles list here.</TabsContent>\n * </Tabs>\n */\nconst Tabs = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.Root>\n>(({ value: valueProp, defaultValue, onValueChange, orientation, ...props }, ref) => {\n // Track the active value so TabsTrigger can conditionally render the motion indicator.\n // For controlled usage, mirror the prop; for uncontrolled, manage internal state.\n const [activeValue, setActiveValue] = React.useState(valueProp ?? defaultValue ?? '')\n\n // Sync controlled value\n React.useEffect(() => {\n if (valueProp !== undefined) setActiveValue(valueProp)\n }, [valueProp])\n\n const handleValueChange = React.useCallback(\n (newValue: string) => {\n setActiveValue(newValue)\n onValueChange?.(newValue)\n },\n [onValueChange],\n )\n\n const resolvedOrientation: TabsOrientation = orientation ?? 'horizontal'\n\n return (\n <TabsValueContext.Provider value={activeValue}>\n <TabsOrientationContext.Provider value={resolvedOrientation}>\n <TabsPrimitive.Root\n ref={ref}\n value={valueProp}\n defaultValue={valueProp === undefined ? defaultValue : undefined}\n onValueChange={handleValueChange}\n orientation={orientation}\n {...props}\n className={cn(\n resolvedOrientation === 'vertical' && 'flex flex-row gap-ds-05',\n props.className,\n )}\n />\n </TabsOrientationContext.Provider>\n </TabsValueContext.Provider>\n )\n})\nTabs.displayName = 'Tabs'\n\n/** Props for the Tabs root (defaultValue, value, onValueChange, etc.). */\nexport type TabsProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.Root>\n\ntype TabsVariant = 'line' | 'contained'\ntype TabsSize = 'sm' | 'md' | 'lg'\ntype TabsColor = 'accent' | 'neutral'\n\ninterface TabsListContextValue {\n variant: TabsVariant\n size: TabsSize\n color: TabsColor\n layoutId: string\n}\n\nconst TabsListContext = React.createContext<TabsListContextValue>({\n variant: 'line',\n size: 'md',\n color: 'accent',\n layoutId: 'tab-indicator',\n})\n\nconst tabsListVariants = cva('inline-flex items-center', {\n variants: {\n variant: {\n line: 'border-b border-surface-border-strong w-full gap-0',\n contained:\n 'bg-surface-raised p-ds-02 rounded-ds-lg gap-ds-02',\n },\n size: {\n sm: 'h-8',\n md: 'h-10',\n lg: 'h-12',\n },\n orientation: {\n horizontal: '',\n vertical: 'flex-col',\n },\n },\n compoundVariants: [\n // Vertical line: left border instead of bottom, fixed width, auto height\n { variant: 'line', orientation: 'vertical', class: 'border-b-0 border-l border-surface-border-strong w-48 shrink-0 h-auto gap-0' },\n // Vertical contained: column layout, fixed width, auto height\n { variant: 'contained', orientation: 'vertical', class: 'h-auto w-48 shrink-0' },\n ],\n defaultVariants: { variant: 'line', size: 'md', orientation: 'horizontal' },\n})\n\nconst tabsTriggerVariants = cva(\n 'relative inline-flex items-center justify-center gap-ds-02 whitespace-nowrap font-sans font-medium transition-colors duration-100 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-1 disabled:pointer-events-none disabled:opacity-action-disabled',\n {\n variants: {\n variant: {\n line: [\n '-mb-px',\n 'text-surface-fg-muted hover:text-surface-fg',\n ],\n contained: [\n 'rounded-ds-md',\n 'text-surface-fg-muted hover:text-surface-fg',\n 'data-[state=active]:text-surface-fg',\n ],\n },\n size: {\n sm: 'px-ds-03 py-ds-02 text-ds-xs',\n md: 'px-ds-05 py-ds-03 text-ds-sm',\n lg: 'px-ds-06 py-ds-04 text-ds-md',\n },\n },\n defaultVariants: { variant: 'line', size: 'md' },\n },\n)\n\n/**\n * TabsList — container for tab triggers. Sets `variant` for all child TabsTriggers via context.\n *\n * **Compound structure — variant propagates automatically:**\n * ```tsx\n * <Tabs defaultValue=\"overview\">\n * <TabsList variant=\"contained\">\n * <TabsTrigger value=\"overview\">Overview</TabsTrigger>\n * <TabsTrigger value=\"activity\">Activity</TabsTrigger>\n * </TabsList>\n * <TabsContent value=\"overview\">Overview content</TabsContent>\n * <TabsContent value=\"activity\">Activity content</TabsContent>\n * </Tabs>\n * ```\n *\n * `variant` on `TabsList` flows to all `TabsTrigger` children via React context.\n * You do NOT need to repeat `variant` on each `TabsTrigger`.\n */\nexport interface TabsListProps\n extends React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>,\n VariantProps<typeof tabsListVariants> {\n /** Color axis — affects active tab indicator and text color. @default \"accent\" */\n color?: TabsColor\n}\n\nconst TabsList = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.List>,\n TabsListProps\n>(({ className, variant, size, color, ...props }, ref) => {\n const orientation = React.useContext(TabsOrientationContext)\n const resolvedVariant: TabsVariant = variant ?? 'line'\n const resolvedSize: TabsSize = size ?? 'md'\n const resolvedColor: TabsColor = color ?? 'accent'\n const layoutId = React.useId()\n const contextValue = React.useMemo(\n () => ({\n variant: resolvedVariant,\n size: resolvedSize,\n color: resolvedColor,\n layoutId: `tab-indicator-${layoutId}`,\n }),\n [resolvedVariant, resolvedSize, resolvedColor, layoutId],\n )\n return (\n <TabsListContext.Provider value={contextValue}>\n <LayoutGroup>\n <TabsPrimitive.List\n ref={ref}\n className={cn(tabsListVariants({ variant: resolvedVariant, size: resolvedSize, orientation }), className)}\n {...props}\n />\n </LayoutGroup>\n </TabsListContext.Provider>\n )\n})\nTabsList.displayName = TabsPrimitive.List.displayName\n\nexport interface TabsTriggerProps\n extends React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>,\n VariantProps<typeof tabsTriggerVariants> {}\n\n/* ── Color classes for active state ────── */\nconst lineActiveColorMap: Record<TabsColor, string> = {\n accent: 'data-[state=active]:text-accent-11',\n neutral: 'data-[state=active]:text-surface-fg',\n}\n\nconst lineIndicatorColorMap: Record<TabsColor, string> = {\n accent: 'bg-accent-9',\n neutral: 'bg-surface-fg',\n}\n\nconst TabsTrigger = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.Trigger>,\n TabsTriggerProps\n>(({ className, variant: variantProp, children, ...props }, ref) => {\n const listContext = React.useContext(TabsListContext)\n const activeValue = React.useContext(TabsValueContext)\n const orientation = React.useContext(TabsOrientationContext)\n const variant = variantProp ?? listContext.variant\n const size = listContext.size\n const color = listContext.color\n const isActive = props.value === activeValue\n const isVertical = orientation === 'vertical'\n\n return (\n <TabsPrimitive.Trigger\n ref={ref}\n className={cn(\n tabsTriggerVariants({ variant, size }),\n variant === 'line' && lineActiveColorMap[color],\n // Vertical: full width triggers, left margin instead of bottom\n isVertical && 'w-full justify-start',\n variant === 'line' && isVertical && '-ml-px -mb-0',\n className,\n )}\n {...props}\n >\n {/* Contained variant: sliding pill background */}\n {variant === 'contained' && isActive && (\n <motion.span\n layoutId={`${listContext.layoutId}-contained`}\n className=\"absolute inset-0 rounded-ds-md bg-surface-overlay shadow-raised\"\n transition={springs.smooth}\n />\n )}\n {/* Content sits above the indicator */}\n <span className=\"relative z-[1] inline-flex items-center gap-ds-02\">{children}</span>\n {/* Line variant: sliding indicator -- bottom underline (horizontal) or left bar (vertical) */}\n {variant === 'line' && isActive && (\n <motion.span\n layoutId={`${listContext.layoutId}-line`}\n className={cn(\n 'absolute',\n isVertical\n ? 'left-0 top-0 bottom-0 w-0.5'\n : 'bottom-0 left-0 right-0 h-0.5',\n lineIndicatorColorMap[color],\n )}\n transition={springs.smooth}\n />\n )}\n </TabsPrimitive.Trigger>\n )\n})\nTabsTrigger.displayName = TabsPrimitive.Trigger.displayName\n\nconst TabsContent = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>\n>(({ className, children, ...props }, ref) => {\n const orientation = React.useContext(TabsOrientationContext)\n const isVertical = orientation === 'vertical'\n\n return (\n <TabsPrimitive.Content\n ref={ref}\n className={cn(\n 'ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2',\n isVertical ? 'mt-0 flex-1 min-w-0' : 'mt-ds-05',\n className,\n )}\n {...props}\n >\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={tweens.fade}\n >\n {children}\n </motion.div>\n </TabsPrimitive.Content>\n )\n})\nTabsContent.displayName = TabsPrimitive.Content.displayName\n\n/** Props for TabsContent. The `value` prop must match a TabsTrigger's `value`. */\nexport type TabsContentProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>\n\nexport { Tabs, TabsContent,TabsList, TabsTrigger }\nexport type { TabsColor, TabsOrientation,TabsSize }\n"],"mappings":"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"}
1
+ {"version":3,"file":"tabs.js","names":[],"sources":["../../src/ui/tabs.tsx"],"sourcesContent":["'use client'\n\nimport * as TabsPrimitive from '@primitives/react-tabs'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { LayoutGroup,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\n\n/* ── Active-value context (drives layoutId indicator) ────── */\ntype TabsOrientation = 'horizontal' | 'vertical'\nconst TabsValueContext = React.createContext<string | undefined>(undefined)\nconst TabsOrientationContext = React.createContext<TabsOrientation>('horizontal')\n\n/**\n * Tabs compound component — accessible tabbed navigation with keyboard support and two visual\n * variants (line underline and contained pill styles).\n *\n * **Parts (in composition order):**\n * - `Tabs` — manages active tab state (this root; takes `defaultValue`, `value`, `onValueChange`)\n * - `TabsList` — tab bar container (takes `variant=\"line\"|\"contained\"`, default `\"line\"`)\n * - `TabsTrigger` — individual tab button (requires `value`; inherits `variant` from TabsList via context)\n * - `TabsContent` — the panel shown when its tab is active (requires `value` matching a TabsTrigger)\n *\n * **Critical behavior:** `variant` set on `TabsList` propagates automatically via React context to\n * all `TabsTrigger` children. You do NOT need to repeat `variant` on each trigger — but you CAN\n * override it per-trigger if needed.\n *\n * The active tab indicator animates between tabs using Framer Motion `layoutId`. Line variant\n * shows a sliding underline; contained variant shows a sliding pill background.\n *\n * @compound\n * @example\n * // Default line variant:\n * <Tabs defaultValue=\"overview\">\n * <TabsList>\n * <TabsTrigger value=\"overview\">Overview</TabsTrigger>\n * <TabsTrigger value=\"activity\">Activity</TabsTrigger>\n * <TabsTrigger value=\"settings\">Settings</TabsTrigger>\n * </TabsList>\n * <TabsContent value=\"overview\">Overview content</TabsContent>\n * <TabsContent value=\"activity\">Activity content</TabsContent>\n * <TabsContent value=\"settings\">Settings content</TabsContent>\n * </Tabs>\n *\n * @example\n * // Contained pill variant (controlled):\n * const [tab, setTab] = useState('members')\n * <Tabs value={tab} onValueChange={setTab}>\n * <TabsList variant=\"contained\">\n * <TabsTrigger value=\"members\">Members</TabsTrigger>\n * <TabsTrigger value=\"roles\">Roles</TabsTrigger>\n * </TabsList>\n * <TabsContent value=\"members\">Members list here.</TabsContent>\n * <TabsContent value=\"roles\">Roles list here.</TabsContent>\n * </Tabs>\n */\nconst Tabs = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.Root>\n>(({ value: valueProp, defaultValue, onValueChange, orientation, ...props }, ref) => {\n // Track the active value so TabsTrigger can conditionally render the motion indicator.\n // For controlled usage, mirror the prop; for uncontrolled, manage internal state.\n const [activeValue, setActiveValue] = React.useState(valueProp ?? defaultValue ?? '')\n\n // Sync controlled value\n React.useEffect(() => {\n if (valueProp !== undefined) setActiveValue(valueProp)\n }, [valueProp])\n\n const handleValueChange = React.useCallback(\n (newValue: string) => {\n setActiveValue(newValue)\n onValueChange?.(newValue)\n },\n [onValueChange],\n )\n\n const resolvedOrientation: TabsOrientation = orientation ?? 'horizontal'\n\n return (\n <TabsValueContext.Provider value={activeValue}>\n <TabsOrientationContext.Provider value={resolvedOrientation}>\n <TabsPrimitive.Root\n ref={ref}\n value={valueProp}\n defaultValue={valueProp === undefined ? defaultValue : undefined}\n onValueChange={handleValueChange}\n orientation={orientation}\n {...props}\n className={cn(\n resolvedOrientation === 'vertical' && 'flex flex-row gap-ds-05',\n props.className,\n )}\n />\n </TabsOrientationContext.Provider>\n </TabsValueContext.Provider>\n )\n})\nTabs.displayName = 'Tabs'\n\n/** Props for the Tabs root (defaultValue, value, onValueChange, etc.). */\nexport type TabsProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.Root>\n\ntype TabsVariant = 'line' | 'contained'\ntype TabsSize = 'sm' | 'md' | 'lg'\ntype TabsColor = 'accent' | 'neutral'\n\ninterface TabsListContextValue {\n variant: TabsVariant\n size: TabsSize\n color: TabsColor\n layoutId: string\n}\n\nconst TabsListContext = React.createContext<TabsListContextValue>({\n variant: 'line',\n size: 'md',\n color: 'accent',\n layoutId: 'tab-indicator',\n})\n\nconst tabsListVariants = cva('inline-flex items-center', {\n variants: {\n variant: {\n line: 'border-b border-surface-border-strong w-full gap-0',\n contained:\n 'bg-surface-raised p-ds-02 rounded-surface gap-ds-02',\n },\n size: {\n sm: 'h-8',\n md: 'h-10',\n lg: 'h-12',\n },\n orientation: {\n horizontal: '',\n vertical: 'flex-col',\n },\n },\n compoundVariants: [\n // Vertical line: left border instead of bottom, fixed width, auto height\n { variant: 'line', orientation: 'vertical', class: 'border-b-0 border-l border-surface-border-strong w-48 shrink-0 h-auto gap-0' },\n // Vertical contained: column layout, fixed width, auto height\n { variant: 'contained', orientation: 'vertical', class: 'h-auto w-48 shrink-0' },\n ],\n defaultVariants: { variant: 'line', size: 'md', orientation: 'horizontal' },\n})\n\nconst tabsTriggerVariants = cva(\n 'relative inline-flex items-center justify-center gap-ds-02 whitespace-nowrap font-sans font-medium transition-colors duration-100 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-1 disabled:pointer-events-none disabled:opacity-action-disabled',\n {\n variants: {\n variant: {\n line: [\n '-mb-px',\n 'text-surface-fg-muted hover:text-surface-fg',\n ],\n contained: [\n 'rounded-control',\n 'text-surface-fg-muted hover:text-surface-fg',\n 'data-[state=active]:text-surface-fg',\n ],\n },\n size: {\n sm: 'px-ds-03 py-ds-02 text-ds-xs',\n md: 'px-ds-05 py-ds-03 text-ds-sm',\n lg: 'px-ds-06 py-ds-04 text-ds-md',\n },\n },\n defaultVariants: { variant: 'line', size: 'md' },\n },\n)\n\n/**\n * TabsList — container for tab triggers. Sets `variant` for all child TabsTriggers via context.\n *\n * **Compound structure — variant propagates automatically:**\n * ```tsx\n * <Tabs defaultValue=\"overview\">\n * <TabsList variant=\"contained\">\n * <TabsTrigger value=\"overview\">Overview</TabsTrigger>\n * <TabsTrigger value=\"activity\">Activity</TabsTrigger>\n * </TabsList>\n * <TabsContent value=\"overview\">Overview content</TabsContent>\n * <TabsContent value=\"activity\">Activity content</TabsContent>\n * </Tabs>\n * ```\n *\n * `variant` on `TabsList` flows to all `TabsTrigger` children via React context.\n * You do NOT need to repeat `variant` on each `TabsTrigger`.\n */\nexport interface TabsListProps\n extends React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>,\n VariantProps<typeof tabsListVariants> {\n /** Color axis — affects active tab indicator and text color. @default \"accent\" */\n color?: TabsColor\n}\n\nconst TabsList = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.List>,\n TabsListProps\n>(({ className, variant, size, color, ...props }, ref) => {\n const orientation = React.useContext(TabsOrientationContext)\n const resolvedVariant: TabsVariant = variant ?? 'line'\n const resolvedSize: TabsSize = size ?? 'md'\n const resolvedColor: TabsColor = color ?? 'accent'\n const layoutId = React.useId()\n const contextValue = React.useMemo(\n () => ({\n variant: resolvedVariant,\n size: resolvedSize,\n color: resolvedColor,\n layoutId: `tab-indicator-${layoutId}`,\n }),\n [resolvedVariant, resolvedSize, resolvedColor, layoutId],\n )\n return (\n <TabsListContext.Provider value={contextValue}>\n <LayoutGroup>\n <TabsPrimitive.List\n ref={ref}\n className={cn(tabsListVariants({ variant: resolvedVariant, size: resolvedSize, orientation }), className)}\n {...props}\n />\n </LayoutGroup>\n </TabsListContext.Provider>\n )\n})\nTabsList.displayName = TabsPrimitive.List.displayName\n\nexport interface TabsTriggerProps\n extends React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>,\n VariantProps<typeof tabsTriggerVariants> {}\n\n/* ── Color classes for active state ────── */\nconst lineActiveColorMap: Record<TabsColor, string> = {\n accent: 'data-[state=active]:text-accent-11',\n neutral: 'data-[state=active]:text-surface-fg',\n}\n\nconst lineIndicatorColorMap: Record<TabsColor, string> = {\n accent: 'bg-accent-9',\n neutral: 'bg-surface-fg',\n}\n\nconst TabsTrigger = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.Trigger>,\n TabsTriggerProps\n>(({ className, variant: variantProp, children, ...props }, ref) => {\n const listContext = React.useContext(TabsListContext)\n const activeValue = React.useContext(TabsValueContext)\n const orientation = React.useContext(TabsOrientationContext)\n const variant = variantProp ?? listContext.variant\n const size = listContext.size\n const color = listContext.color\n const isActive = props.value === activeValue\n const isVertical = orientation === 'vertical'\n\n return (\n <TabsPrimitive.Trigger\n ref={ref}\n className={cn(\n tabsTriggerVariants({ variant, size }),\n variant === 'line' && lineActiveColorMap[color],\n // Vertical: full width triggers, left margin instead of bottom\n isVertical && 'w-full justify-start',\n variant === 'line' && isVertical && '-ml-px -mb-0',\n className,\n )}\n {...props}\n >\n {/* Contained variant: sliding pill background */}\n {variant === 'contained' && isActive && (\n <motion.span\n layoutId={`${listContext.layoutId}-contained`}\n className=\"absolute inset-0 rounded-control bg-surface-overlay shadow-raised\"\n transition={springs.smooth}\n />\n )}\n {/* Content sits above the indicator */}\n <span className=\"relative z-[1] inline-flex items-center gap-ds-02\">{children}</span>\n {/* Line variant: sliding indicator -- bottom underline (horizontal) or left bar (vertical) */}\n {variant === 'line' && isActive && (\n <motion.span\n layoutId={`${listContext.layoutId}-line`}\n className={cn(\n 'absolute',\n isVertical\n ? 'left-0 top-0 bottom-0 w-0.5'\n : 'bottom-0 left-0 right-0 h-0.5',\n lineIndicatorColorMap[color],\n )}\n transition={springs.smooth}\n />\n )}\n </TabsPrimitive.Trigger>\n )\n})\nTabsTrigger.displayName = TabsPrimitive.Trigger.displayName\n\nconst TabsContent = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>\n>(({ className, children, ...props }, ref) => {\n const orientation = React.useContext(TabsOrientationContext)\n const isVertical = orientation === 'vertical'\n\n return (\n <TabsPrimitive.Content\n ref={ref}\n className={cn(\n 'ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2',\n isVertical ? 'mt-0 flex-1 min-w-0' : 'mt-ds-05',\n className,\n )}\n {...props}\n >\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={tweens.fade}\n >\n {children}\n </motion.div>\n </TabsPrimitive.Content>\n )\n})\nTabsContent.displayName = TabsPrimitive.Content.displayName\n\n/** Props for TabsContent. The `value` prop must match a TabsTrigger's `value`. */\nexport type TabsContentProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>\n\nexport { Tabs, TabsContent,TabsList, TabsTrigger }\nexport type { TabsColor, TabsOrientation,TabsSize }\n"],"mappings":"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"}
@@ -10,7 +10,7 @@ import { motion as o } from "framer-motion";
10
10
  var s = t([
11
11
  "flex w-full font-sans resize-y",
12
12
  "bg-surface-raised-hover text-surface-fg",
13
- "border border-surface-border-strong rounded-ds-md",
13
+ "border border-surface-border-strong rounded-control",
14
14
  "placeholder:text-surface-fg-subtle",
15
15
  "hover:bg-surface-raised-active",
16
16
  "transition-colors duration-fast-01 ease-productive-standard",
@@ -1 +1 @@
1
- {"version":3,"file":"textarea.js","names":[],"sources":["../../src/ui/textarea.tsx"],"sourcesContent":["'use client'\n\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useFormField } from './form'\nimport type { InputState } from './input'\nimport { motionProps } from './lib/motion'\nimport { cn } from './lib/utils'\n\nconst textareaVariants = cva(\n [\n 'flex w-full font-sans resize-y',\n 'bg-surface-raised-hover text-surface-fg',\n 'border border-surface-border-strong rounded-ds-md',\n 'placeholder:text-surface-fg-subtle',\n 'hover:bg-surface-raised-active',\n 'transition-colors duration-fast-01 ease-productive-standard',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 focus-visible:border-accent-7',\n 'disabled:cursor-not-allowed disabled:opacity-action-disabled',\n 'read-only:bg-surface-raised read-only:cursor-default',\n ],\n {\n variants: {\n size: {\n xs: 'min-h-[48px] text-ds-sm px-ds-02 py-ds-02',\n sm: 'min-h-[60px] text-ds-sm px-ds-03 py-ds-02',\n md: 'min-h-[80px] text-ds-md px-ds-04 py-ds-03',\n lg: 'min-h-[120px] text-ds-md px-ds-05 py-ds-04',\n },\n },\n defaultVariants: { size: 'md' },\n },\n)\n\n/**\n * Props for Textarea — a resizable multi-line text input with size variants and validation state coloring,\n * sharing the same `InputState` type as `<Input>`.\n *\n * **Sizes:** `sm` (min 60px) | `md` (min 80px, default) | `lg` (min 120px) — all are vertically resizable.\n *\n * **Validation states:** `state=\"error\"` | `\"warning\"` | `\"success\"` — changes border color and focus ring.\n * HTML's native `size` attribute is omitted; use `rows` for initial height or CSS `min-height`.\n *\n * **Pair with FormField:** Use inside `<FormField>` to get a label, helper text, and associated `state`.\n *\n * @example\n * // Basic description textarea in a form:\n * <Textarea placeholder=\"Describe the issue...\" rows={4} />\n *\n * @example\n * // Error state when validation fails:\n * <Textarea state=\"error\" value={bio} onChange={(e) => setBio(e.target.value)} />\n *\n * @example\n * // Large textarea for a full email draft composer:\n * <Textarea size=\"lg\" placeholder=\"Write your message...\" className=\"min-h-[200px]\" />\n *\n * @example\n * // Read-only view of a previously submitted note:\n * <Textarea readOnly value={submission.notes} size=\"md\" />\n * // These are just a few ways — feel free to combine props creatively!\n */\nexport interface TextareaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'>,\n VariantProps<typeof textareaVariants> {\n state?: InputState\n}\n\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n ({ className, state: stateProp, size, ...props }, ref) => {\n const fieldCtx = useFormField()\n // Merge FormField context — explicit props always win\n const state = stateProp ?? (fieldCtx.state === 'helper' ? undefined : fieldCtx.state as InputState | undefined)\n const ariaDescribedBy = props['aria-describedby'] ?? fieldCtx.helperTextId\n const ariaRequired = props['aria-required'] ?? fieldCtx.required\n\n return (\n <motion.textarea\n aria-invalid={state === 'error' || undefined}\n aria-describedby={ariaDescribedBy}\n aria-required={ariaRequired || undefined}\n className={cn(\n textareaVariants({ size }),\n state === 'error' && 'border-error-7 focus-visible:ring-error-7',\n state === 'warning' && 'border-warning-7 focus-visible:ring-warning-7',\n state === 'success' && 'border-success-7 focus-visible:ring-success-7',\n className,\n )}\n ref={ref}\n {...motionProps(props)}\n />\n )\n },\n)\nTextarea.displayName = 'Textarea'\n\nexport { Textarea, textareaVariants }\n"],"mappings":";;;;;;;;;AAWA,IAAM,IAAmB,EACvB;CACE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EACD;CACE,UAAU,EACR,MAAM;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACL,EACF;CACD,iBAAiB,EAAE,MAAM,MAAM;CAChC,CACF,EAoCK,IAAW,EAAM,YACpB,EAAE,cAAW,OAAO,GAAW,SAAM,GAAG,KAAS,MAAQ;CACxD,IAAM,IAAW,GAAc,EAEzB,IAAQ,MAAc,EAAS,UAAU,WAAW,KAAA,IAAY,EAAS,QACzE,IAAkB,EAAM,uBAAuB,EAAS,cACxD,IAAe,EAAM,oBAAoB,EAAS;AAExD,QACE,kBAAC,EAAO,UAAR;EACE,gBAAc,MAAU,WAAW,KAAA;EACnC,oBAAkB;EAClB,iBAAe,KAAgB,KAAA;EAC/B,WAAW,EACT,EAAiB,EAAE,SAAM,CAAC,EAC1B,MAAU,WAAW,6CACrB,MAAU,aAAa,iDACvB,MAAU,aAAa,iDACvB,EACD;EACI;EACL,GAAI,EAAY,EAAM;EACtB,CAAA;EAGP;AACD,EAAS,cAAc"}
1
+ {"version":3,"file":"textarea.js","names":[],"sources":["../../src/ui/textarea.tsx"],"sourcesContent":["'use client'\n\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useFormField } from './form'\nimport type { InputState } from './input'\nimport { motionProps } from './lib/motion'\nimport { cn } from './lib/utils'\n\nconst textareaVariants = cva(\n [\n 'flex w-full font-sans resize-y',\n 'bg-surface-raised-hover text-surface-fg',\n 'border border-surface-border-strong rounded-control',\n 'placeholder:text-surface-fg-subtle',\n 'hover:bg-surface-raised-active',\n 'transition-colors duration-fast-01 ease-productive-standard',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 focus-visible:border-accent-7',\n 'disabled:cursor-not-allowed disabled:opacity-action-disabled',\n 'read-only:bg-surface-raised read-only:cursor-default',\n ],\n {\n variants: {\n size: {\n xs: 'min-h-[48px] text-ds-sm px-ds-02 py-ds-02',\n sm: 'min-h-[60px] text-ds-sm px-ds-03 py-ds-02',\n md: 'min-h-[80px] text-ds-md px-ds-04 py-ds-03',\n lg: 'min-h-[120px] text-ds-md px-ds-05 py-ds-04',\n },\n },\n defaultVariants: { size: 'md' },\n },\n)\n\n/**\n * Props for Textarea — a resizable multi-line text input with size variants and validation state coloring,\n * sharing the same `InputState` type as `<Input>`.\n *\n * **Sizes:** `sm` (min 60px) | `md` (min 80px, default) | `lg` (min 120px) — all are vertically resizable.\n *\n * **Validation states:** `state=\"error\"` | `\"warning\"` | `\"success\"` — changes border color and focus ring.\n * HTML's native `size` attribute is omitted; use `rows` for initial height or CSS `min-height`.\n *\n * **Pair with FormField:** Use inside `<FormField>` to get a label, helper text, and associated `state`.\n *\n * @example\n * // Basic description textarea in a form:\n * <Textarea placeholder=\"Describe the issue...\" rows={4} />\n *\n * @example\n * // Error state when validation fails:\n * <Textarea state=\"error\" value={bio} onChange={(e) => setBio(e.target.value)} />\n *\n * @example\n * // Large textarea for a full email draft composer:\n * <Textarea size=\"lg\" placeholder=\"Write your message...\" className=\"min-h-[200px]\" />\n *\n * @example\n * // Read-only view of a previously submitted note:\n * <Textarea readOnly value={submission.notes} size=\"md\" />\n * // These are just a few ways — feel free to combine props creatively!\n */\nexport interface TextareaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'>,\n VariantProps<typeof textareaVariants> {\n state?: InputState\n}\n\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n ({ className, state: stateProp, size, ...props }, ref) => {\n const fieldCtx = useFormField()\n // Merge FormField context — explicit props always win\n const state = stateProp ?? (fieldCtx.state === 'helper' ? undefined : fieldCtx.state as InputState | undefined)\n const ariaDescribedBy = props['aria-describedby'] ?? fieldCtx.helperTextId\n const ariaRequired = props['aria-required'] ?? fieldCtx.required\n\n return (\n <motion.textarea\n aria-invalid={state === 'error' || undefined}\n aria-describedby={ariaDescribedBy}\n aria-required={ariaRequired || undefined}\n className={cn(\n textareaVariants({ size }),\n state === 'error' && 'border-error-7 focus-visible:ring-error-7',\n state === 'warning' && 'border-warning-7 focus-visible:ring-warning-7',\n state === 'success' && 'border-success-7 focus-visible:ring-success-7',\n className,\n )}\n ref={ref}\n {...motionProps(props)}\n />\n )\n },\n)\nTextarea.displayName = 'Textarea'\n\nexport { Textarea, textareaVariants }\n"],"mappings":";;;;;;;;;AAWA,IAAM,IAAmB,EACvB;CACE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EACD;CACE,UAAU,EACR,MAAM;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACL,EACF;CACD,iBAAiB,EAAE,MAAM,MAAM;CAChC,CACF,EAoCK,IAAW,EAAM,YACpB,EAAE,cAAW,OAAO,GAAW,SAAM,GAAG,KAAS,MAAQ;CACxD,IAAM,IAAW,GAAc,EAEzB,IAAQ,MAAc,EAAS,UAAU,WAAW,KAAA,IAAY,EAAS,QACzE,IAAkB,EAAM,uBAAuB,EAAS,cACxD,IAAe,EAAM,oBAAoB,EAAS;AAExD,QACE,kBAAC,EAAO,UAAR;EACE,gBAAc,MAAU,WAAW,KAAA;EACnC,oBAAkB;EAClB,iBAAe,KAAgB,KAAA;EAC/B,WAAW,EACT,EAAiB,EAAE,SAAM,CAAC,EAC1B,MAAU,WAAW,6CACrB,MAAU,aAAa,iDACvB,MAAU,aAAa,iDACvB,EACD;EACI;EACL,GAAI,EAAY,EAAM;EACtB,CAAA;EAGP;AACD,EAAS,cAAc"}
package/dist/ui/toast.js CHANGED
@@ -87,14 +87,14 @@ function k({ type: a, title: o, description: u, action: d, cancel: f, duration:
87
87
  role: j ? "alert" : "status",
88
88
  "aria-live": j ? "assertive" : "polite",
89
89
  "aria-atomic": "true",
90
- className: "group relative flex w-full overflow-hidden rounded-ds-md border border-surface-border-strong bg-surface-overlay shadow-floating",
90
+ className: "group relative flex w-full overflow-hidden rounded-overlay-sm border border-surface-border-strong bg-surface-overlay shadow-floating",
91
91
  onMouseEnter: () => _(!0),
92
92
  onMouseLeave: () => _(!1),
93
93
  onFocusCapture: () => y(!0),
94
94
  onBlurCapture: () => y(!1),
95
95
  transition: e.smooth,
96
96
  children: [
97
- T.accentClass && /* @__PURE__ */ c("div", { className: n("w-1 shrink-0 rounded-l-ds-md", T.accentClass) }),
97
+ T.accentClass && /* @__PURE__ */ c("div", { className: n("w-1 shrink-0 rounded-l-overlay-sm", T.accentClass) }),
98
98
  /* @__PURE__ */ l("div", {
99
99
  className: "flex min-w-0 flex-1 items-start gap-ds-03 p-ds-04",
100
100
  children: [/* @__PURE__ */ c(x, {
@@ -155,12 +155,12 @@ function k({ type: a, title: o, description: u, action: d, cancel: f, duration:
155
155
  children: [d && /* @__PURE__ */ c("button", {
156
156
  type: "button",
157
157
  onClick: d.onClick,
158
- className: "text-ds-sm font-medium text-accent-11 underline-offset-2 hover:underline hover:bg-surface-raised-hover rounded-ds-sm px-ds-02 py-ds-01 transition-[color,background-color] duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-ds-sm",
158
+ className: "text-ds-sm font-medium text-accent-11 underline-offset-2 hover:underline hover:bg-surface-raised-hover rounded-control-inner px-ds-02 py-ds-01 transition-[color,background-color] duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner",
159
159
  children: d.label
160
160
  }), f && /* @__PURE__ */ c("button", {
161
161
  type: "button",
162
162
  onClick: f.onClick,
163
- className: "text-ds-sm text-surface-fg-muted hover:text-surface-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-ds-sm",
163
+ className: "text-ds-sm text-surface-fg-muted hover:text-surface-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner",
164
164
  children: f.label
165
165
  })]
166
166
  })
@@ -329,7 +329,7 @@ function F({ file: n, onRetry: o, onRemove: s }) {
329
329
  n.status === "error" && o && /* @__PURE__ */ c("button", {
330
330
  type: "button",
331
331
  onClick: () => o(n.id),
332
- className: "touch-target flex min-h-6 min-w-6 items-center justify-center rounded-ds-sm text-surface-fg-muted hover:text-surface-fg",
332
+ className: "touch-target flex min-h-6 min-w-6 items-center justify-center rounded-control-inner text-surface-fg-muted hover:text-surface-fg",
333
333
  "aria-label": `Retry ${n.name}`,
334
334
  title: "Retry",
335
335
  children: /* @__PURE__ */ c(v, { className: "h-3.5 w-3.5" })
@@ -337,7 +337,7 @@ function F({ file: n, onRetry: o, onRemove: s }) {
337
337
  !d && s && /* @__PURE__ */ c("button", {
338
338
  type: "button",
339
339
  onClick: () => s(n.id),
340
- className: "touch-target flex min-h-6 min-w-6 items-center justify-center rounded-ds-sm text-surface-fg-muted hover:text-surface-fg",
340
+ className: "touch-target flex min-h-6 min-w-6 items-center justify-center rounded-control-inner text-surface-fg-muted hover:text-surface-fg",
341
341
  "aria-label": `Cancel ${n.name}`,
342
342
  title: "Remove",
343
343
  children: /* @__PURE__ */ c(b, { className: "h-3.5 w-3.5" })
@@ -370,14 +370,14 @@ function I({ files: r, onRetry: a, onRemove: o, selfDismissId: d }) {
370
370
  role: j ? "alert" : "status",
371
371
  "aria-live": j ? "assertive" : "polite",
372
372
  "aria-label": "File uploads",
373
- className: "group relative flex w-full overflow-hidden rounded-ds-md border border-surface-border-strong bg-surface-overlay shadow-floating",
373
+ className: "group relative flex w-full overflow-hidden rounded-overlay-sm border border-surface-border-strong bg-surface-overlay shadow-floating",
374
374
  onMouseEnter: () => m(!0),
375
375
  onMouseLeave: () => m(!1),
376
376
  onFocusCapture: () => g(!0),
377
377
  onBlurCapture: () => g(!1),
378
378
  transition: e.smooth,
379
379
  children: [
380
- /* @__PURE__ */ c("div", { className: n("w-1 shrink-0 rounded-l-ds-md", k) }),
380
+ /* @__PURE__ */ c("div", { className: n("w-1 shrink-0 rounded-l-overlay-sm", k) }),
381
381
  /* @__PURE__ */ l("div", {
382
382
  className: "min-w-0 flex-1 p-ds-04",
383
383
  children: [
@@ -1 +1 @@
1
- {"version":3,"file":"toast.js","names":[],"sources":["../../src/ui/toast.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconAlertCircle,\n IconAlertTriangle,\n IconCheck,\n IconCircleCheck,\n IconCircleX,\n IconFile,\n IconInfoCircle,\n IconPhoto,\n IconRefresh,\n IconUpload,\n IconX,\n} from '@tabler/icons-react'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\nimport { type ExternalToast,toast as sonnerToast } from 'sonner'\n\nimport { Icon } from './icon'\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\nimport { Progress } from './progress'\nimport { Spinner } from './spinner'\nimport { assertToasterMounted } from './toast-registry'\nimport type {\n ToastActionOptions,\n ToastOptions,\n ToastType,\n ToastUndoOptions,\n ToastUploadOptions,\n UploadFile,\n} from './toast-types'\n\n/* ---------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------ */\n\nconst DEFAULT_DURATION = 5000\nconst UNDO_DURATION = 8000\nconst UPLOAD_COMPLETE_DELAY = 3000\n\n/* ---------------------------------------------------------------------------\n * Accent bar & icon config per toast type\n * ------------------------------------------------------------------------ */\n\nconst TOAST_TYPE_CONFIG: Record<\n ToastType,\n {\n accentClass: string\n iconClass: string\n icon: React.ForwardRefExoticComponent<any> | null\n timerBarClass: string\n }\n> = {\n message: {\n accentClass: '',\n iconClass: '',\n icon: null,\n timerBarClass: 'bg-surface-border',\n },\n success: {\n accentClass: 'bg-success-9',\n iconClass: 'text-success-11',\n icon: IconCircleCheck,\n timerBarClass: 'bg-success-9',\n },\n error: {\n accentClass: 'bg-error-9',\n iconClass: 'text-error-11',\n icon: IconCircleX,\n timerBarClass: 'bg-error-9',\n },\n warning: {\n accentClass: 'bg-warning-9',\n iconClass: 'text-warning-11',\n icon: IconAlertTriangle,\n timerBarClass: 'bg-warning-9',\n },\n info: {\n accentClass: 'bg-info-9',\n iconClass: 'text-info-11',\n icon: IconInfoCircle,\n timerBarClass: 'bg-info-9',\n },\n loading: {\n accentClass: 'bg-accent-9',\n iconClass: 'text-accent-11',\n icon: null,\n timerBarClass: 'bg-accent-9',\n },\n}\n\n/* ---------------------------------------------------------------------------\n * Timer bar — shows auto-dismiss countdown\n * ------------------------------------------------------------------------ */\n\nfunction TimerBar({\n duration,\n type,\n paused,\n}: {\n duration: number\n type: ToastType\n paused: boolean\n}) {\n const config = TOAST_TYPE_CONFIG[type]\n return (\n <div className=\"absolute bottom-0 left-0 right-0 h-[2px] overflow-hidden\">\n <div\n className={cn(\n 'h-full w-full origin-left opacity-30',\n config.timerBarClass,\n 'motion-safe:animate-timer-bar',\n )}\n style={{\n animationDuration: `${duration}ms`,\n animationPlayState: paused ? 'paused' : 'running',\n }}\n />\n </div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Base toast UI — used by all typed toasts\n * ------------------------------------------------------------------------ */\n\nfunction ToastContent({\n type,\n title,\n description,\n action,\n cancel,\n duration = DEFAULT_DURATION,\n showTimerBar = true,\n selfDismissId,\n}: {\n type: ToastType\n title?: React.ReactNode\n description?: React.ReactNode\n action?: ToastActionOptions\n cancel?: ToastActionOptions\n duration?: number\n showTimerBar?: boolean\n /** When set, this component manages its own dismiss timer (hover-aware). */\n selfDismissId?: string | number\n}) {\n const [hovered, setHovered] = React.useState(false)\n const [focused, setFocused] = React.useState(false)\n const paused = hovered || focused\n const config = TOAST_TYPE_CONFIG[type]\n const StatusIcon = config.icon\n\n // Self-managed dismiss timer that pauses on hover or keyboard focus\n const remainingRef = React.useRef(duration)\n const startTimeRef = React.useRef(0)\n\n React.useEffect(() => {\n if (selfDismissId == null || duration === Infinity) return\n if (paused) {\n // Pause: save remaining time\n const elapsed = Date.now() - startTimeRef.current\n remainingRef.current = Math.max(0, remainingRef.current - elapsed)\n return\n }\n // Start/resume timer\n startTimeRef.current = Date.now()\n const timer = setTimeout(() => {\n sonnerToast.dismiss(selfDismissId)\n }, remainingRef.current)\n return () => clearTimeout(timer)\n }, [selfDismissId, duration, paused])\n\n // Error toasts announce assertively so screen readers interrupt;\n // warnings stay polite but atomically re-read the message.\n const isUrgent = type === 'error'\n return (\n <motion.div\n layout=\"position\"\n role={isUrgent ? 'alert' : 'status'}\n aria-live={isUrgent ? 'assertive' : 'polite'}\n aria-atomic=\"true\"\n className=\"group relative flex w-full overflow-hidden rounded-ds-md border border-surface-border-strong bg-surface-overlay shadow-floating\"\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onFocusCapture={() => setFocused(true)}\n onBlurCapture={() => setFocused(false)}\n transition={springs.smooth}\n >\n {/* Left accent bar */}\n {config.accentClass && (\n <div\n className={cn('w-1 shrink-0 rounded-l-ds-md', config.accentClass)}\n />\n )}\n\n {/* Content */}\n <div className=\"flex min-w-0 flex-1 items-start gap-ds-03 p-ds-04\">\n {/* Status icon — animates between spinner ↔ typed icon */}\n <AnimatePresence mode=\"wait\">\n {type === 'loading' ? (\n <motion.div\n key=\"loading\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={tweens.fade}\n className=\"mt-0.5 shrink-0\"\n >\n <Spinner size=\"sm\" className=\"h-4 w-4\" />\n </motion.div>\n ) : StatusIcon ? (\n <motion.div\n key={type}\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n className=\"mt-0.5 shrink-0\"\n >\n <Icon icon={StatusIcon} size=\"sm\" className={config.iconClass} />\n </motion.div>\n ) : null}\n </AnimatePresence>\n\n {/* Text */}\n <div className=\"min-w-0 flex-1\">\n {title && (\n <p className=\"text-ds-md font-semibold text-surface-fg\">\n {title}\n </p>\n )}\n {description && (\n <p\n className={cn(\n 'text-ds-sm text-surface-fg-muted',\n title && 'mt-0.5',\n )}\n >\n {description}\n </p>\n )}\n\n {/* Action / cancel buttons */}\n {(action || cancel) && (\n <div className=\"mt-ds-03 flex items-center gap-ds-03\">\n {action && (\n <button\n type=\"button\"\n onClick={action.onClick}\n className=\"text-ds-sm font-medium text-accent-11 underline-offset-2 hover:underline hover:bg-surface-raised-hover rounded-ds-sm px-ds-02 py-ds-01 transition-[color,background-color] duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-ds-sm\"\n >\n {action.label}\n </button>\n )}\n {cancel && (\n <button\n type=\"button\"\n onClick={cancel.onClick}\n className=\"text-ds-sm text-surface-fg-muted hover:text-surface-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-ds-sm\"\n >\n {cancel.label}\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n\n {/* Timer bar */}\n {showTimerBar && type !== 'loading' && (\n <TimerBar duration={duration} type={type} paused={paused} />\n )}\n </motion.div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Upload toast UI\n * ------------------------------------------------------------------------ */\n\nconst IMAGE_EXTENSIONS = new Set([\n 'jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico', 'avif',\n])\n\nfunction isImageFile(file: UploadFile): boolean {\n if (file.previewUrl) return true\n const ext = file.name.split('.').pop()?.toLowerCase() ?? ''\n return IMAGE_EXTENSIONS.has(ext)\n}\n\nfunction getProgressColor(\n status: UploadFile['status'],\n): 'default' | 'success' | 'error' {\n switch (status) {\n case 'complete':\n return 'success'\n case 'error':\n return 'error'\n default:\n return 'default'\n }\n}\n\nfunction getProgressValue(file: UploadFile): number | undefined {\n switch (file.status) {\n case 'pending':\n return 0\n case 'uploading':\n return file.progress ?? 0\n case 'processing':\n return undefined\n case 'complete':\n return 100\n case 'error':\n return file.progress ?? 0\n }\n}\n\nfunction formatFileSize(bytes: number): string {\n if (!bytes || bytes < 0 || !Number.isFinite(bytes)) return '0 B'\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n if (bytes < 1024 * 1024 * 1024)\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`\n}\n\nfunction UploadFileRow({\n file,\n onRetry,\n onRemove,\n}: {\n file: UploadFile\n onRetry?: (fileId: string) => void\n onRemove?: (fileId: string) => void\n}) {\n const isTerminal = file.status === 'complete' || file.status === 'error'\n const isUploading =\n file.status === 'uploading' || file.status === 'processing'\n\n return (\n <motion.div\n layout\n className=\"flex items-center gap-ds-02 py-1\"\n initial={false}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, x: -20, height: 0 }}\n transition={springs.snappy}\n >\n {/* File icon — animates between states */}\n <div className=\"flex h-5 w-5 shrink-0 items-center justify-center\">\n <AnimatePresence mode=\"wait\">\n {file.status === 'complete' ? (\n <motion.div\n key=\"complete\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconCheck} size=\"xs\" className=\"text-success-11\" />\n </motion.div>\n ) : file.status === 'error' ? (\n <motion.div\n key=\"error\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={tweens.fade}\n >\n <Icon icon={IconAlertCircle} size=\"xs\" className=\"text-error-11\" />\n </motion.div>\n ) : file.status === 'processing' ? (\n <motion.div\n key=\"processing\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Spinner size=\"sm\" className=\"h-3.5 w-3.5\" />\n </motion.div>\n ) : isImageFile(file) ? (\n <motion.div\n key=\"image\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconPhoto} size=\"xs\" className=\"text-surface-fg-muted\" />\n </motion.div>\n ) : (\n <motion.div\n key=\"file\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconFile} size=\"xs\" className=\"text-surface-fg-muted\" />\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n\n {/* Filename */}\n <span className=\"min-w-0 flex-1 truncate text-ds-xs text-surface-fg\">\n {file.name}\n </span>\n\n {/* Progress bar or status */}\n {isUploading ? (\n <div className=\"flex w-16 items-center gap-1\">\n <Progress\n size=\"sm\"\n color={getProgressColor(file.status)}\n value={getProgressValue(file)}\n className=\"flex-1\"\n />\n {file.progress !== undefined && (\n <span className=\"shrink-0 text-ds-xs tabular-nums text-surface-fg-muted\">\n {file.progress}%\n </span>\n )}\n </div>\n ) : file.status === 'complete' ? (\n <span className=\"text-ds-xs text-success-11\">Done</span>\n ) : file.status === 'error' ? (\n <span className=\"max-w-[60px] truncate text-ds-xs text-error-11\">\n {file.error || 'Failed'}\n </span>\n ) : (\n <span className=\"text-ds-xs text-surface-fg-muted\">\n {formatFileSize(file.size)}\n </span>\n )}\n\n {/* Actions */}\n {file.status === 'error' && onRetry && (\n <button\n type=\"button\"\n onClick={() => onRetry(file.id)}\n className=\"touch-target flex min-h-6 min-w-6 items-center justify-center rounded-ds-sm text-surface-fg-muted hover:text-surface-fg\"\n aria-label={`Retry ${file.name}`}\n title=\"Retry\"\n >\n <IconRefresh className=\"h-3.5 w-3.5\" />\n </button>\n )}\n {!isTerminal && onRemove && (\n <button\n type=\"button\"\n onClick={() => onRemove(file.id)}\n className=\"touch-target flex min-h-6 min-w-6 items-center justify-center rounded-ds-sm text-surface-fg-muted hover:text-surface-fg\"\n aria-label={`Cancel ${file.name}`}\n title=\"Remove\"\n >\n <IconX className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </motion.div>\n )\n}\n\nfunction UploadToastContent({\n files,\n onRetry,\n onRemove,\n selfDismissId,\n}: {\n files: UploadFile[]\n onRetry?: (fileId: string) => void\n onRemove?: (fileId: string) => void\n selfDismissId?: string | number\n}) {\n const [hovered, setHovered] = React.useState(false)\n const [focused, setFocused] = React.useState(false)\n const paused = hovered || focused\n const completeCount = files.filter((f) => f.status === 'complete').length\n const errorCount = files.filter((f) => f.status === 'error').length\n const allTerminal = files.every(\n (f) => f.status === 'complete' || f.status === 'error',\n )\n\n // Self-managed dismiss timer that pauses on hover or keyboard focus\n const remainingRef = React.useRef(UPLOAD_COMPLETE_DELAY)\n const startTimeRef = React.useRef(0)\n\n React.useEffect(() => {\n if (selfDismissId == null || !allTerminal) return\n if (paused) {\n const elapsed = Date.now() - startTimeRef.current\n remainingRef.current = Math.max(0, remainingRef.current - elapsed)\n return\n }\n startTimeRef.current = Date.now()\n const timer = setTimeout(() => {\n sonnerToast.dismiss(selfDismissId)\n }, remainingRef.current)\n return () => clearTimeout(timer)\n }, [selfDismissId, allTerminal, paused])\n\n const accentClass = allTerminal\n ? errorCount > 0\n ? 'bg-error-7'\n : 'bg-success-7'\n : 'bg-accent-9'\n\n const timerBarType: ToastType = allTerminal\n ? errorCount > 0\n ? 'error'\n : 'success'\n : 'loading'\n\n const uploadUrgent = allTerminal && errorCount > 0\n return (\n <motion.div\n layout\n role={uploadUrgent ? 'alert' : 'status'}\n aria-live={uploadUrgent ? 'assertive' : 'polite'}\n aria-label=\"File uploads\"\n className=\"group relative flex w-full overflow-hidden rounded-ds-md border border-surface-border-strong bg-surface-overlay shadow-floating\"\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onFocusCapture={() => setFocused(true)}\n onBlurCapture={() => setFocused(false)}\n transition={springs.smooth}\n >\n {/* Left accent bar */}\n <div className={cn('w-1 shrink-0 rounded-l-ds-md', accentClass)} />\n\n {/* Content */}\n <div className=\"min-w-0 flex-1 p-ds-04\">\n {/* Header — icon morphs between upload ↔ result */}\n <div className=\"flex items-center gap-ds-02\">\n <AnimatePresence mode=\"wait\">\n {allTerminal ? (\n errorCount > 0 ? (\n <motion.div\n key=\"error-icon\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconAlertCircle} size=\"sm\" className=\"shrink-0 text-error-11\" />\n </motion.div>\n ) : (\n <motion.div\n key=\"success-icon\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconCheck} size=\"sm\" className=\"shrink-0 text-success-11\" />\n </motion.div>\n )\n ) : (\n <motion.div\n key=\"upload-icon\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconUpload} size=\"sm\" className=\"shrink-0 text-surface-fg-muted\" />\n </motion.div>\n )}\n </AnimatePresence>\n <p className=\"text-ds-md font-semibold text-surface-fg\">\n {allTerminal\n ? errorCount > 0\n ? `${completeCount} of ${files.length} uploaded`\n : `${files.length} file${files.length > 1 ? 's' : ''} uploaded`\n : `Uploading ${files.length} file${files.length > 1 ? 's' : ''}`}\n </p>\n </div>\n {!allTerminal && (\n <p className=\"mt-0.5 text-ds-xs text-surface-fg-muted\">\n {completeCount} of {files.length} complete\n {errorCount > 0 && ` · ${errorCount} failed`}\n </p>\n )}\n\n {/* File list */}\n <div className=\"mt-ds-02 max-h-[140px] overflow-y-auto\">\n <AnimatePresence>\n {files.map((file) => (\n <UploadFileRow\n key={file.id}\n file={file}\n onRetry={onRetry}\n onRemove={onRemove}\n />\n ))}\n </AnimatePresence>\n </div>\n\n {/* Screen reader announcement */}\n <div className=\"sr-only\" aria-live=\"polite\">\n {completeCount} of {files.length} files uploaded\n {errorCount > 0 && `, ${errorCount} failed`}\n </div>\n </div>\n\n {/* Timer bar — only after all terminal */}\n {allTerminal && (\n <TimerBar\n duration={UPLOAD_COMPLETE_DELAY}\n type={timerBarType}\n paused={false}\n />\n )}\n </motion.div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Toast API — wraps Sonner with our styled UI\n * ------------------------------------------------------------------------ */\n\nfunction createSonnerOptions(\n options?: ToastOptions,\n duration?: number,\n): ExternalToast {\n return {\n id: options?.id,\n duration: options?.duration ?? duration ?? DEFAULT_DURATION,\n unstyled: true,\n classNames: { toast: 'w-full' },\n }\n}\n\nfunction createTypedToast(\n type: ToastType,\n message: string | React.ReactNode,\n options?: ToastOptions,\n) {\n assertToasterMounted()\n const duration = options?.duration ?? DEFAULT_DURATION\n return sonnerToast.custom(\n () => (\n <ToastContent\n type={type}\n title={message}\n description={options?.description}\n action={options?.action}\n cancel={options?.cancel}\n duration={duration}\n />\n ),\n createSonnerOptions(options, duration),\n )\n}\n\n/**\n * Toast API — imperative toast notifications with Sonner rendering engine.\n *\n * Mount `<Toaster />` once at your root layout. Then call `toast()` from anywhere.\n *\n * @example\n * toast('Something happened')\n * toast.success('Saved!')\n * toast.error('Failed to save')\n * toast.promise(saveFn(), { loading: 'Saving...', success: 'Done!', error: 'Failed' })\n * toast.undo('Deleted', { onUndo: () => restore() })\n * toast.upload({ files, onRetry, onRemove })\n */\nfunction toast(message: string | React.ReactNode, options?: ToastOptions) {\n return createTypedToast('message', message, options)\n}\n\ntoast.message = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('message', message, options)\n}\n\ntoast.success = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('success', message, options)\n}\n\ntoast.error = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('error', message, options)\n}\n\ntoast.warning = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('warning', message, options)\n}\n\ntoast.info = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('info', message, options)\n}\n\ntoast.loading = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('loading', message, {\n ...options,\n duration: options?.duration ?? Infinity,\n })\n}\n\ntoast.promise = <T,>(\n promise: Promise<T>,\n options: {\n loading: string | React.ReactNode\n success: string | React.ReactNode | ((data: T) => string | React.ReactNode)\n error:\n | string\n | React.ReactNode\n | ((error: unknown) => string | React.ReactNode)\n },\n) => {\n const id = sonnerToast.custom(\n () => (\n <ToastContent type=\"loading\" title={options.loading} showTimerBar={false} />\n ),\n { duration: Infinity, unstyled: true, classNames: { toast: 'w-full' } },\n )\n\n const replaceWith = (type: ToastType, message: React.ReactNode) => {\n // Sonner doesn't reset its auto-dismiss timer when updating a custom toast\n // in-place from duration: Infinity, so ToastContent manages its own\n // hover-aware dismiss timer via selfDismissId.\n sonnerToast.custom(\n () => (\n <ToastContent\n type={type}\n title={message}\n duration={DEFAULT_DURATION}\n selfDismissId={id}\n />\n ),\n { id, duration: Infinity, unstyled: true, classNames: { toast: 'w-full' } },\n )\n }\n\n promise\n .then((data) => {\n const message =\n typeof options.success === 'function'\n ? options.success(data)\n : options.success\n replaceWith('success', message)\n })\n .catch((error) => {\n const message =\n typeof options.error === 'function'\n ? options.error(error)\n : options.error\n replaceWith('error', message)\n })\n\n return id\n}\n\ntoast.undo = (message: string | React.ReactNode, options: ToastUndoOptions) => {\n const duration = options.duration ?? UNDO_DURATION\n return sonnerToast.custom(\n () => (\n <ToastContent\n type=\"message\"\n title={message}\n description={options.description}\n action={{ label: 'Undo', onClick: options.onUndo }}\n duration={duration}\n />\n ),\n { duration, unstyled: true, classNames: { toast: 'w-full' } },\n )\n}\n\ntoast.upload = (options: ToastUploadOptions) => {\n // Pre-determine the id so we can pass it to UploadToastContent for self-dismiss.\n const toastId = options.id ?? String(Date.now())\n\n sonnerToast.custom(\n () => (\n <UploadToastContent\n files={options.files}\n onRetry={options.onRetry}\n onRemove={options.onRemove}\n selfDismissId={toastId}\n />\n ),\n {\n id: toastId,\n duration: Infinity,\n unstyled: true,\n classNames: { toast: 'w-full' },\n },\n )\n\n return toastId\n}\n\ntoast.custom = (\n render: (id: string | number) => React.ReactElement,\n options?: ExternalToast,\n) => {\n return sonnerToast.custom(render, {\n ...options,\n unstyled: true,\n classNames: { toast: 'w-full', ...(options?.classNames as Record<string, string>) },\n })\n}\n\ntoast.dismiss = (id?: string | number) => {\n if (id !== undefined) {\n sonnerToast.dismiss(id)\n } else {\n sonnerToast.dismiss()\n }\n}\n\n/* ---------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------ */\n\nexport type ToastProps = ToastOptions\n\nexport {\n formatFileSize,\n TimerBar,\n toast,\n ToastContent,\n UploadFileRow,\n UploadToastContent,\n}\nexport type {\n ToastActionOptions,\n ToastOptions,\n ToastType,\n ToastUndoOptions,\n ToastUploadOptions,\n UploadFile,\n} from './toast-types'\n"],"mappings":"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"}
1
+ {"version":3,"file":"toast.js","names":[],"sources":["../../src/ui/toast.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconAlertCircle,\n IconAlertTriangle,\n IconCheck,\n IconCircleCheck,\n IconCircleX,\n IconFile,\n IconInfoCircle,\n IconPhoto,\n IconRefresh,\n IconUpload,\n IconX,\n} from '@tabler/icons-react'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\nimport { type ExternalToast,toast as sonnerToast } from 'sonner'\n\nimport { Icon } from './icon'\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\nimport { Progress } from './progress'\nimport { Spinner } from './spinner'\nimport { assertToasterMounted } from './toast-registry'\nimport type {\n ToastActionOptions,\n ToastOptions,\n ToastType,\n ToastUndoOptions,\n ToastUploadOptions,\n UploadFile,\n} from './toast-types'\n\n/* ---------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------ */\n\nconst DEFAULT_DURATION = 5000\nconst UNDO_DURATION = 8000\nconst UPLOAD_COMPLETE_DELAY = 3000\n\n/* ---------------------------------------------------------------------------\n * Accent bar & icon config per toast type\n * ------------------------------------------------------------------------ */\n\nconst TOAST_TYPE_CONFIG: Record<\n ToastType,\n {\n accentClass: string\n iconClass: string\n icon: React.ForwardRefExoticComponent<any> | null\n timerBarClass: string\n }\n> = {\n message: {\n accentClass: '',\n iconClass: '',\n icon: null,\n timerBarClass: 'bg-surface-border',\n },\n success: {\n accentClass: 'bg-success-9',\n iconClass: 'text-success-11',\n icon: IconCircleCheck,\n timerBarClass: 'bg-success-9',\n },\n error: {\n accentClass: 'bg-error-9',\n iconClass: 'text-error-11',\n icon: IconCircleX,\n timerBarClass: 'bg-error-9',\n },\n warning: {\n accentClass: 'bg-warning-9',\n iconClass: 'text-warning-11',\n icon: IconAlertTriangle,\n timerBarClass: 'bg-warning-9',\n },\n info: {\n accentClass: 'bg-info-9',\n iconClass: 'text-info-11',\n icon: IconInfoCircle,\n timerBarClass: 'bg-info-9',\n },\n loading: {\n accentClass: 'bg-accent-9',\n iconClass: 'text-accent-11',\n icon: null,\n timerBarClass: 'bg-accent-9',\n },\n}\n\n/* ---------------------------------------------------------------------------\n * Timer bar — shows auto-dismiss countdown\n * ------------------------------------------------------------------------ */\n\nfunction TimerBar({\n duration,\n type,\n paused,\n}: {\n duration: number\n type: ToastType\n paused: boolean\n}) {\n const config = TOAST_TYPE_CONFIG[type]\n return (\n <div className=\"absolute bottom-0 left-0 right-0 h-[2px] overflow-hidden\">\n <div\n className={cn(\n 'h-full w-full origin-left opacity-30',\n config.timerBarClass,\n 'motion-safe:animate-timer-bar',\n )}\n style={{\n animationDuration: `${duration}ms`,\n animationPlayState: paused ? 'paused' : 'running',\n }}\n />\n </div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Base toast UI — used by all typed toasts\n * ------------------------------------------------------------------------ */\n\nfunction ToastContent({\n type,\n title,\n description,\n action,\n cancel,\n duration = DEFAULT_DURATION,\n showTimerBar = true,\n selfDismissId,\n}: {\n type: ToastType\n title?: React.ReactNode\n description?: React.ReactNode\n action?: ToastActionOptions\n cancel?: ToastActionOptions\n duration?: number\n showTimerBar?: boolean\n /** When set, this component manages its own dismiss timer (hover-aware). */\n selfDismissId?: string | number\n}) {\n const [hovered, setHovered] = React.useState(false)\n const [focused, setFocused] = React.useState(false)\n const paused = hovered || focused\n const config = TOAST_TYPE_CONFIG[type]\n const StatusIcon = config.icon\n\n // Self-managed dismiss timer that pauses on hover or keyboard focus\n const remainingRef = React.useRef(duration)\n const startTimeRef = React.useRef(0)\n\n React.useEffect(() => {\n if (selfDismissId == null || duration === Infinity) return\n if (paused) {\n // Pause: save remaining time\n const elapsed = Date.now() - startTimeRef.current\n remainingRef.current = Math.max(0, remainingRef.current - elapsed)\n return\n }\n // Start/resume timer\n startTimeRef.current = Date.now()\n const timer = setTimeout(() => {\n sonnerToast.dismiss(selfDismissId)\n }, remainingRef.current)\n return () => clearTimeout(timer)\n }, [selfDismissId, duration, paused])\n\n // Error toasts announce assertively so screen readers interrupt;\n // warnings stay polite but atomically re-read the message.\n const isUrgent = type === 'error'\n return (\n <motion.div\n layout=\"position\"\n role={isUrgent ? 'alert' : 'status'}\n aria-live={isUrgent ? 'assertive' : 'polite'}\n aria-atomic=\"true\"\n className=\"group relative flex w-full overflow-hidden rounded-overlay-sm border border-surface-border-strong bg-surface-overlay shadow-floating\"\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onFocusCapture={() => setFocused(true)}\n onBlurCapture={() => setFocused(false)}\n transition={springs.smooth}\n >\n {/* Left accent bar */}\n {config.accentClass && (\n <div\n className={cn('w-1 shrink-0 rounded-l-overlay-sm', config.accentClass)}\n />\n )}\n\n {/* Content */}\n <div className=\"flex min-w-0 flex-1 items-start gap-ds-03 p-ds-04\">\n {/* Status icon — animates between spinner ↔ typed icon */}\n <AnimatePresence mode=\"wait\">\n {type === 'loading' ? (\n <motion.div\n key=\"loading\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={tweens.fade}\n className=\"mt-0.5 shrink-0\"\n >\n <Spinner size=\"sm\" className=\"h-4 w-4\" />\n </motion.div>\n ) : StatusIcon ? (\n <motion.div\n key={type}\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n className=\"mt-0.5 shrink-0\"\n >\n <Icon icon={StatusIcon} size=\"sm\" className={config.iconClass} />\n </motion.div>\n ) : null}\n </AnimatePresence>\n\n {/* Text */}\n <div className=\"min-w-0 flex-1\">\n {title && (\n <p className=\"text-ds-md font-semibold text-surface-fg\">\n {title}\n </p>\n )}\n {description && (\n <p\n className={cn(\n 'text-ds-sm text-surface-fg-muted',\n title && 'mt-0.5',\n )}\n >\n {description}\n </p>\n )}\n\n {/* Action / cancel buttons */}\n {(action || cancel) && (\n <div className=\"mt-ds-03 flex items-center gap-ds-03\">\n {action && (\n <button\n type=\"button\"\n onClick={action.onClick}\n className=\"text-ds-sm font-medium text-accent-11 underline-offset-2 hover:underline hover:bg-surface-raised-hover rounded-control-inner px-ds-02 py-ds-01 transition-[color,background-color] duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner\"\n >\n {action.label}\n </button>\n )}\n {cancel && (\n <button\n type=\"button\"\n onClick={cancel.onClick}\n className=\"text-ds-sm text-surface-fg-muted hover:text-surface-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner\"\n >\n {cancel.label}\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n\n {/* Timer bar */}\n {showTimerBar && type !== 'loading' && (\n <TimerBar duration={duration} type={type} paused={paused} />\n )}\n </motion.div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Upload toast UI\n * ------------------------------------------------------------------------ */\n\nconst IMAGE_EXTENSIONS = new Set([\n 'jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico', 'avif',\n])\n\nfunction isImageFile(file: UploadFile): boolean {\n if (file.previewUrl) return true\n const ext = file.name.split('.').pop()?.toLowerCase() ?? ''\n return IMAGE_EXTENSIONS.has(ext)\n}\n\nfunction getProgressColor(\n status: UploadFile['status'],\n): 'default' | 'success' | 'error' {\n switch (status) {\n case 'complete':\n return 'success'\n case 'error':\n return 'error'\n default:\n return 'default'\n }\n}\n\nfunction getProgressValue(file: UploadFile): number | undefined {\n switch (file.status) {\n case 'pending':\n return 0\n case 'uploading':\n return file.progress ?? 0\n case 'processing':\n return undefined\n case 'complete':\n return 100\n case 'error':\n return file.progress ?? 0\n }\n}\n\nfunction formatFileSize(bytes: number): string {\n if (!bytes || bytes < 0 || !Number.isFinite(bytes)) return '0 B'\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n if (bytes < 1024 * 1024 * 1024)\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`\n}\n\nfunction UploadFileRow({\n file,\n onRetry,\n onRemove,\n}: {\n file: UploadFile\n onRetry?: (fileId: string) => void\n onRemove?: (fileId: string) => void\n}) {\n const isTerminal = file.status === 'complete' || file.status === 'error'\n const isUploading =\n file.status === 'uploading' || file.status === 'processing'\n\n return (\n <motion.div\n layout\n className=\"flex items-center gap-ds-02 py-1\"\n initial={false}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, x: -20, height: 0 }}\n transition={springs.snappy}\n >\n {/* File icon — animates between states */}\n <div className=\"flex h-5 w-5 shrink-0 items-center justify-center\">\n <AnimatePresence mode=\"wait\">\n {file.status === 'complete' ? (\n <motion.div\n key=\"complete\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconCheck} size=\"xs\" className=\"text-success-11\" />\n </motion.div>\n ) : file.status === 'error' ? (\n <motion.div\n key=\"error\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={tweens.fade}\n >\n <Icon icon={IconAlertCircle} size=\"xs\" className=\"text-error-11\" />\n </motion.div>\n ) : file.status === 'processing' ? (\n <motion.div\n key=\"processing\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Spinner size=\"sm\" className=\"h-3.5 w-3.5\" />\n </motion.div>\n ) : isImageFile(file) ? (\n <motion.div\n key=\"image\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconPhoto} size=\"xs\" className=\"text-surface-fg-muted\" />\n </motion.div>\n ) : (\n <motion.div\n key=\"file\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconFile} size=\"xs\" className=\"text-surface-fg-muted\" />\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n\n {/* Filename */}\n <span className=\"min-w-0 flex-1 truncate text-ds-xs text-surface-fg\">\n {file.name}\n </span>\n\n {/* Progress bar or status */}\n {isUploading ? (\n <div className=\"flex w-16 items-center gap-1\">\n <Progress\n size=\"sm\"\n color={getProgressColor(file.status)}\n value={getProgressValue(file)}\n className=\"flex-1\"\n />\n {file.progress !== undefined && (\n <span className=\"shrink-0 text-ds-xs tabular-nums text-surface-fg-muted\">\n {file.progress}%\n </span>\n )}\n </div>\n ) : file.status === 'complete' ? (\n <span className=\"text-ds-xs text-success-11\">Done</span>\n ) : file.status === 'error' ? (\n <span className=\"max-w-[60px] truncate text-ds-xs text-error-11\">\n {file.error || 'Failed'}\n </span>\n ) : (\n <span className=\"text-ds-xs text-surface-fg-muted\">\n {formatFileSize(file.size)}\n </span>\n )}\n\n {/* Actions */}\n {file.status === 'error' && onRetry && (\n <button\n type=\"button\"\n onClick={() => onRetry(file.id)}\n className=\"touch-target flex min-h-6 min-w-6 items-center justify-center rounded-control-inner text-surface-fg-muted hover:text-surface-fg\"\n aria-label={`Retry ${file.name}`}\n title=\"Retry\"\n >\n <IconRefresh className=\"h-3.5 w-3.5\" />\n </button>\n )}\n {!isTerminal && onRemove && (\n <button\n type=\"button\"\n onClick={() => onRemove(file.id)}\n className=\"touch-target flex min-h-6 min-w-6 items-center justify-center rounded-control-inner text-surface-fg-muted hover:text-surface-fg\"\n aria-label={`Cancel ${file.name}`}\n title=\"Remove\"\n >\n <IconX className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </motion.div>\n )\n}\n\nfunction UploadToastContent({\n files,\n onRetry,\n onRemove,\n selfDismissId,\n}: {\n files: UploadFile[]\n onRetry?: (fileId: string) => void\n onRemove?: (fileId: string) => void\n selfDismissId?: string | number\n}) {\n const [hovered, setHovered] = React.useState(false)\n const [focused, setFocused] = React.useState(false)\n const paused = hovered || focused\n const completeCount = files.filter((f) => f.status === 'complete').length\n const errorCount = files.filter((f) => f.status === 'error').length\n const allTerminal = files.every(\n (f) => f.status === 'complete' || f.status === 'error',\n )\n\n // Self-managed dismiss timer that pauses on hover or keyboard focus\n const remainingRef = React.useRef(UPLOAD_COMPLETE_DELAY)\n const startTimeRef = React.useRef(0)\n\n React.useEffect(() => {\n if (selfDismissId == null || !allTerminal) return\n if (paused) {\n const elapsed = Date.now() - startTimeRef.current\n remainingRef.current = Math.max(0, remainingRef.current - elapsed)\n return\n }\n startTimeRef.current = Date.now()\n const timer = setTimeout(() => {\n sonnerToast.dismiss(selfDismissId)\n }, remainingRef.current)\n return () => clearTimeout(timer)\n }, [selfDismissId, allTerminal, paused])\n\n const accentClass = allTerminal\n ? errorCount > 0\n ? 'bg-error-7'\n : 'bg-success-7'\n : 'bg-accent-9'\n\n const timerBarType: ToastType = allTerminal\n ? errorCount > 0\n ? 'error'\n : 'success'\n : 'loading'\n\n const uploadUrgent = allTerminal && errorCount > 0\n return (\n <motion.div\n layout\n role={uploadUrgent ? 'alert' : 'status'}\n aria-live={uploadUrgent ? 'assertive' : 'polite'}\n aria-label=\"File uploads\"\n className=\"group relative flex w-full overflow-hidden rounded-overlay-sm border border-surface-border-strong bg-surface-overlay shadow-floating\"\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onFocusCapture={() => setFocused(true)}\n onBlurCapture={() => setFocused(false)}\n transition={springs.smooth}\n >\n {/* Left accent bar */}\n <div className={cn('w-1 shrink-0 rounded-l-overlay-sm', accentClass)} />\n\n {/* Content */}\n <div className=\"min-w-0 flex-1 p-ds-04\">\n {/* Header — icon morphs between upload ↔ result */}\n <div className=\"flex items-center gap-ds-02\">\n <AnimatePresence mode=\"wait\">\n {allTerminal ? (\n errorCount > 0 ? (\n <motion.div\n key=\"error-icon\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconAlertCircle} size=\"sm\" className=\"shrink-0 text-error-11\" />\n </motion.div>\n ) : (\n <motion.div\n key=\"success-icon\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconCheck} size=\"sm\" className=\"shrink-0 text-success-11\" />\n </motion.div>\n )\n ) : (\n <motion.div\n key=\"upload-icon\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconUpload} size=\"sm\" className=\"shrink-0 text-surface-fg-muted\" />\n </motion.div>\n )}\n </AnimatePresence>\n <p className=\"text-ds-md font-semibold text-surface-fg\">\n {allTerminal\n ? errorCount > 0\n ? `${completeCount} of ${files.length} uploaded`\n : `${files.length} file${files.length > 1 ? 's' : ''} uploaded`\n : `Uploading ${files.length} file${files.length > 1 ? 's' : ''}`}\n </p>\n </div>\n {!allTerminal && (\n <p className=\"mt-0.5 text-ds-xs text-surface-fg-muted\">\n {completeCount} of {files.length} complete\n {errorCount > 0 && ` · ${errorCount} failed`}\n </p>\n )}\n\n {/* File list */}\n <div className=\"mt-ds-02 max-h-[140px] overflow-y-auto\">\n <AnimatePresence>\n {files.map((file) => (\n <UploadFileRow\n key={file.id}\n file={file}\n onRetry={onRetry}\n onRemove={onRemove}\n />\n ))}\n </AnimatePresence>\n </div>\n\n {/* Screen reader announcement */}\n <div className=\"sr-only\" aria-live=\"polite\">\n {completeCount} of {files.length} files uploaded\n {errorCount > 0 && `, ${errorCount} failed`}\n </div>\n </div>\n\n {/* Timer bar — only after all terminal */}\n {allTerminal && (\n <TimerBar\n duration={UPLOAD_COMPLETE_DELAY}\n type={timerBarType}\n paused={false}\n />\n )}\n </motion.div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Toast API — wraps Sonner with our styled UI\n * ------------------------------------------------------------------------ */\n\nfunction createSonnerOptions(\n options?: ToastOptions,\n duration?: number,\n): ExternalToast {\n return {\n id: options?.id,\n duration: options?.duration ?? duration ?? DEFAULT_DURATION,\n unstyled: true,\n classNames: { toast: 'w-full' },\n }\n}\n\nfunction createTypedToast(\n type: ToastType,\n message: string | React.ReactNode,\n options?: ToastOptions,\n) {\n assertToasterMounted()\n const duration = options?.duration ?? DEFAULT_DURATION\n return sonnerToast.custom(\n () => (\n <ToastContent\n type={type}\n title={message}\n description={options?.description}\n action={options?.action}\n cancel={options?.cancel}\n duration={duration}\n />\n ),\n createSonnerOptions(options, duration),\n )\n}\n\n/**\n * Toast API — imperative toast notifications with Sonner rendering engine.\n *\n * Mount `<Toaster />` once at your root layout. Then call `toast()` from anywhere.\n *\n * @example\n * toast('Something happened')\n * toast.success('Saved!')\n * toast.error('Failed to save')\n * toast.promise(saveFn(), { loading: 'Saving...', success: 'Done!', error: 'Failed' })\n * toast.undo('Deleted', { onUndo: () => restore() })\n * toast.upload({ files, onRetry, onRemove })\n */\nfunction toast(message: string | React.ReactNode, options?: ToastOptions) {\n return createTypedToast('message', message, options)\n}\n\ntoast.message = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('message', message, options)\n}\n\ntoast.success = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('success', message, options)\n}\n\ntoast.error = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('error', message, options)\n}\n\ntoast.warning = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('warning', message, options)\n}\n\ntoast.info = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('info', message, options)\n}\n\ntoast.loading = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('loading', message, {\n ...options,\n duration: options?.duration ?? Infinity,\n })\n}\n\ntoast.promise = <T,>(\n promise: Promise<T>,\n options: {\n loading: string | React.ReactNode\n success: string | React.ReactNode | ((data: T) => string | React.ReactNode)\n error:\n | string\n | React.ReactNode\n | ((error: unknown) => string | React.ReactNode)\n },\n) => {\n const id = sonnerToast.custom(\n () => (\n <ToastContent type=\"loading\" title={options.loading} showTimerBar={false} />\n ),\n { duration: Infinity, unstyled: true, classNames: { toast: 'w-full' } },\n )\n\n const replaceWith = (type: ToastType, message: React.ReactNode) => {\n // Sonner doesn't reset its auto-dismiss timer when updating a custom toast\n // in-place from duration: Infinity, so ToastContent manages its own\n // hover-aware dismiss timer via selfDismissId.\n sonnerToast.custom(\n () => (\n <ToastContent\n type={type}\n title={message}\n duration={DEFAULT_DURATION}\n selfDismissId={id}\n />\n ),\n { id, duration: Infinity, unstyled: true, classNames: { toast: 'w-full' } },\n )\n }\n\n promise\n .then((data) => {\n const message =\n typeof options.success === 'function'\n ? options.success(data)\n : options.success\n replaceWith('success', message)\n })\n .catch((error) => {\n const message =\n typeof options.error === 'function'\n ? options.error(error)\n : options.error\n replaceWith('error', message)\n })\n\n return id\n}\n\ntoast.undo = (message: string | React.ReactNode, options: ToastUndoOptions) => {\n const duration = options.duration ?? UNDO_DURATION\n return sonnerToast.custom(\n () => (\n <ToastContent\n type=\"message\"\n title={message}\n description={options.description}\n action={{ label: 'Undo', onClick: options.onUndo }}\n duration={duration}\n />\n ),\n { duration, unstyled: true, classNames: { toast: 'w-full' } },\n )\n}\n\ntoast.upload = (options: ToastUploadOptions) => {\n // Pre-determine the id so we can pass it to UploadToastContent for self-dismiss.\n const toastId = options.id ?? String(Date.now())\n\n sonnerToast.custom(\n () => (\n <UploadToastContent\n files={options.files}\n onRetry={options.onRetry}\n onRemove={options.onRemove}\n selfDismissId={toastId}\n />\n ),\n {\n id: toastId,\n duration: Infinity,\n unstyled: true,\n classNames: { toast: 'w-full' },\n },\n )\n\n return toastId\n}\n\ntoast.custom = (\n render: (id: string | number) => React.ReactElement,\n options?: ExternalToast,\n) => {\n return sonnerToast.custom(render, {\n ...options,\n unstyled: true,\n classNames: { toast: 'w-full', ...(options?.classNames as Record<string, string>) },\n })\n}\n\ntoast.dismiss = (id?: string | number) => {\n if (id !== undefined) {\n sonnerToast.dismiss(id)\n } else {\n sonnerToast.dismiss()\n }\n}\n\n/* ---------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------ */\n\nexport type ToastProps = ToastOptions\n\nexport {\n formatFileSize,\n TimerBar,\n toast,\n ToastContent,\n UploadFileRow,\n UploadToastContent,\n}\nexport type {\n ToastActionOptions,\n ToastOptions,\n ToastType,\n ToastUndoOptions,\n ToastUploadOptions,\n UploadFile,\n} from './toast-types'\n"],"mappings":"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"}
package/dist/ui/toggle.js CHANGED
@@ -7,7 +7,7 @@ import * as a from "react";
7
7
  import { jsx as o } from "react/jsx-runtime";
8
8
  import { motion as s } from "framer-motion";
9
9
  //#region src/ui/toggle.tsx
10
- var c = s.create(e), l = r("inline-flex items-center justify-center gap-ds-03 rounded-ds-md font-medium transition-colors duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-action-disabled", {
10
+ var c = s.create(e), l = r("inline-flex items-center justify-center gap-ds-03 rounded-control font-medium transition-colors duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-action-disabled", {
11
11
  variants: {
12
12
  variant: {
13
13
  default: "bg-transparent hover:bg-surface-raised-hover text-surface-fg-muted",
@@ -1 +1 @@
1
- {"version":3,"file":"toggle.js","names":[],"sources":["../../src/ui/toggle.tsx"],"sourcesContent":["'use client'\n\nimport * as TogglePrimitive from '@primitives/react-toggle'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { motionProps,springs } from './lib/motion'\nimport { cn } from './lib/utils'\n\nconst MotionToggleRoot = motion.create(TogglePrimitive.Root)\n\nconst toggleVariants = cva(\n 'inline-flex items-center justify-center gap-ds-03 rounded-ds-md font-medium transition-colors duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-action-disabled',\n {\n variants: {\n variant: {\n default:\n 'bg-transparent hover:bg-surface-raised-hover text-surface-fg-muted',\n outline:\n 'border border-surface-border-strong bg-transparent hover:bg-surface-raised-hover hover:border-surface-border-strong',\n },\n size: {\n sm: 'h-ds-sm px-ds-03 text-ds-sm',\n md: 'h-ds-md px-ds-04 text-ds-md',\n lg: 'h-ds-lg px-ds-05 text-ds-base',\n },\n color: {\n accent: 'data-[state=on]:bg-accent-2 data-[state=on]:text-accent-11',\n error: 'data-[state=on]:bg-error-2 data-[state=on]:text-error-11',\n success: 'data-[state=on]:bg-success-2 data-[state=on]:text-success-11',\n neutral: 'data-[state=on]:bg-surface-raised-hover data-[state=on]:text-surface-fg',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'md',\n color: 'accent',\n },\n },\n)\n\nconst Toggle = React.forwardRef<\n React.ElementRef<typeof TogglePrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &\n VariantProps<typeof toggleVariants>\n>(({ className, variant, size, color, ...props }, ref) => (\n <MotionToggleRoot\n ref={ref}\n whileTap={{ scale: 0.95 }}\n transition={springs.snappy}\n className={cn(toggleVariants({ variant, size, color }), className)}\n {...motionProps(props)}\n />\n))\nToggle.displayName = TogglePrimitive.Root.displayName\n\nexport type ToggleProps = React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &\n VariantProps<typeof toggleVariants>\n\nexport { Toggle, toggleVariants }\n"],"mappings":";;;;;;;;;AAUA,IAAM,IAAmB,EAAO,OAAO,EAAqB,EAEtD,IAAiB,EACrB,mTACA;CACE,UAAU;EACR,SAAS;GACP,SACE;GACF,SACE;GACH;EACD,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL;EACD,OAAO;GACL,QAAQ;GACR,OAAO;GACP,SAAS;GACT,SAAS;GACV;EACF;CACD,iBAAiB;EACf,SAAS;EACT,MAAM;EACN,OAAO;EACR;CACF,CACF,EAEK,IAAS,EAAM,YAIlB,EAAE,cAAW,YAAS,SAAM,UAAO,GAAG,KAAS,MAChD,kBAAC,GAAD;CACO;CACL,UAAU,EAAE,OAAO,KAAM;CACzB,YAAY,EAAQ;CACpB,WAAW,EAAG,EAAe;EAAE;EAAS;EAAM;EAAO,CAAC,EAAE,EAAU;CAClE,GAAI,EAAY,EAAM;CACtB,CAAA,CACF;AACF,EAAO,cAAA,EAAmC"}
1
+ {"version":3,"file":"toggle.js","names":[],"sources":["../../src/ui/toggle.tsx"],"sourcesContent":["'use client'\n\nimport * as TogglePrimitive from '@primitives/react-toggle'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { motionProps,springs } from './lib/motion'\nimport { cn } from './lib/utils'\n\nconst MotionToggleRoot = motion.create(TogglePrimitive.Root)\n\nconst toggleVariants = cva(\n 'inline-flex items-center justify-center gap-ds-03 rounded-control font-medium transition-colors duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-action-disabled',\n {\n variants: {\n variant: {\n default:\n 'bg-transparent hover:bg-surface-raised-hover text-surface-fg-muted',\n outline:\n 'border border-surface-border-strong bg-transparent hover:bg-surface-raised-hover hover:border-surface-border-strong',\n },\n size: {\n sm: 'h-ds-sm px-ds-03 text-ds-sm',\n md: 'h-ds-md px-ds-04 text-ds-md',\n lg: 'h-ds-lg px-ds-05 text-ds-base',\n },\n color: {\n accent: 'data-[state=on]:bg-accent-2 data-[state=on]:text-accent-11',\n error: 'data-[state=on]:bg-error-2 data-[state=on]:text-error-11',\n success: 'data-[state=on]:bg-success-2 data-[state=on]:text-success-11',\n neutral: 'data-[state=on]:bg-surface-raised-hover data-[state=on]:text-surface-fg',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'md',\n color: 'accent',\n },\n },\n)\n\nconst Toggle = React.forwardRef<\n React.ElementRef<typeof TogglePrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &\n VariantProps<typeof toggleVariants>\n>(({ className, variant, size, color, ...props }, ref) => (\n <MotionToggleRoot\n ref={ref}\n whileTap={{ scale: 0.95 }}\n transition={springs.snappy}\n className={cn(toggleVariants({ variant, size, color }), className)}\n {...motionProps(props)}\n />\n))\nToggle.displayName = TogglePrimitive.Root.displayName\n\nexport type ToggleProps = React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &\n VariantProps<typeof toggleVariants>\n\nexport { Toggle, toggleVariants }\n"],"mappings":";;;;;;;;;AAUA,IAAM,IAAmB,EAAO,OAAO,EAAqB,EAEtD,IAAiB,EACrB,qTACA;CACE,UAAU;EACR,SAAS;GACP,SACE;GACF,SACE;GACH;EACD,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL;EACD,OAAO;GACL,QAAQ;GACR,OAAO;GACP,SAAS;GACT,SAAS;GACV;EACF;CACD,iBAAiB;EACf,SAAS;EACT,MAAM;EACN,OAAO;EACR;CACF,CACF,EAEK,IAAS,EAAM,YAIlB,EAAE,cAAW,YAAS,SAAM,UAAO,GAAG,KAAS,MAChD,kBAAC,GAAD;CACO;CACL,UAAU,EAAE,OAAO,KAAM;CACzB,YAAY,EAAQ;CACpB,WAAW,EAAG,EAAe;EAAE;EAAS;EAAM;EAAO,CAAC,EAAE,EAAU;CAClE,GAAI,EAAY,EAAM;CACtB,CAAA,CACF;AACF,EAAO,cAAA,EAAmC"}
@@ -73,7 +73,7 @@ var v = n, y = {
73
73
  ...a.snappy,
74
74
  opacity: o.fade
75
75
  },
76
- className: s("z-tooltip overflow-hidden rounded-ds-md bg-surface-inverted px-ds-04 py-ds-02b text-ds-sm text-surface-inverted-fg shadow-floating", t),
76
+ className: s("z-tooltip overflow-hidden rounded-overlay-sm bg-surface-inverted px-ds-04 py-ds-02b text-ds-sm text-surface-inverted-fg shadow-floating", t),
77
77
  children: l
78
78
  })
79
79
  })
@@ -1 +1 @@
1
- {"version":3,"file":"tooltip.js","names":[],"sources":["../../src/ui/tooltip.tsx"],"sourcesContent":["'use client'\n\nimport * as TooltipPrimitive from '@primitives/react-tooltip'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\n\n// ── Auto-provider: wraps with TooltipPrimitive.Provider if none exists ──\n\nconst TooltipProviderContext = React.createContext(false)\n\nconst TooltipProvider: React.FC<React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Provider>> = ({\n children,\n ...props\n}) => (\n <TooltipProviderContext.Provider value={true}>\n <TooltipPrimitive.Provider {...props}>\n {children}\n </TooltipPrimitive.Provider>\n </TooltipProviderContext.Provider>\n)\n\nfunction AutoProvider({ children }: { children: React.ReactNode }) {\n const hasProvider = React.useContext(TooltipProviderContext)\n if (hasProvider) return <>{children}</>\n return (\n <TooltipProviderContext.Provider value={true}>\n <TooltipPrimitive.Provider delayDuration={300}>\n {children}\n </TooltipPrimitive.Provider>\n </TooltipProviderContext.Provider>\n )\n}\n\n// ── Internal context to thread `open` state to animated children ──\n\ntype TooltipContextValue = { open: boolean }\nconst TooltipContext = React.createContext<TooltipContextValue>({ open: false })\n\nconst Tooltip: React.FC<React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Root>> = ({\n open: openProp,\n defaultOpen,\n onOpenChange,\n ...props\n}) => {\n const [internalOpen, setInternalOpen] = React.useState(defaultOpen ?? false)\n const isControlled = openProp !== undefined\n const open = isControlled ? openProp : internalOpen\n\n const handleOpenChange = React.useCallback(\n (nextOpen: boolean) => {\n if (!isControlled) setInternalOpen(nextOpen)\n onOpenChange?.(nextOpen)\n },\n [isControlled, onOpenChange],\n )\n\n const contextValue = React.useMemo(() => ({ open }), [open])\n\n return (\n <AutoProvider>\n <TooltipContext.Provider value={contextValue}>\n <TooltipPrimitive.Root open={open} onOpenChange={handleOpenChange} {...props} />\n </TooltipContext.Provider>\n </AutoProvider>\n )\n}\nTooltip.displayName = 'Tooltip'\n\nconst TooltipTrigger = TooltipPrimitive.Trigger\n\n// ── Side-based slide offsets ──\n\nconst sideOffset: Record<string, { x?: number; y?: number }> = {\n top: { y: 4 },\n bottom: { y: -4 },\n left: { x: 4 },\n right: { x: -4 },\n}\n\nconst TooltipContent = React.forwardRef<\n React.ElementRef<typeof TooltipPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset: sideOffsetProp = 4, side = 'top', children, ...props }, ref) => {\n const { open } = React.useContext(TooltipContext)\n const slideInit = sideOffset[side] ?? {}\n\n return (\n <AnimatePresence>\n {open && (\n <TooltipPrimitive.Portal forceMount>\n <TooltipPrimitive.Content\n ref={ref}\n forceMount\n sideOffset={sideOffsetProp}\n side={side}\n {...props}\n asChild\n >\n <motion.div\n initial={{ opacity: 0, scale: 0.95, ...slideInit }}\n animate={{ opacity: 1, scale: 1, x: 0, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, ...slideInit }}\n transition={{ ...springs.snappy, opacity: tweens.fade }}\n className={cn(\n 'z-tooltip overflow-hidden rounded-ds-md bg-surface-inverted px-ds-04 py-ds-02b text-ds-sm text-surface-inverted-fg shadow-floating',\n className,\n )}\n >\n {children}\n </motion.div>\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n )}\n </AnimatePresence>\n )\n})\nTooltipContent.displayName = TooltipPrimitive.Content.displayName\n\nexport type TooltipContentProps = React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n\nexport { Tooltip, TooltipContent, TooltipProvider,TooltipTrigger }\n"],"mappings":";;;;;;;;AAWA,IAAM,IAAyB,EAAM,cAAc,GAAM,EAEnD,KAA+F,EACnG,aACA,GAAG,QAEH,kBAAC,EAAuB,UAAxB;CAAiC,OAAO;WACtC,kBAAC,GAAD;EAA2B,GAAI;EAC5B;EACyB,CAAA;CACI,CAAA;AAGpC,SAAS,EAAa,EAAE,eAA2C;AAGjE,QAFoB,EAAM,WAAW,EAAuB,GACpC,kBAAA,GAAA,EAAG,aAAY,CAAA,GAErC,kBAAC,EAAuB,UAAxB;EAAiC,OAAO;YACtC,kBAAC,GAAD;GAA2B,eAAe;GACvC;GACyB,CAAA;EACI,CAAA;;AAOtC,IAAM,IAAiB,EAAM,cAAmC,EAAE,MAAM,IAAO,CAAC,EAE1E,KAAmF,EACvF,MAAM,GACN,gBACA,iBACA,GAAG,QACC;CACJ,IAAM,CAAC,GAAc,KAAmB,EAAM,SAAS,KAAe,GAAM,EACtE,IAAe,MAAa,KAAA,GAC5B,IAAO,IAAe,IAAW,GAEjC,IAAmB,EAAM,aAC5B,MAAsB;AAErB,EADK,KAAc,EAAgB,EAAS,EAC5C,IAAe,EAAS;IAE1B,CAAC,GAAc,EAAa,CAC7B,EAEK,IAAe,EAAM,eAAe,EAAE,SAAM,GAAG,CAAC,EAAK,CAAC;AAE5D,QACE,kBAAC,GAAD,EAAA,UACE,kBAAC,EAAe,UAAhB;EAAyB,OAAO;YAC9B,kBAAC,GAAD;GAA6B;GAAM,cAAc;GAAkB,GAAI;GAAS,CAAA;EACxD,CAAA,EACb,CAAA;;AAGnB,EAAQ,cAAc;AAEtB,IAAM,IAAiB,GAIjB,IAAyD;CAC7D,KAAK,EAAE,GAAG,GAAG;CACb,QAAQ,EAAE,GAAG,IAAI;CACjB,MAAM,EAAE,GAAG,GAAG;CACd,OAAO,EAAE,GAAG,IAAI;CACjB,EAEK,IAAiB,EAAM,YAG1B,EAAE,cAAW,YAAY,IAAiB,GAAG,UAAO,OAAO,aAAU,GAAG,KAAS,MAAQ;CAC1F,IAAM,EAAE,YAAS,EAAM,WAAW,EAAe,EAC3C,IAAY,EAAW,MAAS,EAAE;AAExC,QACE,kBAAC,GAAD,EAAA,UACG,KACC,kBAAC,GAAD;EAAyB,YAAA;YACvB,kBAAC,GAAD;GACO;GACL,YAAA;GACA,YAAY;GACN;GACN,GAAI;GACJ,SAAA;aAEA,kBAAC,EAAO,KAAR;IACE,SAAS;KAAE,SAAS;KAAG,OAAO;KAAM,GAAG;KAAW;IAClD,SAAS;KAAE,SAAS;KAAG,OAAO;KAAG,GAAG;KAAG,GAAG;KAAG;IAC7C,MAAM;KAAE,SAAS;KAAG,OAAO;KAAM,GAAG;KAAW;IAC/C,YAAY;KAAE,GAAG,EAAQ;KAAQ,SAAS,EAAO;KAAM;IACvD,WAAW,EACT,sIACA,EACD;IAEA;IACU,CAAA;GACY,CAAA;EACH,CAAA,EAEZ,CAAA;EAEpB;AACF,EAAe,cAAA,EAAuC"}
1
+ {"version":3,"file":"tooltip.js","names":[],"sources":["../../src/ui/tooltip.tsx"],"sourcesContent":["'use client'\n\nimport * as TooltipPrimitive from '@primitives/react-tooltip'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\n\n// ── Auto-provider: wraps with TooltipPrimitive.Provider if none exists ──\n\nconst TooltipProviderContext = React.createContext(false)\n\nconst TooltipProvider: React.FC<React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Provider>> = ({\n children,\n ...props\n}) => (\n <TooltipProviderContext.Provider value={true}>\n <TooltipPrimitive.Provider {...props}>\n {children}\n </TooltipPrimitive.Provider>\n </TooltipProviderContext.Provider>\n)\n\nfunction AutoProvider({ children }: { children: React.ReactNode }) {\n const hasProvider = React.useContext(TooltipProviderContext)\n if (hasProvider) return <>{children}</>\n return (\n <TooltipProviderContext.Provider value={true}>\n <TooltipPrimitive.Provider delayDuration={300}>\n {children}\n </TooltipPrimitive.Provider>\n </TooltipProviderContext.Provider>\n )\n}\n\n// ── Internal context to thread `open` state to animated children ──\n\ntype TooltipContextValue = { open: boolean }\nconst TooltipContext = React.createContext<TooltipContextValue>({ open: false })\n\nconst Tooltip: React.FC<React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Root>> = ({\n open: openProp,\n defaultOpen,\n onOpenChange,\n ...props\n}) => {\n const [internalOpen, setInternalOpen] = React.useState(defaultOpen ?? false)\n const isControlled = openProp !== undefined\n const open = isControlled ? openProp : internalOpen\n\n const handleOpenChange = React.useCallback(\n (nextOpen: boolean) => {\n if (!isControlled) setInternalOpen(nextOpen)\n onOpenChange?.(nextOpen)\n },\n [isControlled, onOpenChange],\n )\n\n const contextValue = React.useMemo(() => ({ open }), [open])\n\n return (\n <AutoProvider>\n <TooltipContext.Provider value={contextValue}>\n <TooltipPrimitive.Root open={open} onOpenChange={handleOpenChange} {...props} />\n </TooltipContext.Provider>\n </AutoProvider>\n )\n}\nTooltip.displayName = 'Tooltip'\n\nconst TooltipTrigger = TooltipPrimitive.Trigger\n\n// ── Side-based slide offsets ──\n\nconst sideOffset: Record<string, { x?: number; y?: number }> = {\n top: { y: 4 },\n bottom: { y: -4 },\n left: { x: 4 },\n right: { x: -4 },\n}\n\nconst TooltipContent = React.forwardRef<\n React.ElementRef<typeof TooltipPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset: sideOffsetProp = 4, side = 'top', children, ...props }, ref) => {\n const { open } = React.useContext(TooltipContext)\n const slideInit = sideOffset[side] ?? {}\n\n return (\n <AnimatePresence>\n {open && (\n <TooltipPrimitive.Portal forceMount>\n <TooltipPrimitive.Content\n ref={ref}\n forceMount\n sideOffset={sideOffsetProp}\n side={side}\n {...props}\n asChild\n >\n <motion.div\n initial={{ opacity: 0, scale: 0.95, ...slideInit }}\n animate={{ opacity: 1, scale: 1, x: 0, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, ...slideInit }}\n transition={{ ...springs.snappy, opacity: tweens.fade }}\n className={cn(\n 'z-tooltip overflow-hidden rounded-overlay-sm bg-surface-inverted px-ds-04 py-ds-02b text-ds-sm text-surface-inverted-fg shadow-floating',\n className,\n )}\n >\n {children}\n </motion.div>\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n )}\n </AnimatePresence>\n )\n})\nTooltipContent.displayName = TooltipPrimitive.Content.displayName\n\nexport type TooltipContentProps = React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n\nexport { Tooltip, TooltipContent, TooltipProvider,TooltipTrigger }\n"],"mappings":";;;;;;;;AAWA,IAAM,IAAyB,EAAM,cAAc,GAAM,EAEnD,KAA+F,EACnG,aACA,GAAG,QAEH,kBAAC,EAAuB,UAAxB;CAAiC,OAAO;WACtC,kBAAC,GAAD;EAA2B,GAAI;EAC5B;EACyB,CAAA;CACI,CAAA;AAGpC,SAAS,EAAa,EAAE,eAA2C;AAGjE,QAFoB,EAAM,WAAW,EAAuB,GACpC,kBAAA,GAAA,EAAG,aAAY,CAAA,GAErC,kBAAC,EAAuB,UAAxB;EAAiC,OAAO;YACtC,kBAAC,GAAD;GAA2B,eAAe;GACvC;GACyB,CAAA;EACI,CAAA;;AAOtC,IAAM,IAAiB,EAAM,cAAmC,EAAE,MAAM,IAAO,CAAC,EAE1E,KAAmF,EACvF,MAAM,GACN,gBACA,iBACA,GAAG,QACC;CACJ,IAAM,CAAC,GAAc,KAAmB,EAAM,SAAS,KAAe,GAAM,EACtE,IAAe,MAAa,KAAA,GAC5B,IAAO,IAAe,IAAW,GAEjC,IAAmB,EAAM,aAC5B,MAAsB;AAErB,EADK,KAAc,EAAgB,EAAS,EAC5C,IAAe,EAAS;IAE1B,CAAC,GAAc,EAAa,CAC7B,EAEK,IAAe,EAAM,eAAe,EAAE,SAAM,GAAG,CAAC,EAAK,CAAC;AAE5D,QACE,kBAAC,GAAD,EAAA,UACE,kBAAC,EAAe,UAAhB;EAAyB,OAAO;YAC9B,kBAAC,GAAD;GAA6B;GAAM,cAAc;GAAkB,GAAI;GAAS,CAAA;EACxD,CAAA,EACb,CAAA;;AAGnB,EAAQ,cAAc;AAEtB,IAAM,IAAiB,GAIjB,IAAyD;CAC7D,KAAK,EAAE,GAAG,GAAG;CACb,QAAQ,EAAE,GAAG,IAAI;CACjB,MAAM,EAAE,GAAG,GAAG;CACd,OAAO,EAAE,GAAG,IAAI;CACjB,EAEK,IAAiB,EAAM,YAG1B,EAAE,cAAW,YAAY,IAAiB,GAAG,UAAO,OAAO,aAAU,GAAG,KAAS,MAAQ;CAC1F,IAAM,EAAE,YAAS,EAAM,WAAW,EAAe,EAC3C,IAAY,EAAW,MAAS,EAAE;AAExC,QACE,kBAAC,GAAD,EAAA,UACG,KACC,kBAAC,GAAD;EAAyB,YAAA;YACvB,kBAAC,GAAD;GACO;GACL,YAAA;GACA,YAAY;GACN;GACN,GAAI;GACJ,SAAA;aAEA,kBAAC,EAAO,KAAR;IACE,SAAS;KAAE,SAAS;KAAG,OAAO;KAAM,GAAG;KAAW;IAClD,SAAS;KAAE,SAAS;KAAG,OAAO;KAAG,GAAG;KAAG,GAAG;KAAG;IAC7C,MAAM;KAAE,SAAS;KAAG,OAAO;KAAM,GAAG;KAAW;IAC/C,YAAY;KAAE,GAAG,EAAQ;KAAQ,SAAS,EAAO;KAAM;IACvD,WAAW,EACT,2IACA,EACD;IAEA;IACU,CAAA;GACY,CAAA;EACH,CAAA,EAEZ,CAAA;EAEpB;AACF,EAAe,cAAA,EAAuC"}
@@ -67,7 +67,8 @@ transpilePackages: ['@devalok/shilp-sutra', '@devalok/shilp-sutra-brand'],
67
67
  | `--leading-ds-*` | `leading-ds-tight`, `leading-ds-normal` | ds-namespaced |
68
68
  | `--tracking-*` | `tracking-tight`, `tracking-normal` | standard TW namespace |
69
69
  | `--font-*`, `--font-weight-*` | `font-sans`, `font-semibold` | standard TW namespace |
70
- | `--radius`, `--radius-ds-*` | `rounded` (bare), `rounded-ds-lg` | unprefixed + ds-namespaced |
70
+ | `--radius`, `--radius-ds-*` | `rounded` (bare), `rounded-ds-lg` | unprefixed + ds-namespaced primitive scale |
71
+ | `--radius-control`, `--radius-surface`, `--radius-overlay-*`, `--radius-pill`, `--radius-bubble` | `rounded-control`, `rounded-surface`, `rounded-overlay`, `rounded-pill`, `rounded-bubble` | semantic roles (PUBLIC) — components reference these, `[data-shape]` presets remap them |
71
72
  | `--shadow-*` | `shadow-raised`, `shadow-overlay` | semantic names only; bare `shadow` is NOT generated (TW4 has no default scale and we don't define `--shadow`) |
72
73
  | `--ease-*` | `ease-productive-standard` | semantic names |
73
74
  | `--breakpoint-*` | `md:`, `lg:` | standard TW namespace |
@@ -105,6 +106,94 @@ See `MIGRATION.md#v0370--tailwind-4-css-first-migration` (root of this package).
105
106
 
106
107
  ---
107
108
 
109
+ ## Shape Presets & Radius Roles (v0.39+)
110
+
111
+ Radius has TWO layers:
112
+
113
+ 1. **Primitive scale** — `--radius-ds-sm/md/lg/xl/2xl/full` (private; internal building blocks).
114
+ 2. **Semantic roles** — `--radius-control`, `--radius-control-inner`, `--radius-surface`, `--radius-overlay-sm/md/lg`, `--radius-pill`, `--radius-bubble`. Components reference roles. Consumers customize these.
115
+
116
+ ### Role token reference
117
+
118
+ | Token | Default (px) | Utility class | Used by |
119
+ |--------------------------|--------------|--------------------------|---------|
120
+ | `--radius-control` | 6 | `rounded-control` | Button, Input, Select, Combobox, Autocomplete, NumberInput, Toggle, ToggleGroup, Tabs trigger (contained), menu items, sidebar menu button, stepper, pagination, code block, InputOTP slot |
121
+ | `--radius-control-inner` | 2 | `rounded-control-inner` | Checkbox box, +/− buttons, close X buttons, focus rings on text links, small badges, color preset swatches, skeleton text bars, inline Code |
122
+ | `--radius-surface` | 10 | `rounded-surface` | Card, StatCard, DataTableCard, Alert, Accordion trigger, FileUpload dropzone, Tabs list (contained) |
123
+ | `--radius-overlay-sm` | 6 | `rounded-overlay-sm` | Tooltip, Toast, charts internal tooltip |
124
+ | `--radius-overlay` | 10 | `rounded-overlay` | Popover, HoverCard, DropdownMenu / ContextMenu / Menubar content, Select / Combobox / Autocomplete listbox, NavigationMenu viewport, DataTable bulk-actions toolbar, SplitButton dropdown |
125
+ | `--radius-overlay-lg` | 16 | `rounded-overlay-lg` | Dialog, AlertDialog, Sheet (top corners), BottomSheet, ColorInput picker panel, ChatMessageInput wrapper, Sidebar inset variant |
126
+ | `--radius-pill` | 9999 | `rounded-pill` | Badge, BadgeIndicator, StatusDot, Radio, Switch, Slider, Progress, Avatar circle, SegmentedControl item, ChatMessage reaction button, drag handles |
127
+ | `--radius-bubble` | 24 | `rounded-bubble` | ChatMessage bubble |
128
+
129
+ Bare `rounded-ds-*` and `rounded-full` are BANNED in components (enforced by pre-publish audit gate). Token-showcase stories (`forced-colors.stories.tsx`, `FoundationsShowcase.tsx`) are explicitly allowlisted.
130
+
131
+ ### Shape presets — `[data-shape]`
132
+
133
+ Apply to `<html>`, app root, or any subtree. Three presets ship by default:
134
+
135
+ | Preset | control / control-inner | surface | overlay-sm / overlay / overlay-lg | bubble | Feel |
136
+ |---|---|---|---|---|---|
137
+ | `sharp` | 2 / 0 | 4 | 2 / 4 / 6 | 8 | Vercel / Linear / dev-tool |
138
+ | `slightly-rounded` (default) | 6 / 2 | 10 | 6 / 10 / 16 | 24 | shadcn / Stripe / Notion sidebar |
139
+ | `rounded` | 10 / 4 | 16 | 10 / 16 / 24 | 32 | iOS / Notion content / consumer |
140
+
141
+ ```html
142
+ <!-- Whole-app preset -->
143
+ <html data-shape="sharp">
144
+
145
+ <!-- Or scoped per subtree -->
146
+ <div data-shape="rounded">...</div>
147
+ ```
148
+
149
+ `--radius-pill` stays `9999px` in every preset by design — pill shapes are shape-by-meaning, not shape-by-style.
150
+
151
+ ### Per-token consumer overrides
152
+
153
+ Override any role globally or scoped:
154
+
155
+ ```css
156
+ :root { --radius-control: 4px; } /* tighten controls only */
157
+ .checkout { --radius-control: 8px; --radius-surface: 20px; } /* per-subtree */
158
+ [data-shape="rounded"] .admin { --radius-control: 6px; } /* extend a preset */
159
+ ```
160
+
161
+ ### Custom preset
162
+
163
+ ```css
164
+ [data-shape="brand-soft"] {
165
+ --radius-control: 8px;
166
+ --radius-control-inner: 3px;
167
+ --radius-surface: 14px;
168
+ --radius-overlay-sm: 8px;
169
+ --radius-overlay: 14px;
170
+ --radius-overlay-lg: 20px;
171
+ --radius-pill: 9999px;
172
+ --radius-bubble: 28px;
173
+ }
174
+ ```
175
+
176
+ ```html
177
+ <html data-shape="brand-soft">
178
+ ```
179
+
180
+ ### Migration from `rounded-ds-*` / `rounded-full`
181
+
182
+ ```diff
183
+ - className="rounded-ds-md ..." → + className="rounded-control ..."
184
+ - className="rounded-ds-lg ..." → + className="rounded-surface ..." /* card/panel context */
185
+ - className="rounded-ds-lg ..." → + className="rounded-overlay ..." /* popover/dropdown context */
186
+ - className="rounded-ds-xl ..." → + className="rounded-overlay-lg ..." /* dialog/sheet */
187
+ - className="rounded-ds-2xl ..." → + className="rounded-bubble ..."
188
+ - className="rounded-ds-sm ..." → + className="rounded-control-inner ..."
189
+ - className="rounded-ds-full ..." → + className="rounded-pill ..."
190
+ - className="rounded-full ..." → + className="rounded-pill ..."
191
+ ```
192
+
193
+ Re-runnable codemod: `scripts/migrate-radius-roles.mjs` (dry-run by default, `--write` to apply).
194
+
195
+ ---
196
+
108
197
  ## Architecture Notes
109
198
 
110
199
  ### The Two-Axis Variant System