@devalok/shilp-sutra 0.57.0 → 0.59.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 (258) hide show
  1. package/MIGRATION.md +91 -0
  2. package/dist/_chunks/badge-group.js +75 -188
  3. package/dist/_chunks/badge-group.js.map +1 -1
  4. package/dist/_chunks/chat.js +244 -243
  5. package/dist/_chunks/chat.js.map +1 -1
  6. package/dist/_chunks/image-preview.js +71 -70
  7. package/dist/_chunks/image-preview.js.map +1 -1
  8. package/dist/_chunks/motion-preference.js +15 -0
  9. package/dist/_chunks/motion-preference.js.map +1 -0
  10. package/dist/_chunks/motion-provider.js +1 -1
  11. package/dist/_chunks/motion-provider.js.map +1 -1
  12. package/dist/_chunks/popover.js +50 -49
  13. package/dist/_chunks/popover.js.map +1 -1
  14. package/dist/_chunks/primitives-internal.js +29 -29
  15. package/dist/_chunks/primitives-internal.js.map +1 -1
  16. package/dist/_chunks/shared.js +48 -47
  17. package/dist/_chunks/shared.js.map +1 -1
  18. package/dist/_chunks/success.js +34 -34
  19. package/dist/_chunks/tree-view.js +1 -1
  20. package/dist/_chunks/tree-view.js.map +1 -1
  21. package/dist/ai/block-renderer.js +8 -8
  22. package/dist/ai/command-bar.js +65 -65
  23. package/dist/ai/command-bar.js.map +1 -1
  24. package/dist/ai/conversation.js +35 -35
  25. package/dist/ai/conversation.js.map +1 -1
  26. package/dist/ai/index.js +4 -4
  27. package/dist/composed/activity-feed.d.ts.map +1 -1
  28. package/dist/composed/activity-feed.js +108 -107
  29. package/dist/composed/activity-feed.js.map +1 -1
  30. package/dist/composed/command-palette.js +137 -137
  31. package/dist/composed/command-palette.js.map +1 -1
  32. package/dist/composed/content-card.js +1 -1
  33. package/dist/composed/content-card.js.map +1 -1
  34. package/dist/composed/date-picker/index.js +5 -5
  35. package/dist/composed/date-picker/index.js.map +1 -1
  36. package/dist/composed/empty-state.js +4 -4
  37. package/dist/composed/file-preview/image-preview.d.ts.map +1 -1
  38. package/dist/composed/file-preview/shared.d.ts.map +1 -1
  39. package/dist/composed/file-preview/video-preview.d.ts.map +1 -1
  40. package/dist/composed/file-preview.js +151 -150
  41. package/dist/composed/file-preview.js.map +1 -1
  42. package/dist/composed/filter-bar.d.ts.map +1 -1
  43. package/dist/composed/filter-bar.js +53 -52
  44. package/dist/composed/filter-bar.js.map +1 -1
  45. package/dist/composed/form-section.d.ts.map +1 -1
  46. package/dist/composed/form-section.js +38 -37
  47. package/dist/composed/form-section.js.map +1 -1
  48. package/dist/composed/master-detail.d.ts.map +1 -1
  49. package/dist/composed/master-detail.js +1 -1
  50. package/dist/composed/master-detail.js.map +1 -1
  51. package/dist/composed/multi-select-popover.d.ts.map +1 -1
  52. package/dist/composed/multi-select-popover.js +109 -108
  53. package/dist/composed/multi-select-popover.js.map +1 -1
  54. package/dist/composed/rich-chat-input/attachment-strip.d.ts.map +1 -1
  55. package/dist/composed/rich-chat-input/reply-banner.d.ts.map +1 -1
  56. package/dist/composed/rich-chat-input/schedule-send.d.ts.map +1 -1
  57. package/dist/composed/rich-chat-input.d.ts.map +1 -1
  58. package/dist/composed/rich-chat-input.js +467 -466
  59. package/dist/composed/rich-chat-input.js.map +1 -1
  60. package/dist/composed/rich-text-editor.js +3 -3
  61. package/dist/composed/rich-text-editor.js.map +1 -1
  62. package/dist/composed/schedule-view.js +2 -2
  63. package/dist/composed/schedule-view.js.map +1 -1
  64. package/dist/composed/status-badge.d.ts.map +1 -1
  65. package/dist/composed/status-badge.js +29 -28
  66. package/dist/composed/status-badge.js.map +1 -1
  67. package/dist/motion/index.d.ts +1 -0
  68. package/dist/motion/index.d.ts.map +1 -1
  69. package/dist/motion/index.js +2 -15
  70. package/dist/motion/motion-preference.d.ts +36 -0
  71. package/dist/motion/motion-preference.d.ts.map +1 -0
  72. package/dist/motion/motion-provider.d.ts +2 -1
  73. package/dist/motion/motion-provider.d.ts.map +1 -1
  74. package/dist/motion/primitives-index.js +103 -102
  75. package/dist/motion/primitives-index.js.map +1 -1
  76. package/dist/motion/primitives.d.ts.map +1 -1
  77. package/dist/shell/bottom-navbar.js +19 -19
  78. package/dist/shell/bottom-navbar.js.map +1 -1
  79. package/dist/shell/index.d.ts +1 -1
  80. package/dist/shell/index.d.ts.map +1 -1
  81. package/dist/shell/notification-center.d.ts +17 -0
  82. package/dist/shell/notification-center.d.ts.map +1 -1
  83. package/dist/shell/notification-center.js +109 -104
  84. package/dist/shell/notification-center.js.map +1 -1
  85. package/dist/shell/notification-preferences.js +1 -1
  86. package/dist/shell/notification-preferences.js.map +1 -1
  87. package/dist/shell/top-bar.js +1 -1
  88. package/dist/shell/top-bar.js.map +1 -1
  89. package/dist/tokens/index.css +1 -0
  90. package/dist/tokens/palettes.css +292 -0
  91. package/dist/tokens/semantic.css +72 -8
  92. package/dist/tokens/shilp-sutra.css +1 -0
  93. package/dist/tokens/utilities.css +10 -7
  94. package/dist/ui/accordion.d.ts.map +1 -1
  95. package/dist/ui/accordion.js +27 -26
  96. package/dist/ui/accordion.js.map +1 -1
  97. package/dist/ui/alert-dialog.d.ts.map +1 -1
  98. package/dist/ui/alert-dialog.js +60 -59
  99. package/dist/ui/alert-dialog.js.map +1 -1
  100. package/dist/ui/alert.d.ts.map +1 -1
  101. package/dist/ui/alert.js +55 -113
  102. package/dist/ui/alert.js.map +1 -1
  103. package/dist/ui/autocomplete.js +80 -80
  104. package/dist/ui/autocomplete.js.map +1 -1
  105. package/dist/ui/avatar.d.ts.map +1 -1
  106. package/dist/ui/avatar.js +69 -68
  107. package/dist/ui/avatar.js.map +1 -1
  108. package/dist/ui/badge-indicator.d.ts +4 -8
  109. package/dist/ui/badge-indicator.d.ts.map +1 -1
  110. package/dist/ui/badge-indicator.js +3 -8
  111. package/dist/ui/badge-indicator.js.map +1 -1
  112. package/dist/ui/badge.d.ts +8 -1
  113. package/dist/ui/badge.d.ts.map +1 -1
  114. package/dist/ui/banner.d.ts.map +1 -1
  115. package/dist/ui/banner.js +42 -40
  116. package/dist/ui/banner.js.map +1 -1
  117. package/dist/ui/button-group.d.ts.map +1 -1
  118. package/dist/ui/button-group.js +3 -23
  119. package/dist/ui/button-group.js.map +1 -1
  120. package/dist/ui/button.d.ts +10 -1
  121. package/dist/ui/button.d.ts.map +1 -1
  122. package/dist/ui/button.js +79 -212
  123. package/dist/ui/button.js.map +1 -1
  124. package/dist/ui/card.d.ts.map +1 -1
  125. package/dist/ui/card.js +75 -72
  126. package/dist/ui/card.js.map +1 -1
  127. package/dist/ui/charts/radar-chart.d.ts.map +1 -1
  128. package/dist/ui/charts/radar-chart.js +1 -1
  129. package/dist/ui/charts/radar-chart.js.map +1 -1
  130. package/dist/ui/chat/message-list.d.ts.map +1 -1
  131. package/dist/ui/chat/message.d.ts.map +1 -1
  132. package/dist/ui/chat/typing-indicator.d.ts.map +1 -1
  133. package/dist/ui/code.js +1 -1
  134. package/dist/ui/code.js.map +1 -1
  135. package/dist/ui/color-input.d.ts.map +1 -1
  136. package/dist/ui/color-input.js +151 -150
  137. package/dist/ui/color-input.js.map +1 -1
  138. package/dist/ui/combobox.d.ts +14 -0
  139. package/dist/ui/combobox.d.ts.map +1 -1
  140. package/dist/ui/combobox.js +152 -148
  141. package/dist/ui/combobox.js.map +1 -1
  142. package/dist/ui/context-menu.d.ts.map +1 -1
  143. package/dist/ui/context-menu.js +70 -69
  144. package/dist/ui/context-menu.js.map +1 -1
  145. package/dist/ui/data-table-body.js +2 -2
  146. package/dist/ui/data-table-body.js.map +1 -1
  147. package/dist/ui/data-table-context.js +2 -2
  148. package/dist/ui/data-table-context.js.map +1 -1
  149. package/dist/ui/data-table-header.d.ts.map +1 -1
  150. package/dist/ui/data-table-header.js +41 -40
  151. package/dist/ui/data-table-header.js.map +1 -1
  152. package/dist/ui/data-table-pagination.js +2 -2
  153. package/dist/ui/data-table-pagination.js.map +1 -1
  154. package/dist/ui/dialog.d.ts.map +1 -1
  155. package/dist/ui/dialog.js +61 -60
  156. package/dist/ui/dialog.js.map +1 -1
  157. package/dist/ui/dot.d.ts.map +1 -1
  158. package/dist/ui/dot.js +10 -41
  159. package/dist/ui/dot.js.map +1 -1
  160. package/dist/ui/dropdown-menu.d.ts.map +1 -1
  161. package/dist/ui/dropdown-menu.js +79 -78
  162. package/dist/ui/dropdown-menu.js.map +1 -1
  163. package/dist/ui/file-upload.d.ts.map +1 -1
  164. package/dist/ui/file-upload.js +116 -115
  165. package/dist/ui/file-upload.js.map +1 -1
  166. package/dist/ui/form.d.ts.map +1 -1
  167. package/dist/ui/form.js +27 -26
  168. package/dist/ui/form.js.map +1 -1
  169. package/dist/ui/hover-card.d.ts.map +1 -1
  170. package/dist/ui/hover-card.js +26 -25
  171. package/dist/ui/hover-card.js.map +1 -1
  172. package/dist/ui/icon.js +7 -7
  173. package/dist/ui/index.d.ts +2 -2
  174. package/dist/ui/index.d.ts.map +1 -1
  175. package/dist/ui/index.js +3 -3
  176. package/dist/ui/index.js.map +1 -1
  177. package/dist/ui/input.js +1 -1
  178. package/dist/ui/input.js.map +1 -1
  179. package/dist/ui/menubar.d.ts.map +1 -1
  180. package/dist/ui/menubar.js +73 -72
  181. package/dist/ui/menubar.js.map +1 -1
  182. package/dist/ui/navigation-menu.d.ts.map +1 -1
  183. package/dist/ui/navigation-menu.js +80 -79
  184. package/dist/ui/navigation-menu.js.map +1 -1
  185. package/dist/ui/popover.d.ts.map +1 -1
  186. package/dist/ui/progress.d.ts.map +1 -1
  187. package/dist/ui/progress.js +72 -68
  188. package/dist/ui/progress.js.map +1 -1
  189. package/dist/ui/radio.d.ts.map +1 -1
  190. package/dist/ui/radio.js +32 -31
  191. package/dist/ui/radio.js.map +1 -1
  192. package/dist/ui/search-input.d.ts.map +1 -1
  193. package/dist/ui/search-input.js +28 -27
  194. package/dist/ui/search-input.js.map +1 -1
  195. package/dist/ui/select.d.ts.map +1 -1
  196. package/dist/ui/select.js +64 -63
  197. package/dist/ui/select.js.map +1 -1
  198. package/dist/ui/sidebar.d.ts.map +1 -1
  199. package/dist/ui/sidebar.js +4 -4
  200. package/dist/ui/sidebar.js.map +1 -1
  201. package/dist/ui/slider.d.ts +63 -9
  202. package/dist/ui/slider.d.ts.map +1 -1
  203. package/dist/ui/slider.js +74 -32
  204. package/dist/ui/slider.js.map +1 -1
  205. package/dist/ui/split-button.d.ts +8 -2
  206. package/dist/ui/split-button.d.ts.map +1 -1
  207. package/dist/ui/split-button.js +49 -95
  208. package/dist/ui/split-button.js.map +1 -1
  209. package/dist/ui/stat-card.d.ts.map +1 -1
  210. package/dist/ui/stat-card.js +89 -88
  211. package/dist/ui/stat-card.js.map +1 -1
  212. package/dist/ui/stat-flash.js +16 -16
  213. package/dist/ui/stepper.d.ts.map +1 -1
  214. package/dist/ui/stepper.js +63 -62
  215. package/dist/ui/stepper.js.map +1 -1
  216. package/dist/ui/table.js +3 -3
  217. package/dist/ui/table.js.map +1 -1
  218. package/dist/ui/tabs.d.ts.map +1 -1
  219. package/dist/ui/tabs.js +67 -66
  220. package/dist/ui/tabs.js.map +1 -1
  221. package/dist/ui/textarea.js +1 -1
  222. package/dist/ui/textarea.js.map +1 -1
  223. package/dist/ui/toast.d.ts.map +1 -1
  224. package/dist/ui/toast.js +195 -194
  225. package/dist/ui/toast.js.map +1 -1
  226. package/dist/ui/toggle.d.ts.map +1 -1
  227. package/dist/ui/toggle.js +24 -22
  228. package/dist/ui/toggle.js.map +1 -1
  229. package/dist/ui/tooltip.d.ts.map +1 -1
  230. package/dist/ui/tooltip.js +37 -36
  231. package/dist/ui/tooltip.js.map +1 -1
  232. package/dist/ui/tree-view/tree-item.d.ts.map +1 -1
  233. package/docs/recipes/customize-brand.md +58 -0
  234. package/docs/recipes/install-astro.md +1 -1
  235. package/docs/recipes/install-next-app-router.md +1 -1
  236. package/docs/recipes/install-next-pages.md +22 -1
  237. package/docs/recipes/install-remix.md +1 -1
  238. package/docs/recipes/install-tanstack-start.md +1 -1
  239. package/docs/recipes/install-vite.md +2 -1
  240. package/docs/recipes/server-components.md +7 -4
  241. package/docs/recipes/troubleshoot.md +1 -0
  242. package/llms.txt +1 -1
  243. package/mcp-manifest.json +9 -4
  244. package/package.json +3 -2
  245. package/skill/SKILL.md +1 -1
  246. package/skill/references/components.md +1 -1
  247. package/skill/references/customize-brand.md +58 -0
  248. package/skill/references/server-components.md +7 -4
  249. package/skill/references/setup-astro.md +1 -1
  250. package/skill/references/setup-next-app-router.md +1 -1
  251. package/skill/references/setup-next-pages.md +22 -1
  252. package/skill/references/setup-remix.md +1 -1
  253. package/skill/references/setup-tanstack-start.md +1 -1
  254. package/skill/references/setup-vite.md +2 -1
  255. package/skill/references/troubleshoot.md +1 -0
  256. package/dist/motion/check-motion-provider.d.ts +0 -2
  257. package/dist/motion/check-motion-provider.d.ts.map +0 -1
  258. package/dist/motion/index.js.map +0 -1
