@refraction-ui/astro 0.4.2 → 0.4.5

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 (334) hide show
  1. package/dist/accordion/index.ts +1 -0
  2. package/dist/ai/ai-manager.ts +121 -0
  3. package/dist/ai/index.ts +23 -0
  4. package/dist/ai/mock-providers.ts +68 -0
  5. package/dist/ai/sanitizer.ts +174 -0
  6. package/dist/ai/tts-manager.ts +80 -0
  7. package/dist/ai/types.ts +72 -0
  8. package/dist/animated-text/animated-text.styles.ts +28 -0
  9. package/dist/animated-text/animated-text.ts +83 -0
  10. package/dist/animated-text/index.ts +15 -0
  11. package/dist/app-shell/app-shell.ts +252 -0
  12. package/dist/app-shell/auth-shell.ts +84 -0
  13. package/dist/app-shell/index.ts +22 -0
  14. package/dist/app-shell/page-shell.ts +125 -0
  15. package/dist/astro-accordion/Button.astro +1 -1
  16. package/dist/astro-ai/index.ts +1 -1
  17. package/dist/astro-animated-text/AnimatedText.astro +1 -1
  18. package/dist/astro-animated-text/TypewriterText.astro +1 -1
  19. package/dist/astro-app-shell/AppShell.astro +1 -1
  20. package/dist/astro-app-shell/AuthShell.astro +1 -1
  21. package/dist/astro-app-shell/AuthShellCard.astro +1 -1
  22. package/dist/astro-app-shell/PageShell.astro +1 -1
  23. package/dist/astro-app-shell/PageShellSection.astro +1 -1
  24. package/dist/astro-app-shell/index.ts +1 -1
  25. package/dist/astro-auth/index.ts +2 -2
  26. package/dist/astro-avatar/Avatar.astro +1 -1
  27. package/dist/astro-avatar/AvatarFallback.astro +1 -1
  28. package/dist/astro-avatar/AvatarImage.astro +1 -1
  29. package/dist/astro-avatar-group/AvatarGroup.astro +1 -1
  30. package/dist/astro-badge/Badge.astro +1 -1
  31. package/dist/astro-bottom-nav/BottomNav.astro +1 -1
  32. package/dist/astro-breadcrumbs/Breadcrumbs.astro +1 -1
  33. package/dist/astro-button/Button.astro +1 -1
  34. package/dist/astro-calendar/Calendar.astro +1 -1
  35. package/dist/astro-card/Card.astro +1 -1
  36. package/dist/astro-card/CardContent.astro +1 -1
  37. package/dist/astro-card/CardDescription.astro +1 -1
  38. package/dist/astro-card/CardFooter.astro +1 -1
  39. package/dist/astro-card/CardHeader.astro +1 -1
  40. package/dist/astro-card/CardTitle.astro +1 -1
  41. package/dist/astro-card-grid/CardGrid.astro +1 -1
  42. package/dist/astro-carousel/Button.astro +1 -1
  43. package/dist/astro-charts/Bars.astro +1 -1
  44. package/dist/astro-charts/Chart.astro +1 -1
  45. package/dist/astro-charts/Circles.astro +1 -1
  46. package/dist/astro-charts/Histogram.astro +1 -1
  47. package/dist/astro-charts/Line.astro +1 -1
  48. package/dist/astro-charts/PieChart.astro +1 -1
  49. package/dist/astro-charts/ScatterPlot.astro +1 -1
  50. package/dist/astro-charts/index.ts +1 -1
  51. package/dist/astro-checkbox/Checkbox.astro +1 -1
  52. package/dist/astro-code-editor/CodeEditor.astro +1 -1
  53. package/dist/astro-collapsible/Collapsible.astro +1 -1
  54. package/dist/astro-collapsible/CollapsibleContent.astro +1 -1
  55. package/dist/astro-command/Command.astro +1 -1
  56. package/dist/astro-command/CommandGroup.astro +1 -1
  57. package/dist/astro-command/CommandInput.astro +1 -1
  58. package/dist/astro-command/CommandItem.astro +1 -1
  59. package/dist/astro-command-input/CommandInput.astro +1 -1
  60. package/dist/astro-content-protection/ContentProtection.astro +1 -1
  61. package/dist/astro-data-table/DataTable.astro +1 -1
  62. package/dist/astro-date-picker/DatePicker.astro +1 -1
  63. package/dist/astro-device-frame/DeviceFrame.astro +1 -1
  64. package/dist/astro-dialog/DialogContent.astro +1 -1
  65. package/dist/astro-dialog/DialogOverlay.astro +1 -1
  66. package/dist/astro-diff-viewer/DiffViewer.astro +1 -1
  67. package/dist/astro-dropdown-menu/DropdownMenuContent.astro +1 -1
  68. package/dist/astro-dropdown-menu/DropdownMenuItem.astro +1 -1
  69. package/dist/astro-emoji-picker/EmojiPicker.astro +1 -1
  70. package/dist/astro-feedback-dialog/FeedbackDialog.astro +1 -1
  71. package/dist/astro-file-upload/FileUpload.astro +1 -1
  72. package/dist/astro-footer/Footer.astro +1 -1
  73. package/dist/astro-inline-editor/InlineEditor.astro +1 -1
  74. package/dist/astro-input/Input.astro +1 -1
  75. package/dist/astro-input-group/InputGroup.astro +1 -1
  76. package/dist/astro-input-group/InputGroupAddon.astro +1 -1
  77. package/dist/astro-input-group/InputGroupButton.astro +1 -1
  78. package/dist/astro-install-prompt/InstallPrompt.astro +1 -1
  79. package/dist/astro-keyboard-shortcut/ShortcutBadge.astro +1 -1
  80. package/dist/astro-language-selector/LanguageSelector.astro +1 -1
  81. package/dist/astro-link-card/LinkCard.astro +1 -1
  82. package/dist/astro-markdown-renderer/MarkdownRenderer.astro +1 -1
  83. package/dist/astro-mobile-nav/MobileNav.astro +1 -1
  84. package/dist/astro-mobile-nav/MobileNavLink.astro +1 -1
  85. package/dist/astro-mobile-nav/MobileNavTrigger.astro +1 -1
  86. package/dist/astro-navbar/Navbar.astro +1 -1
  87. package/dist/astro-otp-input/OtpInput.astro +1 -1
  88. package/dist/astro-payment/Payment.astro +1 -1
  89. package/dist/astro-popover/PopoverContent.astro +1 -1
  90. package/dist/astro-presence-indicator/PresenceIndicator.astro +1 -1
  91. package/dist/astro-progress-display/BadgeDisplay.astro +1 -1
  92. package/dist/astro-progress-display/ProgressBar.astro +1 -1
  93. package/dist/astro-progress-display/StatsGrid.astro +1 -1
  94. package/dist/astro-radio/RadioGroup.astro +1 -1
  95. package/dist/astro-radio/RadioItem.astro +1 -1
  96. package/dist/astro-reaction-bar/ReactionBar.astro +1 -1
  97. package/dist/astro-resizable-layout/ResizableDivider.astro +1 -1
  98. package/dist/astro-resizable-layout/ResizableLayout.astro +1 -1
  99. package/dist/astro-resizable-layout/ResizablePane.astro +1 -1
  100. package/dist/astro-resizable-layout/index.ts +1 -1
  101. package/dist/astro-rich-editor/index.ts +2 -2
  102. package/dist/astro-search-bar/SearchBar.astro +1 -1
  103. package/dist/astro-search-bar/SearchResults.astro +1 -1
  104. package/dist/astro-select/Select.astro +1 -1
  105. package/dist/astro-select/SelectContent.astro +1 -1
  106. package/dist/astro-select/SelectItem.astro +1 -1
  107. package/dist/astro-select/SelectTrigger.astro +1 -1
  108. package/dist/astro-sidebar/Sidebar.astro +1 -1
  109. package/dist/astro-skeleton/Skeleton.astro +1 -1
  110. package/dist/astro-slide-viewer/SlideViewer.astro +1 -1
  111. package/dist/astro-status-indicator/StatusIndicator.astro +1 -1
  112. package/dist/astro-switch/Switch.astro +1 -1
  113. package/dist/astro-table-of-contents/TableOfContents.astro +1 -1
  114. package/dist/astro-tabs/Tabs.astro +1 -1
  115. package/dist/astro-tabs/TabsList.astro +1 -1
  116. package/dist/astro-tabs/TabsTrigger.astro +1 -1
  117. package/dist/astro-textarea/Textarea.astro +1 -1
  118. package/dist/astro-theme/ThemeScript.astro +1 -1
  119. package/dist/astro-thread-view/ThreadView.astro +1 -1
  120. package/dist/astro-toast/Toast.astro +1 -1
  121. package/dist/astro-tooltip/TooltipContent.astro +1 -1
  122. package/dist/astro-version-selector/VersionSelector.astro +1 -1
  123. package/dist/astro-video-player/VideoPlayer.astro +1 -1
  124. package/dist/auth/adapters/mock.ts +74 -0
  125. package/dist/auth/auth-machine.ts +120 -0
  126. package/dist/auth/index.ts +22 -0
  127. package/dist/auth/rbac.ts +44 -0
  128. package/dist/auth/types.ts +44 -0
  129. package/dist/avatar/avatar.styles.ts +47 -0
  130. package/dist/avatar/avatar.ts +73 -0
  131. package/dist/avatar/index.ts +15 -0
  132. package/dist/avatar-group/avatar-group.styles.ts +63 -0
  133. package/dist/avatar-group/avatar-group.ts +106 -0
  134. package/dist/avatar-group/index.ts +18 -0
  135. package/dist/badge/badge.styles.ts +24 -0
  136. package/dist/badge/badge.ts +48 -0
  137. package/dist/badge/index.ts +10 -0
  138. package/dist/bottom-nav/bottom-nav.styles.ts +18 -0
  139. package/dist/bottom-nav/bottom-nav.ts +33 -0
  140. package/dist/bottom-nav/index.ts +2 -0
  141. package/dist/breadcrumbs/breadcrumbs.styles.ts +20 -0
  142. package/dist/breadcrumbs/breadcrumbs.ts +72 -0
  143. package/dist/breadcrumbs/index.ts +8 -0
  144. package/dist/button/button.styles.ts +38 -0
  145. package/dist/button/button.ts +64 -0
  146. package/dist/button/index.ts +10 -0
  147. package/dist/calendar/calendar.styles.ts +21 -0
  148. package/dist/calendar/calendar.ts +202 -0
  149. package/dist/calendar/index.ts +9 -0
  150. package/dist/callout/callout.ts +1 -0
  151. package/dist/callout/index.ts +1 -0
  152. package/dist/card/card.styles.ts +55 -0
  153. package/dist/card/card.ts +45 -0
  154. package/dist/card/index.ts +20 -0
  155. package/dist/card-grid/card-grid.ts +9 -0
  156. package/dist/card-grid/index.ts +1 -0
  157. package/dist/carousel/index.ts +1 -0
  158. package/dist/charts/axis.ts +50 -0
  159. package/dist/charts/chart.ts +73 -0
  160. package/dist/charts/colorblind.ts +100 -0
  161. package/dist/charts/data.ts +72 -0
  162. package/dist/charts/geometry.ts +54 -0
  163. package/dist/charts/index.ts +61 -0
  164. package/dist/charts/scales.ts +151 -0
  165. package/dist/checkbox/checkbox.styles.ts +33 -0
  166. package/dist/checkbox/checkbox.ts +73 -0
  167. package/dist/checkbox/index.ts +10 -0
  168. package/dist/code-block/code-block.ts +1 -0
  169. package/dist/code-block/index.ts +1 -0
  170. package/dist/code-editor/code-editor.styles.ts +32 -0
  171. package/dist/code-editor/code-editor.ts +140 -0
  172. package/dist/code-editor/index.ts +9 -0
  173. package/dist/collapsible/collapsible.styles.ts +14 -0
  174. package/dist/collapsible/collapsible.ts +93 -0
  175. package/dist/collapsible/index.ts +7 -0
  176. package/dist/command/command.styles.ts +27 -0
  177. package/dist/command/command.ts +179 -0
  178. package/dist/command/index.ts +14 -0
  179. package/dist/command-input/command-input.ts +176 -0
  180. package/dist/command-input/index.ts +1 -0
  181. package/dist/content-protection/content-protection.styles.ts +9 -0
  182. package/dist/content-protection/content-protection.ts +68 -0
  183. package/dist/content-protection/index.ts +8 -0
  184. package/dist/data-table/data-table.styles.ts +55 -0
  185. package/dist/data-table/data-table.ts +183 -0
  186. package/dist/data-table/index.ts +15 -0
  187. package/dist/date-picker/date-picker.styles.ts +31 -0
  188. package/dist/date-picker/date-picker.ts +333 -0
  189. package/dist/date-picker/index.ts +18 -0
  190. package/dist/device-frame/device-frame.styles.ts +21 -0
  191. package/dist/device-frame/device-frame.ts +112 -0
  192. package/dist/device-frame/index.ts +10 -0
  193. package/dist/dialog/dialog.styles.ts +8 -0
  194. package/dist/dialog/dialog.ts +133 -0
  195. package/dist/dialog/index.ts +8 -0
  196. package/dist/diff-viewer/diff-viewer.styles.ts +124 -0
  197. package/dist/diff-viewer/diff-viewer.ts +255 -0
  198. package/dist/diff-viewer/index.ts +20 -0
  199. package/dist/dropdown-menu/dropdown-menu.styles.ts +9 -0
  200. package/dist/dropdown-menu/dropdown-menu.ts +139 -0
  201. package/dist/dropdown-menu/index.ts +9 -0
  202. package/dist/emoji-picker/emoji-data.ts +282 -0
  203. package/dist/emoji-picker/emoji-picker.styles.ts +32 -0
  204. package/dist/emoji-picker/emoji-picker.ts +165 -0
  205. package/dist/emoji-picker/index.ts +25 -0
  206. package/dist/feedback-dialog/feedback-dialog.styles.ts +15 -0
  207. package/dist/feedback-dialog/feedback-dialog.ts +147 -0
  208. package/dist/feedback-dialog/index.ts +11 -0
  209. package/dist/file-upload/file-upload.styles.ts +30 -0
  210. package/dist/file-upload/file-upload.ts +260 -0
  211. package/dist/file-upload/index.ts +19 -0
  212. package/dist/footer/footer.styles.ts +5 -0
  213. package/dist/footer/footer.ts +32 -0
  214. package/dist/footer/index.ts +9 -0
  215. package/dist/index.ts +1 -1
  216. package/dist/inline-editor/index.ts +13 -0
  217. package/dist/inline-editor/inline-editor.styles.ts +42 -0
  218. package/dist/inline-editor/inline-editor.ts +96 -0
  219. package/dist/input/index.ts +9 -0
  220. package/dist/input/input.styles.ts +15 -0
  221. package/dist/input/input.ts +84 -0
  222. package/dist/input-group/index.ts +13 -0
  223. package/dist/input-group/input-group.styles.ts +50 -0
  224. package/dist/input-group/input-group.ts +44 -0
  225. package/dist/install-prompt/index.ts +9 -0
  226. package/dist/install-prompt/install-prompt.styles.ts +5 -0
  227. package/dist/install-prompt/install-prompt.ts +68 -0
  228. package/dist/keyboard-shortcut/index.ts +12 -0
  229. package/dist/keyboard-shortcut/keyboard-shortcut.styles.ts +8 -0
  230. package/dist/keyboard-shortcut/keyboard-shortcut.ts +153 -0
  231. package/dist/language-selector/index.ts +8 -0
  232. package/dist/language-selector/language-selector.styles.ts +28 -0
  233. package/dist/language-selector/language-selector.ts +149 -0
  234. package/dist/link-card/index.ts +1 -0
  235. package/dist/link-card/link-card.ts +9 -0
  236. package/dist/markdown-renderer/index.ts +8 -0
  237. package/dist/markdown-renderer/markdown-renderer.styles.ts +31 -0
  238. package/dist/markdown-renderer/markdown-renderer.ts +251 -0
  239. package/dist/mobile-nav/index.ts +14 -0
  240. package/dist/mobile-nav/mobile-nav.styles.ts +39 -0
  241. package/dist/mobile-nav/mobile-nav.ts +74 -0
  242. package/dist/navbar/index.ts +9 -0
  243. package/dist/navbar/navbar.styles.ts +29 -0
  244. package/dist/navbar/navbar.ts +39 -0
  245. package/dist/otp-input/index.ts +13 -0
  246. package/dist/otp-input/otp-input.styles.ts +51 -0
  247. package/dist/otp-input/otp-input.ts +198 -0
  248. package/dist/pagination/index.ts +1 -0
  249. package/dist/payment/index.ts +1 -0
  250. package/dist/payment/payment.ts +16 -0
  251. package/dist/popover/index.ts +7 -0
  252. package/dist/popover/popover.styles.ts +16 -0
  253. package/dist/popover/popover.ts +89 -0
  254. package/dist/presence-indicator/index.ts +14 -0
  255. package/dist/presence-indicator/presence-indicator.styles.ts +29 -0
  256. package/dist/presence-indicator/presence-indicator.ts +63 -0
  257. package/dist/progress-display/index.ts +15 -0
  258. package/dist/progress-display/progress-display.styles.ts +72 -0
  259. package/dist/progress-display/progress-display.ts +60 -0
  260. package/dist/radio/index.ts +8 -0
  261. package/dist/radio/radio.styles.ts +34 -0
  262. package/dist/radio/radio.ts +70 -0
  263. package/dist/reaction-bar/index.ts +14 -0
  264. package/dist/reaction-bar/reaction-bar.styles.ts +23 -0
  265. package/dist/reaction-bar/reaction-bar.ts +87 -0
  266. package/dist/resizable-layout/index.ts +12 -0
  267. package/dist/resizable-layout/resizable-layout.styles.ts +50 -0
  268. package/dist/resizable-layout/resizable-layout.ts +139 -0
  269. package/dist/rich-editor/editor.ts +342 -0
  270. package/dist/rich-editor/emoji.ts +175 -0
  271. package/dist/rich-editor/history.ts +79 -0
  272. package/dist/rich-editor/index.ts +106 -0
  273. package/dist/rich-editor/markdown-shortcuts.ts +248 -0
  274. package/dist/rich-editor/mentions.ts +162 -0
  275. package/dist/rich-editor/model.ts +206 -0
  276. package/dist/rich-editor/operations.ts +772 -0
  277. package/dist/rich-editor/selection.ts +157 -0
  278. package/dist/rich-editor/serialization.ts +711 -0
  279. package/dist/rich-editor/slash-commands.ts +212 -0
  280. package/dist/search-bar/index.ts +7 -0
  281. package/dist/search-bar/search-bar.styles.ts +29 -0
  282. package/dist/search-bar/search-bar.ts +130 -0
  283. package/dist/select/index.ts +13 -0
  284. package/dist/select/select.styles.ts +52 -0
  285. package/dist/select/select.ts +152 -0
  286. package/dist/sidebar/index.ts +9 -0
  287. package/dist/sidebar/sidebar.styles.ts +27 -0
  288. package/dist/sidebar/sidebar.ts +59 -0
  289. package/dist/skeleton/index.ts +8 -0
  290. package/dist/skeleton/skeleton.styles.ts +16 -0
  291. package/dist/skeleton/skeleton.ts +39 -0
  292. package/dist/skip-to-content/index.ts +1 -0
  293. package/dist/skip-to-content/skip-to-content.ts +1 -0
  294. package/dist/slide-viewer/index.ts +16 -0
  295. package/dist/slide-viewer/slide-viewer.styles.ts +57 -0
  296. package/dist/slide-viewer/slide-viewer.ts +152 -0
  297. package/dist/slider/index.ts +1 -0
  298. package/dist/slider/slider.ts +2 -0
  299. package/dist/status-indicator/index.ts +15 -0
  300. package/dist/status-indicator/status-indicator.styles.ts +41 -0
  301. package/dist/status-indicator/status-indicator.ts +66 -0
  302. package/dist/steps/index.ts +1 -0
  303. package/dist/steps/steps.ts +1 -0
  304. package/dist/switch/index.ts +7 -0
  305. package/dist/switch/switch.styles.ts +55 -0
  306. package/dist/switch/switch.ts +63 -0
  307. package/dist/table-of-contents/index.ts +1 -0
  308. package/dist/table-of-contents/table-of-contents.ts +37 -0
  309. package/dist/tabs/index.ts +8 -0
  310. package/dist/tabs/tabs.styles.ts +9 -0
  311. package/dist/tabs/tabs.ts +133 -0
  312. package/dist/textarea/index.ts +7 -0
  313. package/dist/textarea/textarea.styles.ts +15 -0
  314. package/dist/textarea/textarea.ts +59 -0
  315. package/dist/theme/dom-adapters.ts +60 -0
  316. package/dist/theme/index.ts +18 -0
  317. package/dist/theme/theme-machine.ts +130 -0
  318. package/dist/theme/theme-script.ts +18 -0
  319. package/dist/thread-view/index.ts +26 -0
  320. package/dist/thread-view/thread-view.styles.ts +35 -0
  321. package/dist/thread-view/thread-view.ts +176 -0
  322. package/dist/toast/index.ts +13 -0
  323. package/dist/toast/toast.styles.ts +19 -0
  324. package/dist/toast/toast.ts +203 -0
  325. package/dist/tooltip/index.ts +7 -0
  326. package/dist/tooltip/tooltip.styles.ts +16 -0
  327. package/dist/tooltip/tooltip.ts +109 -0
  328. package/dist/version-selector/index.ts +12 -0
  329. package/dist/version-selector/version-selector.styles.ts +41 -0
  330. package/dist/version-selector/version-selector.ts +136 -0
  331. package/dist/video-player/index.ts +12 -0
  332. package/dist/video-player/video-player.styles.ts +42 -0
  333. package/dist/video-player/video-player.ts +100 -0
  334. package/package.json +1 -1
@@ -0,0 +1,19 @@
1
+ import { cva } from '../shared/index.ts'
2
+
3
+ export const toastVariants = cva({
4
+ base: 'pointer-events-auto relative flex w-full items-center justify-between gap-2 overflow-hidden rounded-lg border p-4 shadow-lg transition-all',
5
+ variants: {
6
+ variant: {
7
+ default: 'border bg-background text-foreground',
8
+ success:
9
+ 'border-l-4 border-green-500/50 bg-green-50 text-green-900 dark:bg-green-950 dark:text-green-100',
10
+ error:
11
+ 'border-l-4 border-red-500/50 bg-red-50 text-red-900 dark:bg-red-950 dark:text-red-100',
12
+ warning:
13
+ 'border-l-4 border-amber-500/50 bg-amber-50 text-amber-900 dark:bg-amber-950 dark:text-amber-100',
14
+ },
15
+ },
16
+ defaultVariants: {
17
+ variant: 'default',
18
+ },
19
+ })
@@ -0,0 +1,203 @@
1
+ import type { AccessibilityProps } from '../shared/index.ts'
2
+ import { generateId } from '../shared/index.ts'
3
+
4
+ export type ToastVariant = 'default' | 'success' | 'error' | 'warning'
5
+
6
+ /** Icon name for each toast variant — non-color indicator for colorblind safety */
7
+ const TOAST_ICONS: Record<ToastVariant, string> = {
8
+ default: 'info-circle',
9
+ success: 'check-circle',
10
+ error: 'x-circle',
11
+ warning: 'alert-triangle',
12
+ }
13
+
14
+ /** Get the icon name for a toast variant (colorblind-safe: meaning not conveyed by color alone) */
15
+ export function getToastIcon(variant: ToastVariant): string {
16
+ return TOAST_ICONS[variant]
17
+ }
18
+
19
+ export interface ToastProps {
20
+ /** Visual variant */
21
+ variant?: ToastVariant
22
+ /** Auto-dismiss duration in ms (default: 3000). Set to 0 to disable. */
23
+ duration?: number
24
+ /** Whether the toast is open (controlled) */
25
+ open?: boolean
26
+ /** Callback when open state changes */
27
+ onOpenChange?: (open: boolean) => void
28
+ }
29
+
30
+ export interface ToastState {
31
+ open: boolean
32
+ variant: ToastVariant
33
+ }
34
+
35
+ export interface ToastAPI {
36
+ /** Current toast state */
37
+ state: ToastState
38
+ /** ARIA attributes for the toast element */
39
+ ariaProps: Partial<AccessibilityProps>
40
+ /** Dismiss the toast */
41
+ dismiss(): void
42
+ /** Start the auto-dismiss timer */
43
+ startTimer(): void
44
+ /** Pause the auto-dismiss timer (e.g. on hover) */
45
+ pauseTimer(): void
46
+ /** Resume the auto-dismiss timer */
47
+ resumeTimer(): void
48
+ }
49
+
50
+ export function createToast(props: ToastProps = {}): ToastAPI {
51
+ const {
52
+ variant = 'default',
53
+ duration = 3000,
54
+ open: controlledOpen,
55
+ onOpenChange,
56
+ } = props
57
+
58
+ let isOpen = controlledOpen ?? true
59
+ let timerId: ReturnType<typeof setTimeout> | null = null
60
+ let remaining = duration
61
+ let startedAt = 0
62
+
63
+ function dismiss(): void {
64
+ clearTimer()
65
+ isOpen = false
66
+ onOpenChange?.(false)
67
+ }
68
+
69
+ function clearTimer(): void {
70
+ if (timerId !== null) {
71
+ clearTimeout(timerId)
72
+ timerId = null
73
+ }
74
+ }
75
+
76
+ function startTimer(): void {
77
+ if (duration <= 0) return
78
+ clearTimer()
79
+ remaining = duration
80
+ startedAt = Date.now()
81
+ timerId = setTimeout(dismiss, remaining)
82
+ }
83
+
84
+ function pauseTimer(): void {
85
+ if (timerId === null) return
86
+ clearTimer()
87
+ remaining = remaining - (Date.now() - startedAt)
88
+ if (remaining < 0) remaining = 0
89
+ }
90
+
91
+ function resumeTimer(): void {
92
+ if (duration <= 0 || remaining <= 0) return
93
+ clearTimer()
94
+ startedAt = Date.now()
95
+ timerId = setTimeout(dismiss, remaining)
96
+ }
97
+
98
+ const ariaProps: Partial<AccessibilityProps> = {
99
+ role: 'alert',
100
+ 'aria-live': 'assertive',
101
+ 'aria-atomic': true,
102
+ }
103
+
104
+ return {
105
+ state: {
106
+ get open() {
107
+ return controlledOpen !== undefined ? controlledOpen : isOpen
108
+ },
109
+ variant,
110
+ },
111
+ ariaProps,
112
+ dismiss,
113
+ startTimer,
114
+ pauseTimer,
115
+ resumeTimer,
116
+ }
117
+ }
118
+
119
+ /** Entry in the toast manager */
120
+ export interface ToastEntry {
121
+ id: string
122
+ message: string
123
+ variant: ToastVariant
124
+ duration: number
125
+ createdAt: number
126
+ }
127
+
128
+ export interface ToastManagerAPI {
129
+ /** Add a new toast. Returns the toast id. */
130
+ toast(message: string, opts?: { variant?: ToastVariant; duration?: number }): string
131
+ /** Dismiss a toast by id */
132
+ dismiss(id: string): void
133
+ /** Current toast list (read-only) */
134
+ readonly toasts: ReadonlyArray<ToastEntry>
135
+ /** Subscribe to toast list changes. Returns unsubscribe function. */
136
+ subscribe(fn: (toasts: ReadonlyArray<ToastEntry>) => void): () => void
137
+ }
138
+
139
+ export function createToastManager(): ToastManagerAPI {
140
+ let toasts: ToastEntry[] = []
141
+ const listeners = new Set<(toasts: ReadonlyArray<ToastEntry>) => void>()
142
+ const timers = new Map<string, ReturnType<typeof setTimeout>>()
143
+
144
+ function notify(): void {
145
+ const snapshot = [...toasts]
146
+ for (const fn of listeners) {
147
+ fn(snapshot)
148
+ }
149
+ }
150
+
151
+ function dismiss(id: string): void {
152
+ const timer = timers.get(id)
153
+ if (timer) {
154
+ clearTimeout(timer)
155
+ timers.delete(id)
156
+ }
157
+ toasts = toasts.filter((t) => t.id !== id)
158
+ notify()
159
+ }
160
+
161
+ function toast(
162
+ message: string,
163
+ opts?: { variant?: ToastVariant; duration?: number },
164
+ ): string {
165
+ const id = generateId('rfr-toast')
166
+ const variant = opts?.variant ?? 'default'
167
+ const duration = opts?.duration ?? 3000
168
+
169
+ const entry: ToastEntry = {
170
+ id,
171
+ message,
172
+ variant,
173
+ duration,
174
+ createdAt: Date.now(),
175
+ }
176
+
177
+ toasts = [...toasts, entry]
178
+ notify()
179
+
180
+ if (duration > 0) {
181
+ const timer = setTimeout(() => dismiss(id), duration)
182
+ timers.set(id, timer)
183
+ }
184
+
185
+ return id
186
+ }
187
+
188
+ function subscribe(fn: (toasts: ReadonlyArray<ToastEntry>) => void): () => void {
189
+ listeners.add(fn)
190
+ return () => {
191
+ listeners.delete(fn)
192
+ }
193
+ }
194
+
195
+ return {
196
+ toast,
197
+ dismiss,
198
+ get toasts() {
199
+ return toasts
200
+ },
201
+ subscribe,
202
+ }
203
+ }
@@ -0,0 +1,7 @@
1
+ export {
2
+ createTooltip,
3
+ type TooltipProps,
4
+ type TooltipAPI,
5
+ } from './tooltip.js'
6
+
7
+ export { tooltipContentVariants } from './tooltip.styles.js'
@@ -0,0 +1,16 @@
1
+ import { cva } from '../shared/index.ts'
2
+
3
+ export const tooltipContentVariants = cva({
4
+ base: 'z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground shadow-md',
5
+ variants: {
6
+ side: {
7
+ top: 'animate-slide-down-fade',
8
+ right: 'animate-slide-left-fade',
9
+ bottom: 'animate-slide-up-fade',
10
+ left: 'animate-slide-right-fade',
11
+ },
12
+ },
13
+ defaultVariants: {
14
+ side: 'top',
15
+ },
16
+ })
@@ -0,0 +1,109 @@
1
+ import type { AccessibilityProps, Side } from '../shared/index.ts'
2
+ import { generateId } from '../shared/index.ts'
3
+
4
+ export interface TooltipProps {
5
+ open?: boolean
6
+ defaultOpen?: boolean
7
+ onOpenChange?: (open: boolean) => void
8
+ placement?: Side
9
+ delayDuration?: number
10
+ }
11
+
12
+ export interface TooltipAPI {
13
+ /** Current open state */
14
+ state: { open: boolean }
15
+ /** Props to spread on the trigger element */
16
+ triggerProps: Partial<AccessibilityProps> & {
17
+ 'aria-describedby': string
18
+ }
19
+ /** Props to spread on the content element */
20
+ contentProps: {
21
+ role: 'tooltip'
22
+ id: string
23
+ }
24
+ /** Resolved placement */
25
+ placement: Side
26
+ /** Open the tooltip */
27
+ open: () => void
28
+ /** Close the tooltip (and clear any pending delay) */
29
+ close: () => void
30
+ /** Open with delay */
31
+ openWithDelay: () => void
32
+ /** The configured delay duration */
33
+ delayDuration: number
34
+ /** Cancel any pending delay timer */
35
+ cancelDelay: () => void
36
+ }
37
+
38
+ export function createTooltip(props: TooltipProps = {}): TooltipAPI {
39
+ const {
40
+ open: controlledOpen,
41
+ defaultOpen = false,
42
+ onOpenChange,
43
+ placement = 'top',
44
+ delayDuration = 300,
45
+ } = props
46
+
47
+ const contentId = generateId('rfr-tooltip')
48
+
49
+ let isOpen = controlledOpen ?? defaultOpen
50
+ let delayTimer: ReturnType<typeof setTimeout> | null = null
51
+
52
+ const state = { open: isOpen }
53
+
54
+ function setOpen(value: boolean) {
55
+ isOpen = value
56
+ state.open = value
57
+ onOpenChange?.(value)
58
+ }
59
+
60
+ function cancelDelay() {
61
+ if (delayTimer !== null) {
62
+ clearTimeout(delayTimer)
63
+ delayTimer = null
64
+ }
65
+ }
66
+
67
+ function open() {
68
+ cancelDelay()
69
+ setOpen(true)
70
+ }
71
+
72
+ function close() {
73
+ cancelDelay()
74
+ setOpen(false)
75
+ }
76
+
77
+ function openWithDelay() {
78
+ cancelDelay()
79
+ if (delayDuration <= 0) {
80
+ setOpen(true)
81
+ return
82
+ }
83
+ delayTimer = setTimeout(() => {
84
+ setOpen(true)
85
+ delayTimer = null
86
+ }, delayDuration)
87
+ }
88
+
89
+ const triggerProps = {
90
+ 'aria-describedby': contentId,
91
+ }
92
+
93
+ const contentProps = {
94
+ role: 'tooltip' as const,
95
+ id: contentId,
96
+ }
97
+
98
+ return {
99
+ state,
100
+ triggerProps,
101
+ contentProps,
102
+ placement,
103
+ open,
104
+ close,
105
+ openWithDelay,
106
+ delayDuration,
107
+ cancelDelay,
108
+ }
109
+ }
@@ -0,0 +1,12 @@
1
+ export {
2
+ createVersionSelector,
3
+ type VersionSelectorProps,
4
+ type VersionSelectorAPI,
5
+ type VersionOption,
6
+ } from './version-selector.js'
7
+
8
+ export {
9
+ versionSelectorVariants,
10
+ optionVariants,
11
+ latestBadgeVariants,
12
+ } from './version-selector.styles.js'
@@ -0,0 +1,41 @@
1
+ import { cva } from '../shared/index.ts'
2
+
3
+ export const versionSelectorVariants = cva({
4
+ base: 'relative inline-flex items-center justify-between rounded-md border bg-background text-foreground cursor-pointer',
5
+ variants: {
6
+ size: {
7
+ sm: 'h-8 text-sm px-2 min-w-[100px]',
8
+ md: 'h-10 text-base px-3 min-w-[140px]',
9
+ lg: 'h-12 text-lg px-4 min-w-[180px]',
10
+ },
11
+ },
12
+ defaultVariants: {
13
+ size: 'md',
14
+ },
15
+ })
16
+
17
+ export const optionVariants = cva({
18
+ base: 'flex items-center gap-2 px-3 py-2 cursor-pointer hover:bg-accent',
19
+ variants: {
20
+ selected: {
21
+ true: 'bg-accent/50 font-medium',
22
+ false: '',
23
+ },
24
+ },
25
+ defaultVariants: {
26
+ selected: 'false',
27
+ },
28
+ })
29
+
30
+ export const latestBadgeVariants = cva({
31
+ base: 'inline-flex items-center rounded-full text-xs font-medium px-2 py-0.5',
32
+ variants: {
33
+ variant: {
34
+ default: 'bg-primary/10 text-primary',
35
+ accent: 'bg-accent text-accent-foreground',
36
+ },
37
+ },
38
+ defaultVariants: {
39
+ variant: 'default',
40
+ },
41
+ })
@@ -0,0 +1,136 @@
1
+ import type { KeyboardHandlerMap } from '../shared/index.ts'
2
+ import { generateId, Keys, createKeyboardHandler } from '../shared/index.ts'
3
+
4
+ export interface VersionOption {
5
+ value: string
6
+ label: string
7
+ isLatest?: boolean
8
+ }
9
+
10
+ export interface VersionSelectorProps {
11
+ value?: string
12
+ onValueChange?: (value: string) => void
13
+ versions: VersionOption[]
14
+ }
15
+
16
+ export interface VersionSelectorAPI {
17
+ /** Current state */
18
+ state: { selectedVersion: string; isOpen: boolean }
19
+ /** Props to spread on the trigger element */
20
+ triggerProps: {
21
+ 'aria-expanded': boolean
22
+ 'aria-controls': string
23
+ 'aria-haspopup': 'listbox'
24
+ role: 'combobox'
25
+ }
26
+ /** Props to spread on the content/dropdown element */
27
+ contentProps: {
28
+ role: 'listbox'
29
+ id: string
30
+ }
31
+ /** Get props for an individual option */
32
+ getOptionProps: (value: string) => {
33
+ role: 'option'
34
+ 'aria-selected': boolean
35
+ 'data-value': string
36
+ 'data-latest'?: string
37
+ }
38
+ /** Select a version */
39
+ select: (value: string) => void
40
+ /** Open the dropdown */
41
+ open: () => void
42
+ /** Close the dropdown */
43
+ close: () => void
44
+ /** Keyboard handler map */
45
+ keyboardHandlers: KeyboardHandlerMap
46
+ /** Pre-built keyboard event handler */
47
+ handleKeyDown: (event: KeyboardEvent) => void
48
+ }
49
+
50
+ export function createVersionSelector(props: VersionSelectorProps): VersionSelectorAPI {
51
+ const {
52
+ value: initialValue = '',
53
+ onValueChange,
54
+ versions,
55
+ } = props
56
+
57
+ const contentId = generateId('rfr-ver-sel')
58
+
59
+ let selectedVersion = initialValue
60
+ let isOpen = false
61
+
62
+ const state = { selectedVersion, isOpen }
63
+
64
+ function setOpen(value: boolean) {
65
+ isOpen = value
66
+ state.isOpen = value
67
+ }
68
+
69
+ function select(value: string) {
70
+ selectedVersion = value
71
+ state.selectedVersion = value
72
+ onValueChange?.(value)
73
+ setOpen(false)
74
+ }
75
+
76
+ const triggerProps = {
77
+ 'aria-expanded': isOpen,
78
+ 'aria-controls': contentId,
79
+ 'aria-haspopup': 'listbox' as const,
80
+ role: 'combobox' as const,
81
+ }
82
+
83
+ const contentProps = {
84
+ role: 'listbox' as const,
85
+ id: contentId,
86
+ }
87
+
88
+ function getOptionProps(value: string) {
89
+ const version = versions.find((v) => v.value === value)
90
+ const base = {
91
+ role: 'option' as const,
92
+ 'aria-selected': selectedVersion === value,
93
+ 'data-value': value,
94
+ }
95
+ if (version?.isLatest) {
96
+ return { ...base, 'data-latest': 'true' }
97
+ }
98
+ return base
99
+ }
100
+
101
+ const keyboardHandlers: KeyboardHandlerMap = {
102
+ [Keys.Escape]: () => {
103
+ setOpen(false)
104
+ },
105
+ [Keys.Enter]: () => {
106
+ setOpen(!isOpen)
107
+ },
108
+ [Keys.Space]: () => {
109
+ setOpen(!isOpen)
110
+ },
111
+ [Keys.ArrowDown]: () => {
112
+ if (!isOpen) {
113
+ setOpen(true)
114
+ }
115
+ },
116
+ [Keys.ArrowUp]: () => {
117
+ if (!isOpen) {
118
+ setOpen(true)
119
+ }
120
+ },
121
+ }
122
+
123
+ const handleKeyDown = createKeyboardHandler(keyboardHandlers)
124
+
125
+ return {
126
+ state,
127
+ triggerProps,
128
+ contentProps,
129
+ getOptionProps,
130
+ select,
131
+ open: () => setOpen(true),
132
+ close: () => setOpen(false),
133
+ keyboardHandlers,
134
+ handleKeyDown,
135
+ }
136
+ }
@@ -0,0 +1,12 @@
1
+ export {
2
+ createVideoPlayer,
3
+ type VideoPlayerProps,
4
+ type VideoState,
5
+ type VideoPlayerAPI,
6
+ } from './video-player.js'
7
+
8
+ export {
9
+ playerVariants,
10
+ controlsVariants,
11
+ overlayVariants,
12
+ } from './video-player.styles.js'
@@ -0,0 +1,42 @@
1
+ import { cva } from '../shared/index.ts'
2
+
3
+ export const playerVariants = cva({
4
+ base: 'relative overflow-hidden rounded-lg bg-black',
5
+ variants: {
6
+ size: {
7
+ sm: 'max-w-sm',
8
+ md: 'max-w-2xl',
9
+ lg: 'max-w-4xl',
10
+ full: 'w-full',
11
+ },
12
+ },
13
+ defaultVariants: {
14
+ size: 'full',
15
+ },
16
+ })
17
+
18
+ export const controlsVariants = cva({
19
+ base: 'absolute bottom-0 left-0 right-0 flex items-center gap-2 bg-gradient-to-t from-black/80 to-transparent p-4 transition-opacity',
20
+ variants: {
21
+ visibility: {
22
+ visible: 'opacity-100',
23
+ hidden: 'opacity-0 pointer-events-none',
24
+ },
25
+ },
26
+ defaultVariants: {
27
+ visibility: 'visible',
28
+ },
29
+ })
30
+
31
+ export const overlayVariants = cva({
32
+ base: 'absolute inset-0 flex items-center justify-center bg-black/40 transition-opacity',
33
+ variants: {
34
+ visibility: {
35
+ visible: 'opacity-100',
36
+ hidden: 'opacity-0 pointer-events-none',
37
+ },
38
+ },
39
+ defaultVariants: {
40
+ visibility: 'visible',
41
+ },
42
+ })
@@ -0,0 +1,100 @@
1
+ import type { AccessibilityProps } from '../shared/index.ts'
2
+
3
+ export interface VideoPlayerProps {
4
+ src?: string
5
+ poster?: string
6
+ autoplay?: boolean
7
+ muted?: boolean
8
+ controls?: boolean
9
+ }
10
+
11
+ export type VideoState = 'idle' | 'loading' | 'playing' | 'paused' | 'ended'
12
+
13
+ export interface VideoPlayerAPI {
14
+ /** Current player state */
15
+ state: VideoState
16
+ /** Transition to playing state */
17
+ play: () => void
18
+ /** Transition to paused state */
19
+ pause: () => void
20
+ /** Toggle between playing and paused */
21
+ togglePlay: () => void
22
+ /** Toggle mute state */
23
+ toggleMute: () => boolean
24
+ /** ARIA props for the player region */
25
+ ariaProps: Partial<AccessibilityProps>
26
+ /** ARIA props for control buttons */
27
+ controlAriaProps: {
28
+ playPause: Partial<AccessibilityProps>
29
+ mute: Partial<AccessibilityProps>
30
+ }
31
+ /** Data attributes reflecting current state */
32
+ dataAttributes: Record<string, string>
33
+ }
34
+
35
+ export function createVideoPlayer(props: VideoPlayerProps = {}): VideoPlayerAPI {
36
+ const { muted: initialMuted = false } = props
37
+
38
+ let state: VideoState = 'idle'
39
+ let isMuted = initialMuted
40
+
41
+ function play() {
42
+ if (state === 'ended') {
43
+ state = 'playing'
44
+ } else if (state !== 'playing') {
45
+ state = 'playing'
46
+ }
47
+ }
48
+
49
+ function pause() {
50
+ if (state === 'playing') {
51
+ state = 'paused'
52
+ }
53
+ }
54
+
55
+ function togglePlay() {
56
+ if (state === 'playing') {
57
+ pause()
58
+ } else {
59
+ play()
60
+ }
61
+ }
62
+
63
+ function toggleMute(): boolean {
64
+ isMuted = !isMuted
65
+ return isMuted
66
+ }
67
+
68
+ const ariaProps: Partial<AccessibilityProps> = {
69
+ role: 'region',
70
+ 'aria-label': 'Video player',
71
+ }
72
+
73
+ const api: VideoPlayerAPI = {
74
+ get state() {
75
+ return state
76
+ },
77
+ play,
78
+ pause,
79
+ togglePlay,
80
+ toggleMute,
81
+ ariaProps,
82
+ get controlAriaProps() {
83
+ return {
84
+ playPause: {
85
+ 'aria-label': state === 'playing' ? 'Pause' : 'Play',
86
+ },
87
+ mute: {
88
+ 'aria-label': isMuted ? 'Unmute' : 'Mute',
89
+ },
90
+ }
91
+ },
92
+ get dataAttributes() {
93
+ return {
94
+ 'data-state': state,
95
+ }
96
+ },
97
+ }
98
+
99
+ return api
100
+ }