@@ -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-ds-01 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 showAccent = false,\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 /** Render the colored left accent bar. Off by default (status = icon + timer bar). */\n showAccent?: 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={cn(\n 'group relative flex w-full overflow-hidden rounded-overlay-sm bg-surface-overlay shadow-floating',\n // Faint surface tint on error only — the one you must not miss.\n type === 'error' && 'bg-error-2',\n )}\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 — opt-in via showAccent (status is carried by icon + timer bar) */}\n {showAccent && 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-body-md font-semibold text-surface-fg\">\n {title}\n </p>\n )}\n {description && (\n <p\n className={cn(\n 'text-body-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-body-sm font-medium text-accent-11 underline-offset-2 hover:underline hover:bg-surface-panel-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-body-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): 'accent' | 'success' | 'error' {\n switch (status) {\n case 'complete':\n return 'success'\n case 'error':\n return 'error'\n default:\n return 'accent'\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-body-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-caption tabular-nums text-surface-fg-muted\">\n {file.progress}%\n </span>\n )}\n </div>\n ) : file.status === 'complete' ? (\n <span className=\"text-body-xs text-success-11\">Done</span>\n ) : file.status === 'error' ? (\n <span className=\"max-w-[60px] truncate text-body-xs text-error-11\">\n {file.error || 'Failed'}\n </span>\n ) : (\n <span className=\"text-caption 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 showAccent = false,\n}: {\n files: UploadFile[]\n onRetry?: (fileId: string) => void\n onRemove?: (fileId: string) => void\n selfDismissId?: string | number\n /** Render the colored left accent bar. Off by default. */\n showAccent?: boolean\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={cn(\n 'group relative flex w-full overflow-hidden rounded-overlay-sm bg-surface-overlay shadow-floating',\n // Faint surface tint when the batch ended with failures.\n uploadUrgent && 'bg-error-2',\n )}\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 — opt-in via showAccent */}\n {showAccent && (\n <div className={cn('w-1 shrink-0 rounded-l-overlay-sm', accentClass)} />\n )}\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-body-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-caption 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 showAccent={options?.showAccent}\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 showAccent={options.showAccent}\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 { MotionPreference } from '../motion/motion-preference'\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-ds-01 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 showAccent = false,\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 /** Render the colored left accent bar. Off by default (status = icon + timer bar). */\n showAccent?: 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 <MotionPreference>\n <motion.div\n layout=\"position\"\n role={isUrgent ? 'alert' : 'status'}\n aria-live={isUrgent ? 'assertive' : 'polite'}\n aria-atomic=\"true\"\n className={cn(\n 'group relative flex w-full overflow-hidden rounded-overlay-sm bg-surface-overlay shadow-floating',\n // Faint surface tint on error only — the one you must not miss.\n type === 'error' && 'bg-error-2',\n )}\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 — opt-in via showAccent (status is carried by icon + timer bar) */}\n {showAccent && 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-body-md font-semibold text-surface-fg\">\n {title}\n </p>\n )}\n {description && (\n <p\n className={cn(\n 'text-body-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-body-sm font-medium text-accent-11 underline-offset-2 hover:underline hover:bg-surface-panel-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-body-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 </MotionPreference>\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): 'accent' | 'success' | 'error' {\n switch (status) {\n case 'complete':\n return 'success'\n case 'error':\n return 'error'\n default:\n return 'accent'\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 <MotionPreference>\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-body-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-caption tabular-nums text-surface-fg-muted\">\n {file.progress}%\n </span>\n )}\n </div>\n ) : file.status === 'complete' ? (\n <span className=\"text-body-xs text-success-11\">Done</span>\n ) : file.status === 'error' ? (\n <span className=\"max-w-[60px] truncate text-body-xs text-error-11\">\n {file.error || 'Failed'}\n </span>\n ) : (\n <span className=\"text-caption 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 </MotionPreference>\n )\n}\n\nfunction UploadToastContent({\n files,\n onRetry,\n onRemove,\n selfDismissId,\n showAccent = false,\n}: {\n files: UploadFile[]\n onRetry?: (fileId: string) => void\n onRemove?: (fileId: string) => void\n selfDismissId?: string | number\n /** Render the colored left accent bar. Off by default. */\n showAccent?: boolean\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 <MotionPreference>\n <motion.div\n layout\n role={uploadUrgent ? 'alert' : 'status'}\n aria-live={uploadUrgent ? 'assertive' : 'polite'}\n aria-label=\"File uploads\"\n className={cn(\n 'group relative flex w-full overflow-hidden rounded-overlay-sm bg-surface-overlay shadow-floating',\n // Faint surface tint when the batch ended with failures.\n uploadUrgent && 'bg-error-2',\n )}\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 — opt-in via showAccent */}\n {showAccent && (\n <div className={cn('w-1 shrink-0 rounded-l-overlay-sm', accentClass)} />\n )}\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-body-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-caption 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 </MotionPreference>\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 showAccent={options?.showAccent}\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 showAccent={options.showAccent}\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 +1 @@
1
- {"version":3,"file":"toggle.d.ts","sourceRoot":"","sources":["../../src/ui/toggle.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,eAAe,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAO9B,QAAA,MAAM,cAAc;;;;8EA4BnB,CAAA;AAED,QAAA,MAAM,MAAM;;;;yHAYV,CAAA;AAGF,MAAM,MAAM,WAAW,GAAG,KAAK,CAAC,wBAAwB,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,GACnF,YAAY,CAAC,OAAO,cAAc,CAAC,CAAA;AAErC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAA"}
1
+ {"version":3,"file":"toggle.d.ts","sourceRoot":"","sources":["../../src/ui/toggle.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,eAAe,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,QAAA,MAAM,cAAc;;;;8EAsCnB,CAAA;AAED,QAAA,MAAM,MAAM;;;;yHAeV,CAAA;AAGF,MAAM,MAAM,WAAW,GAAG,KAAK,CAAC,wBAAwB,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,GACnF,YAAY,CAAC,OAAO,cAAc,CAAC,CAAA;AAErC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAA"}
package/dist/ui/toggle.js CHANGED
@@ -1,13 +1,14 @@
1
1
  "use client";
2
2
  import { motionProps as e, springs as t } from "./lib/motion.js";
3
- import { cn as n } from "./lib/utils.js";
4
- import { Root as r } from "../_chunks/primitives/react-toggle.js";
5
- import * as i from "react";
6
- import { jsx as a } from "react/jsx-runtime";
7
- import { motion as o } from "framer-motion";
8
- import { cva as s } from "class-variance-authority";
3
+ import { MotionPreference as n } from "../_chunks/motion-preference.js";
4
+ import { cn as r } from "./lib/utils.js";
5
+ import { Root as i } from "../_chunks/primitives/react-toggle.js";
6
+ import * as a from "react";
7
+ import { jsx as o } from "react/jsx-runtime";
8
+ import { motion as s } from "framer-motion";
9
+ import { cva as c } from "class-variance-authority";
9
10
  //#region src/ui/toggle.tsx
10
- var c = o.create(r), l = s("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
+ var l = s.create(i), u = c("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
12
  variants: {
12
13
  variant: {
13
14
  default: "bg-transparent hover:bg-surface-panel-hover text-surface-fg-muted",
@@ -19,10 +20,10 @@ var c = o.create(r), l = s("inline-flex items-center justify-center gap-ds-03 ro
19
20
  lg: "h-ds-lg px-ds-05 text-body-lg"
20
21
  },
21
22
  color: {
22
- accent: "data-[state=on]:bg-accent-2 data-[state=on]:text-accent-11",
23
- error: "data-[state=on]:bg-error-2 data-[state=on]:text-error-11",
24
- success: "data-[state=on]:bg-success-2 data-[state=on]:text-success-11",
25
- neutral: "data-[state=on]:bg-surface-panel-hover data-[state=on]:text-surface-fg"
23
+ accent: "data-[state=on]:bg-palette-soft-hover data-[state=on]:text-palette-text",
24
+ error: "data-[state=on]:bg-palette-soft-hover data-[state=on]:text-palette-text",
25
+ success: "data-[state=on]:bg-palette-soft-hover data-[state=on]:text-palette-text",
26
+ neutral: "data-[state=on]:bg-palette-soft-hover data-[state=on]:text-surface-fg"
26
27
  }
27
28
  },
28
29
  defaultVariants: {
@@ -30,17 +31,18 @@ var c = o.create(r), l = s("inline-flex items-center justify-center gap-ds-03 ro
30
31
  size: "md",
31
32
  color: "accent"
32
33
  }
33
- }), u = i.forwardRef(({ className: r, variant: i, size: o, color: s, ...u }, d) => /* @__PURE__ */ a(c, {
34
- ref: d,
34
+ }), d = a.forwardRef(({ className: i, variant: a, size: s, color: c, ...d }, f) => /* @__PURE__ */ o(n, { children: /* @__PURE__ */ o(l, {
35
+ ref: f,
35
36
  whileTap: { scale: .95 },
36
37
  transition: t.snappy,
37
- className: n(l({
38
- variant: i,
39
- size: o,
40
- color: s
41
- }), r),
42
- ...e(u)
43
- }));
44
- u.displayName = r.displayName;
38
+ "data-palette": c ?? void 0,
39
+ className: r(u({
40
+ variant: a,
41
+ size: s,
42
+ color: c
43
+ }), i),
44
+ ...e(d)
45
+ }) }));
46
+ d.displayName = i.displayName;
45
47
  //#endregion
46
- export { u as Toggle, l as toggleVariants };
48
+ export { d as Toggle, u as toggleVariants };
@@ -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-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-panel-hover text-surface-fg-muted',\n outline:\n 'border border-surface-border-interactive bg-transparent hover:bg-surface-panel-hover hover:border-surface-border-interactive',\n },\n size: {\n sm: 'h-ds-sm px-ds-03 text-body-sm',\n md: 'h-ds-md px-ds-04 text-body-md',\n lg: 'h-ds-lg px-ds-05 text-body-lg',\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-panel-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,CAAoB,GAErD,IAAiB,EACrB,qTACA;CACE,UAAU;EACR,SAAS;GACP,SACE;GACF,SACE;EACJ;EACA,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN;EACA,OAAO;GACL,QAAQ;GACR,OAAO;GACP,SAAS;GACT,SAAS;EACX;CACF;CACA,iBAAiB;EACf,SAAS;EACT,MAAM;EACN,OAAO;CACT;AACF,CACF,GAEM,IAAS,EAAM,YAIlB,EAAE,cAAW,YAAS,SAAM,UAAO,GAAG,KAAS,MAChD,kBAAC,GAAD;CACO;CACL,UAAU,EAAE,OAAO,IAAK;CACxB,YAAY,EAAQ;CACpB,WAAW,EAAG,EAAe;EAAE;EAAS;EAAM;CAAM,CAAC,GAAG,CAAS;CACjE,GAAI,EAAY,CAAK;AACtB,CAAA,CACF;AACD,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 { MotionPreference } from '../motion/motion-preference'\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-panel-hover text-surface-fg-muted',\n outline:\n 'border border-surface-border-interactive bg-transparent hover:bg-surface-panel-hover hover:border-surface-border-interactive',\n },\n size: {\n sm: 'h-ds-sm px-ds-03 text-body-sm',\n md: 'h-ds-md px-ds-04 text-body-md',\n lg: 'h-ds-lg px-ds-05 text-body-lg',\n },\n // Pressed state only — an unpressed toggle carries no colour at all.\n //\n // `soft-hover`, not `subtle`: both variants hover to `surface-panel-hover`,\n // and `palette-subtle` resolves to exactly that for neutral — so a pressed\n // neutral toggle was the identical colour to a hovered unpressed one\n // (1.00:1). On accent the two were 1.01:1 apart in light, and in dark the\n // unpressed-hovered toggle was the MORE prominent of the two. `soft-hover`\n // clears the hover fill in both themes for every palette.\n color: {\n accent: 'data-[state=on]:bg-palette-soft-hover data-[state=on]:text-palette-text',\n error: 'data-[state=on]:bg-palette-soft-hover data-[state=on]:text-palette-text',\n success: 'data-[state=on]:bg-palette-soft-hover data-[state=on]:text-palette-text',\n // Neutral takes full-contrast text rather than the muted `text` role,\n // because a pressed neutral toggle has no hue to carry the emphasis.\n neutral: 'data-[state=on]:bg-palette-soft-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 <MotionPreference>\n <MotionToggleRoot\n ref={ref}\n whileTap={{ scale: 0.95 }}\n transition={springs.snappy}\n data-palette={color ?? undefined}\n className={cn(toggleVariants({ variant, size, color }), className)}\n {...motionProps(props)}\n />\n </MotionPreference>\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":";;;;;;;;;;AAWA,IAAM,IAAmB,EAAO,OAAO,CAAoB,GAErD,IAAiB,EACrB,qTACA;CACE,UAAU;EACR,SAAS;GACP,SACE;GACF,SACE;EACJ;EACA,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN;EASA,OAAO;GACL,QAAQ;GACR,OAAO;GACP,SAAS;GAGT,SAAS;EACX;CACF;CACA,iBAAiB;EACf,SAAS;EACT,MAAM;EACN,OAAO;CACT;AACF,CACF,GAEM,IAAS,EAAM,YAIlB,EAAE,cAAW,YAAS,SAAM,UAAO,GAAG,KAAS,MAChD,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;CACO;CACL,UAAU,EAAE,OAAO,IAAK;CACxB,YAAY,EAAQ;CACpB,gBAAc,KAAS,KAAA;CACvB,WAAW,EAAG,EAAe;EAAE;EAAS;EAAM;CAAM,CAAC,GAAG,CAAS;CACjE,GAAI,EAAY,CAAK;AACtB,CAAA,EACe,CAAA,CACnB;AACD,EAAO,cAAA,EAAmC"}
@@ -1 +1 @@
1
- {"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../../src/ui/tooltip.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,gBAAgB,MAAM,2BAA2B,CAAA;AAE7D,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAU9B,QAAA,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,wBAAwB,CAAC,OAAO,gBAAgB,CAAC,QAAQ,CAAC,CAS/F,CAAA;AAmBD,QAAA,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,wBAAwB,CAAC,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAiBnF,CAAA;AAGD,QAAA,MAAM,cAAc,gHAA2B,CAAA;AAW/C,QAAA,MAAM,cAAc,gKAoClB,CAAA;AAGF,MAAM,MAAM,mBAAmB,GAAG,KAAK,CAAC,wBAAwB,CAAC,OAAO,gBAAgB,CAAC,OAAO,CAAC,CAAA;AAEjG,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAC,cAAc,EAAE,CAAA"}
1
+ {"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../../src/ui/tooltip.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,gBAAgB,MAAM,2BAA2B,CAAA;AAE7D,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAW9B,QAAA,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,wBAAwB,CAAC,OAAO,gBAAgB,CAAC,QAAQ,CAAC,CAS/F,CAAA;AAmBD,QAAA,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,wBAAwB,CAAC,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAiBnF,CAAA;AAGD,QAAA,MAAM,cAAc,gHAA2B,CAAA;AAW/C,QAAA,MAAM,cAAc,gKAsClB,CAAA;AAGF,MAAM,MAAM,mBAAmB,GAAG,KAAK,CAAC,wBAAwB,CAAC,OAAO,gBAAgB,CAAC,OAAO,CAAC,CAAA;AAEjG,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAC,cAAc,EAAE,CAAA"}
@@ -1,65 +1,66 @@
1
1
  "use client";
2
2
  import { springs as e, tweens as t } from "./lib/motion.js";
3
- import { cn as n } from "./lib/utils.js";
4
- import { useControllableOpen as r } from "../_chunks/use-controllable-open.js";
5
- import { Content2 as i, Portal as a, Provider as o, Root3 as s, Trigger as c } from "../_chunks/primitives/react-tooltip.js";
6
- import * as l from "react";
7
- import { Fragment as u, jsx as d } from "react/jsx-runtime";
8
- import { AnimatePresence as f, motion as p } from "framer-motion";
3
+ import { MotionPreference as n } from "../_chunks/motion-preference.js";
4
+ import { cn as r } from "./lib/utils.js";
5
+ import { useControllableOpen as i } from "../_chunks/use-controllable-open.js";
6
+ import { Content2 as a, Portal as o, Provider as s, Root3 as c, Trigger as l } from "../_chunks/primitives/react-tooltip.js";
7
+ import * as u from "react";
8
+ import { Fragment as d, jsx as f } from "react/jsx-runtime";
9
+ import { AnimatePresence as p, motion as m } from "framer-motion";
9
10
  //#region src/ui/tooltip.tsx
10
- var m = l.createContext(!1), h = ({ children: e, ...t }) => /* @__PURE__ */ d(m.Provider, {
11
+ var h = u.createContext(!1), g = ({ children: e, ...t }) => /* @__PURE__ */ f(h.Provider, {
11
12
  value: !0,
12
- children: /* @__PURE__ */ d(o, {
13
+ children: /* @__PURE__ */ f(s, {
13
14
  ...t,
14
15
  children: e
15
16
  })
16
17
  });
17
- function g({ children: e }) {
18
- return l.useContext(m) ? /* @__PURE__ */ d(u, { children: e }) : /* @__PURE__ */ d(m.Provider, {
18
+ function _({ children: e }) {
19
+ return u.useContext(h) ? /* @__PURE__ */ f(d, { children: e }) : /* @__PURE__ */ f(h.Provider, {
19
20
  value: !0,
20
- children: /* @__PURE__ */ d(o, {
21
+ children: /* @__PURE__ */ f(s, {
21
22
  delayDuration: 300,
22
23
  children: e
23
24
  })
24
25
  });
25
26
  }
26
- var _ = l.createContext({ open: !1 }), v = ({ open: e, defaultOpen: t, onOpenChange: n, ...i }) => {
27
- let { open: a, setOpen: o } = r({
27
+ var v = u.createContext({ open: !1 }), y = ({ open: e, defaultOpen: t, onOpenChange: n, ...r }) => {
28
+ let { open: a, setOpen: o } = i({
28
29
  open: e,
29
30
  defaultOpen: t,
30
31
  onOpenChange: n
31
- }), c = l.useMemo(() => ({ open: a }), [a]);
32
- return /* @__PURE__ */ d(g, { children: /* @__PURE__ */ d(_.Provider, {
33
- value: c,
34
- children: /* @__PURE__ */ d(s, {
32
+ }), s = u.useMemo(() => ({ open: a }), [a]);
33
+ return /* @__PURE__ */ f(_, { children: /* @__PURE__ */ f(v.Provider, {
34
+ value: s,
35
+ children: /* @__PURE__ */ f(c, {
35
36
  open: a,
36
37
  onOpenChange: o,
37
- ...i
38
+ ...r
38
39
  })
39
40
  }) });
40
41
  };
41
- v.displayName = "Tooltip";
42
- var y = c, b = {
42
+ y.displayName = "Tooltip";
43
+ var b = l, x = {
43
44
  top: { y: 4 },
44
45
  bottom: { y: -4 },
45
46
  left: { x: 4 },
46
47
  right: { x: -4 }
47
- }, x = l.forwardRef(({ className: r, sideOffset: o = 4, side: s = "top", children: c, ...u }, m) => {
48
- let { open: h } = l.useContext(_), g = b[s] ?? {};
49
- return /* @__PURE__ */ d(f, { children: h && /* @__PURE__ */ d(a, {
48
+ }, S = u.forwardRef(({ className: i, sideOffset: s = 4, side: c = "top", children: l, ...d }, h) => {
49
+ let { open: g } = u.useContext(v), _ = x[c] ?? {};
50
+ return /* @__PURE__ */ f(n, { children: /* @__PURE__ */ f(p, { children: g && /* @__PURE__ */ f(o, {
50
51
  forceMount: !0,
51
- children: /* @__PURE__ */ d(i, {
52
- ref: m,
52
+ children: /* @__PURE__ */ f(a, {
53
+ ref: h,
53
54
  forceMount: !0,
54
- sideOffset: o,
55
- side: s,
56
- ...u,
55
+ sideOffset: s,
56
+ side: c,
57
+ ...d,
57
58
  asChild: !0,
58
- children: /* @__PURE__ */ d(p.div, {
59
+ children: /* @__PURE__ */ f(m.div, {
59
60
  initial: {
60
61
  opacity: 0,
61
62
  scale: .95,
62
- ...g
63
+ ..._
63
64
  },
64
65
  animate: {
65
66
  opacity: 1,
@@ -70,18 +71,18 @@ var y = c, b = {
70
71
  exit: {
71
72
  opacity: 0,
72
73
  scale: .95,
73
- ...g
74
+ ..._
74
75
  },
75
76
  transition: {
76
77
  ...e.snappy,
77
78
  opacity: t.fade
78
79
  },
79
- className: n("z-tooltip overflow-hidden rounded-overlay-sm bg-surface-inverted px-ds-04 py-ds-02b text-body-sm text-surface-inverted-fg shadow-floating", r),
80
- children: c
80
+ className: r("z-tooltip overflow-hidden rounded-overlay-sm bg-surface-inverted px-ds-04 py-ds-02b text-body-sm text-surface-inverted-fg shadow-floating", i),
81
+ children: l
81
82
  })
82
83
  })
83
- }) });
84
+ }) }) });
84
85
  });
85
- x.displayName = i.displayName;
86
+ S.displayName = a.displayName;
86
87
  //#endregion
87
- export { v as Tooltip, x as TooltipContent, h as TooltipProvider, y as TooltipTrigger };
88
+ export { y as Tooltip, S as TooltipContent, g as TooltipProvider, b as TooltipTrigger };
@@ -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 { useControllableOpen } from './lib/use-controllable-open'\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 { open, setOpen } = useControllableOpen({ open: openProp, defaultOpen, onOpenChange })\n\n const contextValue = React.useMemo(() => ({ open }), [open])\n\n return (\n <AutoProvider>\n <TooltipContext.Provider value={contextValue}>\n <TooltipPrimitive.Root open={open} onOpenChange={setOpen} {...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-body-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":";;;;;;;;;AAYA,IAAM,IAAyB,EAAM,cAAc,EAAK,GAElD,KAA+F,EACnG,aACA,GAAG,QAEH,kBAAC,EAAuB,UAAxB;CAAiC,OAAO;WACtC,kBAAC,GAAD;EAA2B,GAAI;EAC5B;CACwB,CAAA;AACI,CAAA;AAGnC,SAAS,EAAa,EAAE,eAA2C;CAGjE,OAFoB,EAAM,WAAW,CACjC,IAAoB,kBAAA,GAAA,EAAG,YAAW,CAAA,IAEpC,kBAAC,EAAuB,UAAxB;EAAiC,OAAO;YACtC,kBAAC,GAAD;GAA2B,eAAe;GACvC;EACwB,CAAA;CACI,CAAA;AAErC;AAKA,IAAM,IAAiB,EAAM,cAAmC,EAAE,MAAM,GAAM,CAAC,GAEzE,KAAmF,EACvF,MAAM,GACN,gBACA,iBACA,GAAG,QACC;CACJ,IAAM,EAAE,SAAM,eAAY,EAAoB;EAAE,MAAM;EAAU;EAAa;CAAa,CAAC,GAErF,IAAe,EAAM,eAAe,EAAE,QAAK,IAAI,CAAC,CAAI,CAAC;CAE3D,OACE,kBAAC,GAAD,EAAA,UACE,kBAAC,EAAe,UAAhB;EAAyB,OAAO;YAC9B,kBAAC,GAAD;GAA6B;GAAM,cAAc;GAAS,GAAI;EAAQ,CAAA;CAC/C,CAAA,EACb,CAAA;AAElB;AACA,EAAQ,cAAc;AAEtB,IAAM,IAAiB,GAIjB,IAAyD;CAC7D,KAAK,EAAE,GAAG,EAAE;CACZ,QAAQ,EAAE,GAAG,GAAG;CAChB,MAAM,EAAE,GAAG,EAAE;CACb,OAAO,EAAE,GAAG,GAAG;AACjB,GAEM,IAAiB,EAAM,YAG1B,EAAE,cAAW,YAAY,IAAiB,GAAG,UAAO,OAAO,aAAU,GAAG,KAAS,MAAQ;CAC1F,IAAM,EAAE,YAAS,EAAM,WAAW,CAAc,GAC1C,IAAY,EAAW,MAAS,CAAC;CAEvC,OACE,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;IAAU;IACjD,SAAS;KAAE,SAAS;KAAG,OAAO;KAAG,GAAG;KAAG,GAAG;IAAE;IAC5C,MAAM;KAAE,SAAS;KAAG,OAAO;KAAM,GAAG;IAAU;IAC9C,YAAY;KAAE,GAAG,EAAQ;KAAQ,SAAS,EAAO;IAAK;IACtD,WAAW,EACT,6IACA,CACF;IAEC;GACS,CAAA;EACY,CAAA;CACH,CAAA,EAEZ,CAAA;AAErB,CAAC;AACD,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 { MotionPreference } from '../motion/motion-preference'\nimport { springs, tweens } from './lib/motion'\nimport { useControllableOpen } from './lib/use-controllable-open'\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 { open, setOpen } = useControllableOpen({ open: openProp, defaultOpen, onOpenChange })\n\n const contextValue = React.useMemo(() => ({ open }), [open])\n\n return (\n <AutoProvider>\n <TooltipContext.Provider value={contextValue}>\n <TooltipPrimitive.Root open={open} onOpenChange={setOpen} {...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 <MotionPreference>\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-body-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 </MotionPreference>\n )\n})\nTooltipContent.displayName = TooltipPrimitive.Content.displayName\n\nexport type TooltipContentProps = React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n\nexport { Tooltip, TooltipContent, TooltipProvider,TooltipTrigger }\n"],"mappings":";;;;;;;;;;AAaA,IAAM,IAAyB,EAAM,cAAc,EAAK,GAElD,KAA+F,EACnG,aACA,GAAG,QAEH,kBAAC,EAAuB,UAAxB;CAAiC,OAAO;WACtC,kBAAC,GAAD;EAA2B,GAAI;EAC5B;CACwB,CAAA;AACI,CAAA;AAGnC,SAAS,EAAa,EAAE,eAA2C;CAGjE,OAFoB,EAAM,WAAW,CACjC,IAAoB,kBAAA,GAAA,EAAG,YAAW,CAAA,IAEpC,kBAAC,EAAuB,UAAxB;EAAiC,OAAO;YACtC,kBAAC,GAAD;GAA2B,eAAe;GACvC;EACwB,CAAA;CACI,CAAA;AAErC;AAKA,IAAM,IAAiB,EAAM,cAAmC,EAAE,MAAM,GAAM,CAAC,GAEzE,KAAmF,EACvF,MAAM,GACN,gBACA,iBACA,GAAG,QACC;CACJ,IAAM,EAAE,SAAM,eAAY,EAAoB;EAAE,MAAM;EAAU;EAAa;CAAa,CAAC,GAErF,IAAe,EAAM,eAAe,EAAE,QAAK,IAAI,CAAC,CAAI,CAAC;CAE3D,OACE,kBAAC,GAAD,EAAA,UACE,kBAAC,EAAe,UAAhB;EAAyB,OAAO;YAC9B,kBAAC,GAAD;GAA6B;GAAM,cAAc;GAAS,GAAI;EAAQ,CAAA;CAC/C,CAAA,EACb,CAAA;AAElB;AACA,EAAQ,cAAc;AAEtB,IAAM,IAAiB,GAIjB,IAAyD;CAC7D,KAAK,EAAE,GAAG,EAAE;CACZ,QAAQ,EAAE,GAAG,GAAG;CAChB,MAAM,EAAE,GAAG,EAAE;CACb,OAAO,EAAE,GAAG,GAAG;AACjB,GAEM,IAAiB,EAAM,YAG1B,EAAE,cAAW,YAAY,IAAiB,GAAG,UAAO,OAAO,aAAU,GAAG,KAAS,MAAQ;CAC1F,IAAM,EAAE,YAAS,EAAM,WAAW,CAAc,GAC1C,IAAY,EAAW,MAAS,CAAC;CAEvC,OACE,kBAAC,GAAD,EAAA,UACE,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;IAAU;IACjD,SAAS;KAAE,SAAS;KAAG,OAAO;KAAG,GAAG;KAAG,GAAG;IAAE;IAC5C,MAAM;KAAE,SAAS;KAAG,OAAO;KAAM,GAAG;IAAU;IAC9C,YAAY;KAAE,GAAG,EAAQ;KAAQ,SAAS,EAAO;IAAK;IACtD,WAAW,EACT,6IACA,CACF;IAEC;GACS,CAAA;EACY,CAAA;CACH,CAAA,EAEZ,CAAA,EACD,CAAA;AAEtB,CAAC;AACD,EAAe,cAAA,EAAuC"}
@@ -1 +1 @@
1
- {"version":3,"file":"tree-item.d.ts","sourceRoot":"","sources":["../../../src/ui/tree-view/tree-item.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAKlD,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAA;IACd,4BAA4B;IAC5B,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,wEAAwE;IACxE,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,uDAAuD;IACvD,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAChC,oDAAoD;IACpD,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,wBAAwB;IACxB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iCAAiC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,QAAA,MAAM,QAAQ,qFAqMb,CAAA;AAGD,OAAO,EAAE,QAAQ,EAAE,CAAA"}
1
+ {"version":3,"file":"tree-item.d.ts","sourceRoot":"","sources":["../../../src/ui/tree-view/tree-item.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAKlD,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAA;IACd,4BAA4B;IAC5B,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,wEAAwE;IACxE,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,uDAAuD;IACvD,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAChC,oDAAoD;IACpD,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,wBAAwB;IACxB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iCAAiC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,QAAA,MAAM,QAAQ,qFA0Mb,CAAA;AAGD,OAAO,EAAE,QAAQ,EAAE,CAAA"}
@@ -256,6 +256,64 @@ Every override should consider both modes. Pattern:
256
256
 
257
257
  `.dark` is a regular class selector — its specificity is higher than `:root`/`@theme` body, so dark-mode overrides win when the class is active.
258
258
 
259
+ ## High contrast (`[data-contrast]`)
260
+
261
+ WCAG 2.2 SC 1.4.11 asks for **3:1** between a control's edge and its background.
262
+ The default control edge here is **2.00:1** in light and **2.08:1** in dark — a
263
+ deliberately softer look, and deliberately under that bar. It is recorded as
264
+ `CONTROL-EDGE-BELOW-AA` in the deviations register.
265
+
266
+ Set `data-contrast="high"` to opt into edges that clear the threshold:
267
+
268
+ ```html
269
+ <!-- whole app -->
270
+ <html data-contrast="high">
271
+ ```
272
+
273
+ ```tsx
274
+ {/* or one region — a data-entry screen inside an otherwise soft product */}
275
+ <div data-contrast="high">
276
+ <InvoiceForm />
277
+ </div>
278
+ ```
279
+
280
+ | | control edge, light | control edge, dark |
281
+ |---|---|---|
282
+ | default | `#b7b7b7` — 2.00:1 | `#4c4c4c` — 2.08:1 |
283
+ | `data-contrast="high"` | `#868686` — **3.64:1** | `#767676` — **3.93:1** |
284
+
285
+ It affects Input, Textarea, Select, Combobox, Checkbox, Radio, Switch,
286
+ NumberInput and outlined Button — anything using the control-edge tier. It does
287
+ **not** touch decorative borders (card and panel hairlines stay soft), and it
288
+ does not touch the chromatic ramps, so an outlined Button on a colour is
289
+ unchanged.
290
+
291
+ Composes with everything else — it is a plain attribute, like `.dark` and
292
+ `data-shape`:
293
+
294
+ ```html
295
+ <html class="dark" data-shape="sharp" data-contrast="high">
296
+ ```
297
+
298
+ **Two things worth knowing.**
299
+
300
+ If you override the neutral ramp, high contrast follows automatically — the
301
+ block resolves `--neutral-8` / `--neutral-9` by name rather than hard-coding
302
+ hexes, so a rebrand gets a correctly-scaled high-contrast mode for free.
303
+
304
+ If you want the *opposite* — compliant edges everywhere, with no attribute to
305
+ remember — set the tokens directly and skip the attribute entirely:
306
+
307
+ ```css
308
+ :root {
309
+ --color-surface-border-interactive: var(--neutral-8);
310
+ --color-surface-border-interactive-strong: var(--neutral-9);
311
+ }
312
+ ```
313
+
314
+ That is worth considering. A default is what most products ship, so leaving the
315
+ attribute unset means shipping the edge that misses 1.4.11.
316
+
259
317
  ## Forced colors (Windows high-contrast)
260
318
 
261
319
  If you override semantic colors, the `@media (forced-colors: active)` block in `semantic.css` continues to remap to system keywords. Your override is ignored when the user is in high-contrast mode — this is the correct behavior.
@@ -36,10 +36,10 @@ Some components ship hard peers as optional. **Install BEFORE first import.**
36
36
  | When you import… | Install |
37
37
  |---|---|
38
38
  | `@devalok/shilp-sutra/composed/date-picker` | `pnpm add date-fns` |
39
+ | `@devalok/shilp-sutra/composed/diff` | `pnpm add react-syntax-highlighter` |
39
40
  | `@devalok/shilp-sutra/composed/file-preview` | `pnpm add react-pdf react-zoom-pan-pinch` |
40
41
  | `@devalok/shilp-sutra/composed/markdown-viewer` | `pnpm add react-markdown react-syntax-highlighter remark-gfm` |
41
42
  | `@devalok/shilp-sutra/composed/rich-chat-input` | `pnpm add @tiptap/core @tiptap/extension-highlight @tiptap/extension-image @tiptap/extension-list @tiptap/extension-mention @tiptap/extension-text-align @tiptap/extensions @tiptap/markdown @tiptap/pm @tiptap/react @tiptap/starter-kit @tiptap/suggestion date-fns` |
42
- | `@devalok/shilp-sutra/composed/diff` | `pnpm add react-syntax-highlighter` |
43
43
  | `@devalok/shilp-sutra/composed/rich-text-editor` | `pnpm add @tiptap/core @tiptap/extension-highlight @tiptap/extension-image @tiptap/extension-list @tiptap/extension-mention @tiptap/extension-text-align @tiptap/extensions @tiptap/markdown @tiptap/pm @tiptap/react @tiptap/starter-kit @tiptap/suggestion` |
44
44
  | `@devalok/shilp-sutra/composed/schedule-view` | `pnpm add date-fns` |
45
45
  | `@devalok/shilp-sutra/ui/charts` | `pnpm add d3-axis d3-scale d3-selection d3-shape` |
@@ -49,10 +49,10 @@ Some components depend on third-party libraries that ship as optional peers. **I
49
49
  | When you import… | Install |
50
50
  |---|---|
51
51
  | `@devalok/shilp-sutra/composed/date-picker` | `pnpm add date-fns` |
52
+ | `@devalok/shilp-sutra/composed/diff` | `pnpm add react-syntax-highlighter` |
52
53
  | `@devalok/shilp-sutra/composed/file-preview` | `pnpm add react-pdf react-zoom-pan-pinch` |
53
54
  | `@devalok/shilp-sutra/composed/markdown-viewer` | `pnpm add react-markdown react-syntax-highlighter remark-gfm` |
54
55
  | `@devalok/shilp-sutra/composed/rich-chat-input` | `pnpm add @tiptap/core @tiptap/extension-highlight @tiptap/extension-image @tiptap/extension-list @tiptap/extension-mention @tiptap/extension-text-align @tiptap/extensions @tiptap/markdown @tiptap/pm @tiptap/react @tiptap/starter-kit @tiptap/suggestion date-fns` |
55
- | `@devalok/shilp-sutra/composed/diff` | `pnpm add react-syntax-highlighter` |
56
56
  | `@devalok/shilp-sutra/composed/rich-text-editor` | `pnpm add @tiptap/core @tiptap/extension-highlight @tiptap/extension-image @tiptap/extension-list @tiptap/extension-mention @tiptap/extension-text-align @tiptap/extensions @tiptap/markdown @tiptap/pm @tiptap/react @tiptap/starter-kit @tiptap/suggestion` |
57
57
  | `@devalok/shilp-sutra/composed/schedule-view` | `pnpm add date-fns` |
58
58
  | `@devalok/shilp-sutra/ui/charts` | `pnpm add d3-axis d3-scale d3-selection d3-shape` |
@@ -16,7 +16,28 @@ If both `app/` and `pages/` exist, prefer [install-next-app-router.md](./install
16
16
 
17
17
  Same dependencies as the App Router recipe — see [install-next-app-router.md § 2](./install-next-app-router.md#2-install-dependencies). Replace `next-themes`'s `attribute="class"` setup with the same on Pages Router (it works identically).
18
18
 
19
- **Optional peer dependencies** if you'll render `<DataTable>`, any chart, `<DatePicker>`, `<ScheduleView>`, `<InputOTP>`, `<FilePreview>`, `<MarkdownViewer>`, or `<Toaster>`, install the matching peers BEFORE first import. (The emoji picker and rich-text editors bundle their deps — no install needed; `@tabler/icons-react` auto-installs as a required peer.) Full table at [install-next-app-router.md § 2a](./install-next-app-router.md#2a-optional-peer-dependencies-install-only-when-importing-the-matching-subpath) — identical for Pages Router.
19
+ ### 2a. Optional peer dependencies (install only when importing the matching subpath)
20
+
21
+ Some components depend on third-party libraries that ship as optional peers. **Install BEFORE first import** of the matching component, or `next build` will exit with `Module not found`. Skip entirely if you only use core components (`Button`, `Text`, `Stack`, `Dialog`, `Toast`, `Form*`, `Input`, `Card`, etc.).
22
+
23
+ | When you import… | Install |
24
+ |---|---|
25
+ | `@devalok/shilp-sutra/composed/date-picker` | `pnpm add date-fns` |
26
+ | `@devalok/shilp-sutra/composed/diff` | `pnpm add react-syntax-highlighter` |
27
+ | `@devalok/shilp-sutra/composed/file-preview` | `pnpm add react-pdf react-zoom-pan-pinch` |
28
+ | `@devalok/shilp-sutra/composed/markdown-viewer` | `pnpm add react-markdown react-syntax-highlighter remark-gfm` |
29
+ | `@devalok/shilp-sutra/composed/rich-chat-input` | `pnpm add @tiptap/core @tiptap/extension-highlight @tiptap/extension-image @tiptap/extension-list @tiptap/extension-mention @tiptap/extension-text-align @tiptap/extensions @tiptap/markdown @tiptap/pm @tiptap/react @tiptap/starter-kit @tiptap/suggestion date-fns` |
30
+ | `@devalok/shilp-sutra/composed/rich-text-editor` | `pnpm add @tiptap/core @tiptap/extension-highlight @tiptap/extension-image @tiptap/extension-list @tiptap/extension-mention @tiptap/extension-text-align @tiptap/extensions @tiptap/markdown @tiptap/pm @tiptap/react @tiptap/starter-kit @tiptap/suggestion` |
31
+ | `@devalok/shilp-sutra/composed/schedule-view` | `pnpm add date-fns` |
32
+ | `@devalok/shilp-sutra/ui/charts` | `pnpm add d3-axis d3-scale d3-selection d3-shape` |
33
+ | `@devalok/shilp-sutra/ui/data-table` | `pnpm add @tanstack/react-table @tanstack/react-virtual` |
34
+ | `@devalok/shilp-sutra/ui/data-table-toolbar` | `pnpm add @tanstack/react-table` |
35
+ | `@devalok/shilp-sutra/ui/input-otp` | `pnpm add input-otp` |
36
+ | `@devalok/shilp-sutra/ui/toast` | `pnpm add sonner` |
37
+ | `@devalok/shilp-sutra/ui/toaster` | `pnpm add sonner` |
38
+ | Any `Icon` / `IconButton` with Tabler icons (near-universal — most components use icons internally, so it is a base-install peer) | `pnpm add @tabler/icons-react` |
39
+
40
+ > These aren't in core deps so consumers who never render a chart, OTP input, or rich-text editor don't pay the install / bundle cost. One-time decision at install.
20
41
 
21
42
  ## 3. PostCSS
22
43
 
@@ -32,10 +32,10 @@ Some components ship hard peers as optional. **Install BEFORE first import.**
32
32
  | When you import… | Install |
33
33
  |---|---|
34
34
  | `@devalok/shilp-sutra/composed/date-picker` | `pnpm add date-fns` |
35
+ | `@devalok/shilp-sutra/composed/diff` | `pnpm add react-syntax-highlighter` |
35
36
  | `@devalok/shilp-sutra/composed/file-preview` | `pnpm add react-pdf react-zoom-pan-pinch` |
36
37
  | `@devalok/shilp-sutra/composed/markdown-viewer` | `pnpm add react-markdown react-syntax-highlighter remark-gfm` |
37
38
  | `@devalok/shilp-sutra/composed/rich-chat-input` | `pnpm add @tiptap/core @tiptap/extension-highlight @tiptap/extension-image @tiptap/extension-list @tiptap/extension-mention @tiptap/extension-text-align @tiptap/extensions @tiptap/markdown @tiptap/pm @tiptap/react @tiptap/starter-kit @tiptap/suggestion date-fns` |
38
- | `@devalok/shilp-sutra/composed/diff` | `pnpm add react-syntax-highlighter` |
39
39
  | `@devalok/shilp-sutra/composed/rich-text-editor` | `pnpm add @tiptap/core @tiptap/extension-highlight @tiptap/extension-image @tiptap/extension-list @tiptap/extension-mention @tiptap/extension-text-align @tiptap/extensions @tiptap/markdown @tiptap/pm @tiptap/react @tiptap/starter-kit @tiptap/suggestion` |
40
40
  | `@devalok/shilp-sutra/composed/schedule-view` | `pnpm add date-fns` |
41
41
  | `@devalok/shilp-sutra/ui/charts` | `pnpm add d3-axis d3-scale d3-selection d3-shape` |