@refraction-ui/astro 0.4.0 → 0.4.2

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 (246) hide show
  1. package/dist/astro-accordion/Button.astro +55 -0
  2. package/dist/astro-accordion/index.ts +1 -0
  3. package/dist/astro-ai/AIScript.astro +69 -0
  4. package/dist/astro-ai/TTSScript.astro +60 -0
  5. package/dist/astro-ai/index.ts +16 -0
  6. package/dist/astro-animated-text/AnimatedText.astro +81 -0
  7. package/dist/astro-animated-text/TypewriterText.astro +90 -0
  8. package/dist/astro-animated-text/index.ts +1 -0
  9. package/dist/astro-app-shell/AppShell.astro +195 -0
  10. package/dist/astro-app-shell/AppShellContent.astro +29 -0
  11. package/dist/astro-app-shell/AppShellHeader.astro +54 -0
  12. package/dist/astro-app-shell/AppShellMain.astro +18 -0
  13. package/dist/astro-app-shell/AppShellMobileNav.astro +27 -0
  14. package/dist/astro-app-shell/AppShellOverlay.astro +21 -0
  15. package/dist/astro-app-shell/AppShellSidebar.astro +26 -0
  16. package/dist/astro-app-shell/AuthShell.astro +31 -0
  17. package/dist/astro-app-shell/AuthShellCard.astro +24 -0
  18. package/dist/astro-app-shell/PageShell.astro +36 -0
  19. package/dist/astro-app-shell/PageShellFooter.astro +30 -0
  20. package/dist/astro-app-shell/PageShellNav.astro +33 -0
  21. package/dist/astro-app-shell/PageShellSection.astro +32 -0
  22. package/dist/astro-app-shell/index.ts +21 -0
  23. package/dist/astro-auth/AuthGuard.astro +133 -0
  24. package/dist/astro-auth/index.ts +5 -0
  25. package/dist/astro-avatar/Avatar.astro +22 -0
  26. package/dist/astro-avatar/AvatarFallback.astro +18 -0
  27. package/dist/astro-avatar/AvatarImage.astro +48 -0
  28. package/dist/astro-avatar/index.ts +1 -0
  29. package/dist/astro-avatar-group/AvatarGroup.astro +42 -0
  30. package/dist/astro-avatar-group/index.ts +1 -0
  31. package/dist/astro-badge/Badge.astro +27 -0
  32. package/dist/astro-badge/index.ts +1 -0
  33. package/dist/astro-bottom-nav/BottomNav.astro +40 -0
  34. package/dist/astro-bottom-nav/index.ts +1 -0
  35. package/dist/astro-breadcrumbs/Breadcrumbs.astro +56 -0
  36. package/dist/astro-breadcrumbs/index.ts +1 -0
  37. package/dist/astro-button/Button.astro +55 -0
  38. package/dist/astro-button/index.ts +1 -0
  39. package/dist/astro-calendar/Calendar.astro +259 -0
  40. package/dist/astro-calendar/index.ts +1 -0
  41. package/dist/astro-callout/Callout.astro +3 -0
  42. package/dist/astro-callout/index.ts +1 -0
  43. package/dist/astro-card/Card.astro +20 -0
  44. package/dist/astro-card/CardContent.astro +13 -0
  45. package/dist/astro-card/CardDescription.astro +13 -0
  46. package/dist/astro-card/CardFooter.astro +13 -0
  47. package/dist/astro-card/CardHeader.astro +13 -0
  48. package/dist/astro-card/CardTitle.astro +15 -0
  49. package/dist/astro-card/index.ts +1 -0
  50. package/dist/astro-card-grid/CardGrid.astro +17 -0
  51. package/dist/astro-card-grid/index.ts +1 -0
  52. package/dist/astro-carousel/Button.astro +55 -0
  53. package/dist/astro-carousel/index.ts +1 -0
  54. package/dist/astro-charts/Bars.astro +53 -0
  55. package/dist/astro-charts/Chart.astro +31 -0
  56. package/dist/astro-charts/Circles.astro +48 -0
  57. package/dist/astro-charts/Gradient.astro +34 -0
  58. package/dist/astro-charts/Histogram.astro +107 -0
  59. package/dist/astro-charts/Line.astro +55 -0
  60. package/dist/astro-charts/PieChart.astro +63 -0
  61. package/dist/astro-charts/ScatterPlot.astro +95 -0
  62. package/dist/astro-charts/XAxis.astro +46 -0
  63. package/dist/astro-charts/YAxis.astro +44 -0
  64. package/dist/astro-charts/index.ts +26 -0
  65. package/dist/astro-checkbox/Checkbox.astro +129 -0
  66. package/dist/astro-checkbox/index.ts +1 -0
  67. package/dist/astro-code-block/CodeBlock.astro +3 -0
  68. package/dist/astro-code-block/index.ts +1 -0
  69. package/dist/astro-code-editor/CodeEditor.astro +123 -0
  70. package/dist/astro-code-editor/index.ts +1 -0
  71. package/dist/astro-collapsible/Collapsible.astro +58 -0
  72. package/dist/astro-collapsible/CollapsibleContent.astro +21 -0
  73. package/dist/astro-collapsible/CollapsibleTrigger.astro +17 -0
  74. package/dist/astro-collapsible/index.ts +1 -0
  75. package/dist/astro-command/Command.astro +170 -0
  76. package/dist/astro-command/CommandEmpty.astro +17 -0
  77. package/dist/astro-command/CommandGroup.astro +25 -0
  78. package/dist/astro-command/CommandInput.astro +21 -0
  79. package/dist/astro-command/CommandItem.astro +26 -0
  80. package/dist/astro-command/CommandList.astro +17 -0
  81. package/dist/astro-command/CommandSeparator.astro +14 -0
  82. package/dist/astro-command/index.ts +1 -0
  83. package/dist/astro-command-input/CommandInput.astro +6 -0
  84. package/dist/astro-command-input/index.ts +1 -0
  85. package/dist/astro-content-protection/ContentProtection.astro +104 -0
  86. package/dist/astro-content-protection/index.ts +1 -0
  87. package/dist/astro-data-table/DataTable.astro +263 -0
  88. package/dist/astro-data-table/index.ts +1 -0
  89. package/dist/astro-date-picker/DatePicker.astro +398 -0
  90. package/dist/astro-date-picker/index.ts +1 -0
  91. package/dist/astro-device-frame/DeviceFrame.astro +44 -0
  92. package/dist/astro-device-frame/index.ts +1 -0
  93. package/dist/astro-dialog/Dialog.astro +138 -0
  94. package/dist/astro-dialog/DialogClose.astro +16 -0
  95. package/dist/astro-dialog/DialogContent.astro +24 -0
  96. package/dist/astro-dialog/DialogDescription.astro +15 -0
  97. package/dist/astro-dialog/DialogFooter.astro +14 -0
  98. package/dist/astro-dialog/DialogHeader.astro +14 -0
  99. package/dist/astro-dialog/DialogOverlay.astro +17 -0
  100. package/dist/astro-dialog/DialogTitle.astro +15 -0
  101. package/dist/astro-dialog/DialogTrigger.astro +18 -0
  102. package/dist/astro-dialog/index.ts +1 -0
  103. package/dist/astro-diff-viewer/DiffViewer.astro +371 -0
  104. package/dist/astro-diff-viewer/index.ts +1 -0
  105. package/dist/astro-dropdown-menu/DropdownMenu.astro +138 -0
  106. package/dist/astro-dropdown-menu/DropdownMenuContent.astro +20 -0
  107. package/dist/astro-dropdown-menu/DropdownMenuItem.astro +21 -0
  108. package/dist/astro-dropdown-menu/DropdownMenuLabel.astro +14 -0
  109. package/dist/astro-dropdown-menu/DropdownMenuSeparator.astro +14 -0
  110. package/dist/astro-dropdown-menu/DropdownMenuTrigger.astro +18 -0
  111. package/dist/astro-dropdown-menu/index.ts +1 -0
  112. package/dist/astro-emoji-picker/EmojiPicker.astro +255 -0
  113. package/dist/astro-emoji-picker/index.ts +1 -0
  114. package/dist/astro-feedback-dialog/FeedbackDialog.astro +152 -0
  115. package/dist/astro-feedback-dialog/index.ts +1 -0
  116. package/dist/astro-file-tree/astro-file-tree.astro +4 -0
  117. package/dist/astro-file-tree/index.ts +1 -0
  118. package/dist/astro-file-upload/FileUpload.astro +306 -0
  119. package/dist/astro-file-upload/index.ts +1 -0
  120. package/dist/astro-footer/Footer.astro +61 -0
  121. package/dist/astro-footer/index.ts +1 -0
  122. package/dist/astro-icon-system/astro-icon-system.astro +4 -0
  123. package/dist/astro-icon-system/index.ts +1 -0
  124. package/dist/astro-inline-editor/InlineEditor.astro +169 -0
  125. package/dist/astro-inline-editor/index.ts +1 -0
  126. package/dist/astro-input/Input.astro +49 -0
  127. package/dist/astro-input/index.ts +1 -0
  128. package/dist/astro-input-group/InputGroup.astro +29 -0
  129. package/dist/astro-input-group/InputGroupAddon.astro +14 -0
  130. package/dist/astro-input-group/InputGroupButton.astro +19 -0
  131. package/dist/astro-input-group/InputGroupText.astro +11 -0
  132. package/dist/astro-input-group/index.ts +1 -0
  133. package/dist/astro-install-prompt/InstallPrompt.astro +115 -0
  134. package/dist/astro-install-prompt/index.ts +1 -0
  135. package/dist/astro-keyboard-shortcut/KeyboardShortcut.astro +83 -0
  136. package/dist/astro-keyboard-shortcut/ShortcutBadge.astro +41 -0
  137. package/dist/astro-keyboard-shortcut/index.ts +1 -0
  138. package/dist/astro-language-selector/LanguageSelector.astro +247 -0
  139. package/dist/astro-language-selector/index.ts +1 -0
  140. package/dist/astro-link-card/LinkCard.astro +17 -0
  141. package/dist/astro-link-card/index.ts +1 -0
  142. package/dist/astro-markdown-renderer/MarkdownRenderer.astro +62 -0
  143. package/dist/astro-markdown-renderer/index.ts +1 -0
  144. package/dist/astro-mobile-nav/MobileNav.astro +84 -0
  145. package/dist/astro-mobile-nav/MobileNavContent.astro +29 -0
  146. package/dist/astro-mobile-nav/MobileNavLink.astro +16 -0
  147. package/dist/astro-mobile-nav/MobileNavTrigger.astro +57 -0
  148. package/dist/astro-mobile-nav/index.ts +1 -0
  149. package/dist/astro-navbar/Navbar.astro +49 -0
  150. package/dist/astro-navbar/index.ts +1 -0
  151. package/dist/astro-otp-input/OtpInput.astro +253 -0
  152. package/dist/astro-otp-input/index.ts +1 -0
  153. package/dist/astro-pagination/Pagination.astro +3 -0
  154. package/dist/astro-pagination/index.ts +1 -0
  155. package/dist/astro-payment/Payment.astro +19 -0
  156. package/dist/astro-payment/index.ts +1 -0
  157. package/dist/astro-popover/Popover.astro +87 -0
  158. package/dist/astro-popover/PopoverClose.astro +16 -0
  159. package/dist/astro-popover/PopoverContent.astro +23 -0
  160. package/dist/astro-popover/PopoverTrigger.astro +18 -0
  161. package/dist/astro-popover/index.ts +1 -0
  162. package/dist/astro-presence-indicator/PresenceIndicator.astro +26 -0
  163. package/dist/astro-presence-indicator/index.ts +1 -0
  164. package/dist/astro-progress-display/BadgeDisplay.astro +33 -0
  165. package/dist/astro-progress-display/ProgressBar.astro +27 -0
  166. package/dist/astro-progress-display/StatsGrid.astro +32 -0
  167. package/dist/astro-progress-display/index.ts +1 -0
  168. package/dist/astro-radio/RadioGroup.astro +111 -0
  169. package/dist/astro-radio/RadioItem.astro +36 -0
  170. package/dist/astro-radio/index.ts +1 -0
  171. package/dist/astro-reaction-bar/ReactionBar.astro +117 -0
  172. package/dist/astro-reaction-bar/index.ts +1 -0
  173. package/dist/astro-resizable-layout/ResizableDivider.astro +27 -0
  174. package/dist/astro-resizable-layout/ResizableLayout.astro +162 -0
  175. package/dist/astro-resizable-layout/ResizablePane.astro +28 -0
  176. package/dist/astro-resizable-layout/index.ts +11 -0
  177. package/dist/astro-rich-editor/RichEditor.astro +260 -0
  178. package/dist/astro-rich-editor/index.ts +25 -0
  179. package/dist/astro-search-bar/SearchBar.astro +140 -0
  180. package/dist/astro-search-bar/SearchResultItem.astro +16 -0
  181. package/dist/astro-search-bar/SearchResults.astro +17 -0
  182. package/dist/astro-search-bar/index.ts +1 -0
  183. package/dist/astro-select/Select.astro +165 -0
  184. package/dist/astro-select/SelectContent.astro +19 -0
  185. package/dist/astro-select/SelectItem.astro +42 -0
  186. package/dist/astro-select/SelectTrigger.astro +36 -0
  187. package/dist/astro-select/index.ts +1 -0
  188. package/dist/astro-sidebar/Sidebar.astro +64 -0
  189. package/dist/astro-sidebar/index.ts +1 -0
  190. package/dist/astro-skeleton/Skeleton.astro +39 -0
  191. package/dist/astro-skeleton/SkeletonText.astro +22 -0
  192. package/dist/astro-skeleton/index.ts +1 -0
  193. package/dist/astro-skip-to-content/SkipToContent.astro +3 -0
  194. package/dist/astro-skip-to-content/index.ts +1 -0
  195. package/dist/astro-slide-viewer/SlideViewer.astro +231 -0
  196. package/dist/astro-slide-viewer/index.ts +1 -0
  197. package/dist/astro-slider/index.ts +1 -0
  198. package/dist/astro-status-indicator/StatusIndicator.astro +31 -0
  199. package/dist/astro-status-indicator/index.ts +1 -0
  200. package/dist/astro-steps/Steps.astro +3 -0
  201. package/dist/astro-steps/index.ts +1 -0
  202. package/dist/astro-switch/Switch.astro +89 -0
  203. package/dist/astro-switch/index.ts +1 -0
  204. package/dist/astro-table-of-contents/TableOfContents.astro +53 -0
  205. package/dist/astro-table-of-contents/index.ts +1 -0
  206. package/dist/astro-tabs/Tabs.astro +96 -0
  207. package/dist/astro-tabs/TabsContent.astro +20 -0
  208. package/dist/astro-tabs/TabsList.astro +16 -0
  209. package/dist/astro-tabs/TabsTrigger.astro +24 -0
  210. package/dist/astro-tabs/index.ts +1 -0
  211. package/dist/astro-textarea/Textarea.astro +51 -0
  212. package/dist/astro-textarea/index.ts +1 -0
  213. package/dist/astro-theme/ThemeScript.astro +13 -0
  214. package/dist/astro-theme/ThemeToggle.astro +279 -0
  215. package/dist/astro-theme/index.ts +1 -0
  216. package/dist/astro-thread-view/ThreadView.astro +159 -0
  217. package/dist/astro-thread-view/index.ts +1 -0
  218. package/dist/astro-toast/Toast.astro +49 -0
  219. package/dist/astro-toast/Toaster.astro +142 -0
  220. package/dist/astro-toast/index.ts +1 -0
  221. package/dist/astro-tooltip/Tooltip.astro +95 -0
  222. package/dist/astro-tooltip/TooltipContent.astro +22 -0
  223. package/dist/astro-tooltip/TooltipTrigger.astro +16 -0
  224. package/dist/astro-tooltip/index.ts +1 -0
  225. package/dist/astro-version-selector/VersionSelector.astro +174 -0
  226. package/dist/astro-version-selector/index.ts +1 -0
  227. package/dist/astro-video-player/VideoPlayer.astro +153 -0
  228. package/dist/astro-video-player/index.ts +1 -0
  229. package/dist/index.ts +77 -0
  230. package/dist/shared/a11y.ts +33 -0
  231. package/dist/shared/aria.ts +30 -0
  232. package/dist/shared/cn.ts +35 -0
  233. package/dist/shared/cva.ts +79 -0
  234. package/dist/shared/engine-adapter.ts +10 -0
  235. package/dist/shared/focus-trap.ts +123 -0
  236. package/dist/shared/index.ts +60 -0
  237. package/dist/shared/keyboard.ts +36 -0
  238. package/dist/shared/live-region.ts +90 -0
  239. package/dist/shared/motion.ts +15 -0
  240. package/dist/shared/skip-link.ts +35 -0
  241. package/dist/shared/state-machine.ts +57 -0
  242. package/dist/shared/tokens.ts +16 -0
  243. package/dist/shared/types.ts +45 -0
  244. package/dist/shared/variants.ts +17 -0
  245. package/package.json +20 -19
  246. package/src/index.ts +0 -94
@@ -0,0 +1,49 @@
1
+ ---
2
+ import { toastVariants, type ToastVariant } from '@refraction-ui/toast'
3
+ import { cn } from '../shared/index.ts'
4
+
5
+ interface Props extends astroHTML.JSX.HTMLAttributes {
6
+ /** Visual variant */
7
+ variant?: ToastVariant
8
+ /** Auto-dismiss duration in ms (default: 3000). Set to 0 to disable. */
9
+ duration?: number
10
+ /** Whether to show a dismiss button */
11
+ dismissible?: boolean
12
+ /** Toast message text (alternative to using the default slot) */
13
+ message?: string
14
+ }
15
+
16
+ const {
17
+ variant = 'default',
18
+ duration = 3000,
19
+ dismissible = true,
20
+ message,
21
+ class: className,
22
+ ...props
23
+ } = Astro.props
24
+ ---
25
+
26
+ <div
27
+ data-rfr-toast
28
+ data-rfr-toast-variant={variant}
29
+ data-rfr-toast-duration={duration}
30
+ role="alert"
31
+ aria-live="assertive"
32
+ aria-atomic="true"
33
+ class={cn(toastVariants({ variant }), className)}
34
+ {...props}
35
+ >
36
+ <div class="flex-1">
37
+ {message ? message : <slot />}
38
+ </div>
39
+ {dismissible && (
40
+ <button
41
+ type="button"
42
+ data-rfr-toast-dismiss
43
+ class="shrink-0"
44
+ aria-label="Dismiss"
45
+ >
46
+ &times;
47
+ </button>
48
+ )}
49
+ </div>
@@ -0,0 +1,142 @@
1
+ ---
2
+ import { cn } from '../shared/index.ts'
3
+
4
+ interface Props extends astroHTML.JSX.HTMLAttributes {}
5
+
6
+ const { class: className, ...props } = Astro.props
7
+ ---
8
+
9
+ <div
10
+ data-rfr-toaster
11
+ class={cn('fixed bottom-4 right-4 z-50 flex flex-col gap-2 w-full max-w-sm pointer-events-none', className)}
12
+ {...props}
13
+ >
14
+ <slot />
15
+ </div>
16
+
17
+ <script>
18
+ function initToaster() {
19
+ // Initialize all individual toasts (auto-dismiss, pause on hover)
20
+ document.querySelectorAll<HTMLElement>('[data-rfr-toast]').forEach((toast) => {
21
+ if (toast.hasAttribute('data-rfr-toast-init')) return
22
+ toast.setAttribute('data-rfr-toast-init', '')
23
+
24
+ const duration = parseInt(toast.getAttribute('data-rfr-toast-duration') || '3000', 10)
25
+ let timerId: ReturnType<typeof setTimeout> | null = null
26
+ let remaining = duration
27
+ let startedAt = 0
28
+
29
+ function dismiss() {
30
+ clearTimer()
31
+ toast.style.transition = 'opacity 200ms, transform 200ms'
32
+ toast.style.opacity = '0'
33
+ toast.style.transform = 'translateX(100%)'
34
+ setTimeout(() => {
35
+ toast.remove()
36
+ }, 200)
37
+ toast.dispatchEvent(new CustomEvent('rfr-toast-dismiss', { bubbles: true }))
38
+ }
39
+
40
+ function clearTimer() {
41
+ if (timerId !== null) {
42
+ clearTimeout(timerId)
43
+ timerId = null
44
+ }
45
+ }
46
+
47
+ function startTimer() {
48
+ if (duration <= 0) return
49
+ clearTimer()
50
+ remaining = duration
51
+ startedAt = Date.now()
52
+ timerId = setTimeout(dismiss, remaining)
53
+ }
54
+
55
+ function pauseTimer() {
56
+ if (timerId === null) return
57
+ clearTimer()
58
+ remaining = remaining - (Date.now() - startedAt)
59
+ if (remaining < 0) remaining = 0
60
+ }
61
+
62
+ function resumeTimer() {
63
+ if (duration <= 0 || remaining <= 0) return
64
+ clearTimer()
65
+ startedAt = Date.now()
66
+ timerId = setTimeout(dismiss, remaining)
67
+ }
68
+
69
+ // Dismiss button
70
+ const dismissBtn = toast.querySelector<HTMLElement>('[data-rfr-toast-dismiss]')
71
+ dismissBtn?.addEventListener('click', dismiss)
72
+
73
+ // Pause on hover, resume on leave
74
+ toast.addEventListener('mouseenter', pauseTimer)
75
+ toast.addEventListener('mouseleave', resumeTimer)
76
+
77
+ // Start auto-dismiss
78
+ startTimer()
79
+ })
80
+ }
81
+
82
+ // Expose a global function to programmatically add toasts
83
+ interface ToastOptions {
84
+ message: string
85
+ variant?: 'default' | 'success' | 'error' | 'warning'
86
+ duration?: number
87
+ }
88
+
89
+ const VARIANT_CLASSES: Record<string, string> = {
90
+ default: 'border bg-background text-foreground',
91
+ success: 'border-l-4 border-green-500/50 bg-green-50 text-green-900 dark:bg-green-950 dark:text-green-100',
92
+ error: 'border-l-4 border-red-500/50 bg-red-50 text-red-900 dark:bg-red-950 dark:text-red-100',
93
+ warning: 'border-l-4 border-amber-500/50 bg-amber-50 text-amber-900 dark:bg-amber-950 dark:text-amber-100',
94
+ }
95
+
96
+ function createToastElement(options: ToastOptions): HTMLElement {
97
+ const { message, variant = 'default', duration = 3000 } = options
98
+
99
+ const toast = document.createElement('div')
100
+ toast.setAttribute('data-rfr-toast', '')
101
+ toast.setAttribute('data-rfr-toast-variant', variant)
102
+ toast.setAttribute('data-rfr-toast-duration', String(duration))
103
+ toast.setAttribute('role', 'alert')
104
+ toast.setAttribute('aria-live', 'assertive')
105
+ toast.setAttribute('aria-atomic', 'true')
106
+ toast.className = `pointer-events-auto relative flex w-full items-center justify-between gap-2 overflow-hidden rounded-lg border p-4 shadow-lg transition-all ${VARIANT_CLASSES[variant] || VARIANT_CLASSES.default}`
107
+
108
+ const content = document.createElement('div')
109
+ content.className = 'flex-1'
110
+ content.textContent = message
111
+
112
+ const dismissBtn = document.createElement('button')
113
+ dismissBtn.type = 'button'
114
+ dismissBtn.setAttribute('data-rfr-toast-dismiss', '')
115
+ dismissBtn.className = 'shrink-0'
116
+ dismissBtn.setAttribute('aria-label', 'Dismiss')
117
+ dismissBtn.innerHTML = '&times;'
118
+
119
+ toast.appendChild(content)
120
+ toast.appendChild(dismissBtn)
121
+
122
+ return toast
123
+ }
124
+
125
+ ;(window as any).__rfr_toast = function(options: ToastOptions | string) {
126
+ const opts: ToastOptions = typeof options === 'string' ? { message: options } : options
127
+ const toaster = document.querySelector<HTMLElement>('[data-rfr-toaster]')
128
+ if (!toaster) {
129
+ console.warn('[refraction-ui] No <Toaster /> found in the document. Add a <Toaster /> component to use programmatic toasts.')
130
+ return
131
+ }
132
+
133
+ const toast = createToastElement(opts)
134
+ toaster.appendChild(toast)
135
+
136
+ // Re-init to attach event listeners to the new toast
137
+ initToaster()
138
+ }
139
+
140
+ initToaster()
141
+ document.addEventListener('astro:after-swap', initToaster)
142
+ </script>
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,95 @@
1
+ ---
2
+ import { cn } from '../shared/index.ts'
3
+
4
+ interface Props extends astroHTML.JSX.HTMLAttributes {
5
+ /** Delay before showing in ms (default: 300) */
6
+ delayDuration?: number
7
+ }
8
+
9
+ const { delayDuration = 300, class: className, ...props } = Astro.props
10
+ ---
11
+
12
+ <span
13
+ data-rfr-tooltip
14
+ data-rfr-tooltip-delay={delayDuration}
15
+ data-state="closed"
16
+ class={cn('relative inline-block', className)}
17
+ {...props}
18
+ >
19
+ <slot />
20
+ </span>
21
+
22
+ <script>
23
+ function initTooltips() {
24
+ document.querySelectorAll<HTMLElement>('[data-rfr-tooltip]').forEach((root) => {
25
+ if (root.hasAttribute('data-rfr-tooltip-init')) return
26
+ root.setAttribute('data-rfr-tooltip-init', '')
27
+
28
+ const delay = parseInt(root.getAttribute('data-rfr-tooltip-delay') || '300', 10)
29
+ let isOpen = false
30
+ let timer: ReturnType<typeof setTimeout> | null = null
31
+
32
+ const trigger = root.querySelector<HTMLElement>('[data-rfr-tooltip-trigger]')
33
+ const content = root.querySelector<HTMLElement>('[data-rfr-tooltip-content]')
34
+
35
+ function cancelTimer() {
36
+ if (timer !== null) {
37
+ clearTimeout(timer)
38
+ timer = null
39
+ }
40
+ }
41
+
42
+ function setState(open: boolean) {
43
+ isOpen = open
44
+ const state = open ? 'open' : 'closed'
45
+ root.setAttribute('data-state', state)
46
+ content?.setAttribute('data-state', state)
47
+
48
+ if (open) {
49
+ content?.removeAttribute('hidden')
50
+ } else {
51
+ content?.setAttribute('hidden', '')
52
+ }
53
+ }
54
+
55
+ function showWithDelay() {
56
+ cancelTimer()
57
+ if (delay <= 0) {
58
+ setState(true)
59
+ return
60
+ }
61
+ timer = setTimeout(() => {
62
+ setState(true)
63
+ timer = null
64
+ }, delay)
65
+ }
66
+
67
+ function hide() {
68
+ cancelTimer()
69
+ setState(false)
70
+ }
71
+
72
+ // Mouse events on trigger
73
+ trigger?.addEventListener('mouseenter', () => showWithDelay())
74
+ trigger?.addEventListener('mouseleave', () => hide())
75
+
76
+ // Focus events on trigger
77
+ trigger?.addEventListener('focus', () => showWithDelay())
78
+ trigger?.addEventListener('blur', () => hide())
79
+
80
+ // Escape key
81
+ trigger?.addEventListener('keydown', (e) => {
82
+ if (e.key === 'Escape' && isOpen) {
83
+ e.preventDefault()
84
+ hide()
85
+ }
86
+ })
87
+
88
+ // Set initial state
89
+ content?.setAttribute('hidden', '')
90
+ })
91
+ }
92
+
93
+ initTooltips()
94
+ document.addEventListener('astro:after-swap', initTooltips)
95
+ </script>
@@ -0,0 +1,22 @@
1
+ ---
2
+ import { tooltipContentVariants } from '@refraction-ui/tooltip'
3
+ import { cn } from '../shared/index.ts'
4
+ import type { Side } from '../shared/index.ts'
5
+
6
+ interface Props extends astroHTML.JSX.HTMLAttributes {
7
+ side?: Side
8
+ }
9
+
10
+ const { side = 'top', class: className, ...props } = Astro.props
11
+ ---
12
+
13
+ <div
14
+ data-rfr-tooltip-content
15
+ data-state="closed"
16
+ role="tooltip"
17
+ hidden
18
+ class={cn(tooltipContentVariants({ side }), className)}
19
+ {...props}
20
+ >
21
+ <slot />
22
+ </div>
@@ -0,0 +1,16 @@
1
+ ---
2
+ import { cn } from '../shared/index.ts'
3
+
4
+ interface Props extends astroHTML.JSX.HTMLAttributes {}
5
+
6
+ const { class: className, ...props } = Astro.props
7
+ ---
8
+
9
+ <span
10
+ data-rfr-tooltip-trigger
11
+ tabindex="0"
12
+ class={cn(className)}
13
+ {...props}
14
+ >
15
+ <slot />
16
+ </span>
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,174 @@
1
+ ---
2
+ import {
3
+ createVersionSelector,
4
+ versionSelectorVariants,
5
+ optionVariants,
6
+ latestBadgeVariants,
7
+ type VersionOption,
8
+ } from '@refraction-ui/version-selector'
9
+ import { cn } from '../shared/index.ts'
10
+
11
+ interface Props extends astroHTML.JSX.HTMLAttributes {
12
+ value?: string
13
+ versions: VersionOption[]
14
+ placeholder?: string
15
+ }
16
+
17
+ const {
18
+ value = '',
19
+ versions,
20
+ placeholder = 'Select version...',
21
+ class: className,
22
+ ...props
23
+ } = Astro.props
24
+
25
+ const api = createVersionSelector({ value, versions })
26
+ const selectedOpt = versions.find((v) => v.value === value)
27
+ const displayLabel = selectedOpt ? selectedOpt.label : placeholder
28
+ ---
29
+
30
+ <div
31
+ class={cn('rfr-version-selector relative inline-block', className)}
32
+ data-rfr-version-selector
33
+ data-rfr-versions={JSON.stringify(versions)}
34
+ data-rfr-version-value={value}
35
+ data-rfr-version-placeholder={placeholder}
36
+ {...props}
37
+ >
38
+ <!-- Trigger button -->
39
+ <button
40
+ type="button"
41
+ role={api.triggerProps.role}
42
+ aria-expanded="false"
43
+ aria-controls={api.triggerProps['aria-controls']}
44
+ aria-haspopup={api.triggerProps['aria-haspopup']}
45
+ class={versionSelectorVariants()}
46
+ data-rfr-version-trigger
47
+ >
48
+ <span data-rfr-version-label>{displayLabel}</span>
49
+ {selectedOpt?.isLatest && (
50
+ <span class={cn(latestBadgeVariants(), 'ml-2')} data-rfr-version-latest-badge>Latest</span>
51
+ )}
52
+ <span aria-hidden="true" class="ml-2">&#x25BE;</span>
53
+ </button>
54
+
55
+ <!-- Dropdown -->
56
+ <ul
57
+ role={api.contentProps.role}
58
+ id={api.contentProps.id}
59
+ class="absolute top-full left-0 w-full mt-1 rounded-md border bg-popover text-popover-foreground shadow-md z-50 overflow-auto max-h-60"
60
+ style="display:none"
61
+ data-rfr-version-dropdown
62
+ >
63
+ {versions.map((ver) => {
64
+ const optProps = api.getOptionProps(ver.value)
65
+ const isSelected = value === ver.value
66
+ return (
67
+ <li
68
+ role={optProps.role}
69
+ aria-selected={optProps['aria-selected']}
70
+ data-value={ver.value}
71
+ class={optionVariants({ selected: isSelected ? 'true' : 'false' })}
72
+ data-rfr-version-option
73
+ >
74
+ <span>{ver.label}</span>
75
+ {ver.isLatest && (
76
+ <span class={latestBadgeVariants()}>Latest</span>
77
+ )}
78
+ </li>
79
+ )
80
+ })}
81
+ </ul>
82
+ </div>
83
+
84
+ <script>
85
+ document.querySelectorAll<HTMLElement>('[data-rfr-version-selector]').forEach((root) => {
86
+ const versions: Array<{ value: string; label: string; isLatest?: boolean }> = JSON.parse(
87
+ root.getAttribute('data-rfr-versions') || '[]',
88
+ )
89
+ const placeholder = root.getAttribute('data-rfr-version-placeholder') || 'Select version...'
90
+
91
+ const trigger = root.querySelector<HTMLButtonElement>('[data-rfr-version-trigger]')!
92
+ const dropdown = root.querySelector<HTMLElement>('[data-rfr-version-dropdown]')!
93
+ const label = root.querySelector<HTMLElement>('[data-rfr-version-label]')!
94
+ const options = root.querySelectorAll<HTMLElement>('[data-rfr-version-option]')
95
+
96
+ let isOpen = false
97
+ let selectedValue = root.getAttribute('data-rfr-version-value') || ''
98
+
99
+ function setOpen(open: boolean) {
100
+ isOpen = open
101
+ dropdown.style.display = isOpen ? '' : 'none'
102
+ trigger.setAttribute('aria-expanded', String(isOpen))
103
+ }
104
+
105
+ function select(value: string) {
106
+ selectedValue = value
107
+ root.setAttribute('data-rfr-version-value', value)
108
+
109
+ const opt = versions.find((v) => v.value === value)
110
+ label.textContent = opt ? opt.label : placeholder
111
+
112
+ // Update latest badge on trigger
113
+ let badge = trigger.querySelector('[data-rfr-version-latest-badge]')
114
+ if (opt?.isLatest) {
115
+ if (!badge) {
116
+ badge = document.createElement('span')
117
+ badge.setAttribute('data-rfr-version-latest-badge', '')
118
+ badge.className = trigger.querySelector('[class*="badge"]')?.className || 'inline-flex items-center rounded-full text-xs font-medium px-2 py-0.5 bg-primary/10 text-primary ml-2'
119
+ // Insert before the arrow
120
+ const arrow = trigger.querySelector('[aria-hidden]')
121
+ trigger.insertBefore(badge, arrow)
122
+ }
123
+ badge.textContent = 'Latest'
124
+ } else if (badge) {
125
+ badge.remove()
126
+ }
127
+
128
+ // Update option states
129
+ options.forEach((optEl) => {
130
+ const val = optEl.getAttribute('data-value')
131
+ const isSel = val === value
132
+ optEl.setAttribute('aria-selected', String(isSel))
133
+ // Toggle selected styling
134
+ if (isSel) {
135
+ optEl.classList.add('bg-accent/50', 'font-medium')
136
+ } else {
137
+ optEl.classList.remove('bg-accent/50', 'font-medium')
138
+ }
139
+ })
140
+
141
+ setOpen(false)
142
+ root.dispatchEvent(new CustomEvent('change', { detail: { value }, bubbles: true }))
143
+ }
144
+
145
+ trigger.addEventListener('click', () => setOpen(!isOpen))
146
+
147
+ options.forEach((optEl) => {
148
+ optEl.addEventListener('click', () => {
149
+ const value = optEl.getAttribute('data-value') || ''
150
+ select(value)
151
+ })
152
+ })
153
+
154
+ trigger.addEventListener('keydown', (e) => {
155
+ if (e.key === 'Escape') {
156
+ setOpen(false)
157
+ } else if (e.key === 'Enter' || e.key === ' ') {
158
+ e.preventDefault()
159
+ setOpen(!isOpen)
160
+ } else if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
161
+ if (!isOpen) {
162
+ setOpen(true)
163
+ }
164
+ }
165
+ })
166
+
167
+ // Click outside to close
168
+ document.addEventListener('mousedown', (e) => {
169
+ if (isOpen && !root.contains(e.target as Node)) {
170
+ setOpen(false)
171
+ }
172
+ })
173
+ })
174
+ </script>
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,153 @@
1
+ ---
2
+ import {
3
+ createVideoPlayer,
4
+ playerVariants,
5
+ controlsVariants,
6
+ overlayVariants,
7
+ } from '@refraction-ui/video-player'
8
+ import { cn } from '../shared/index.ts'
9
+
10
+ interface Props extends astroHTML.JSX.HTMLAttributes {
11
+ src?: string
12
+ poster?: string
13
+ autoplay?: boolean
14
+ muted?: boolean
15
+ controls?: boolean
16
+ }
17
+
18
+ const {
19
+ src,
20
+ poster,
21
+ autoplay = false,
22
+ muted: initialMuted = false,
23
+ controls = true,
24
+ class: className,
25
+ ...props
26
+ } = Astro.props
27
+
28
+ const api = createVideoPlayer({ src, poster, autoplay, muted: initialMuted, controls })
29
+ ---
30
+
31
+ {!src ? (
32
+ <div
33
+ class={cn(playerVariants(), 'flex items-center justify-center aspect-video', className)}
34
+ role={api.ariaProps.role}
35
+ aria-label={api.ariaProps['aria-label'] as string}
36
+ {...props}
37
+ >
38
+ <div class="text-center text-muted-foreground">
39
+ <p class="text-lg font-medium">Coming soon</p>
40
+ </div>
41
+ </div>
42
+ ) : (
43
+ <div
44
+ class={cn(playerVariants(), 'aspect-video', className)}
45
+ role={api.ariaProps.role}
46
+ aria-label={api.ariaProps['aria-label'] as string}
47
+ data-state={api.dataAttributes['data-state']}
48
+ data-rfr-video-player
49
+ {...props}
50
+ >
51
+ <video
52
+ class="h-full w-full"
53
+ src={src}
54
+ poster={poster}
55
+ autoplay={autoplay}
56
+ muted={initialMuted}
57
+ data-rfr-video-element
58
+ />
59
+
60
+ {controls && (
61
+ <div class={overlayVariants({ visibility: 'visible' })} data-rfr-video-overlay>
62
+ <button
63
+ type="button"
64
+ class="rounded-full bg-white/90 p-4 text-black shadow-lg"
65
+ aria-label="Play"
66
+ data-rfr-video-play-pause
67
+ >
68
+ Play
69
+ </button>
70
+ </div>
71
+ )}
72
+
73
+ {controls && (
74
+ <div class={controlsVariants()} data-rfr-video-controls>
75
+ <button
76
+ type="button"
77
+ class="rounded bg-white/20 px-3 py-1 text-sm text-white"
78
+ aria-label={initialMuted ? 'Unmute' : 'Mute'}
79
+ data-rfr-video-mute
80
+ >
81
+ {initialMuted ? 'Unmute' : 'Mute'}
82
+ </button>
83
+ </div>
84
+ )}
85
+ </div>
86
+ )}
87
+
88
+ <script>
89
+ document.querySelectorAll<HTMLElement>('[data-rfr-video-player]').forEach((root) => {
90
+ const video = root.querySelector<HTMLVideoElement>('[data-rfr-video-element]')
91
+ const overlay = root.querySelector<HTMLElement>('[data-rfr-video-overlay]')
92
+ const playPauseBtn = root.querySelector<HTMLButtonElement>('[data-rfr-video-play-pause]')
93
+ const muteBtn = root.querySelector<HTMLButtonElement>('[data-rfr-video-mute]')
94
+
95
+ if (!video) return
96
+
97
+ let isMuted = video.muted
98
+
99
+ function updatePlayState() {
100
+ const isPlaying = !video!.paused && !video!.ended
101
+ root.setAttribute('data-state', isPlaying ? 'playing' : 'paused')
102
+
103
+ if (overlay) {
104
+ if (isPlaying) {
105
+ overlay.classList.add('opacity-0', 'pointer-events-none')
106
+ overlay.classList.remove('opacity-100')
107
+ } else {
108
+ overlay.classList.remove('opacity-0', 'pointer-events-none')
109
+ overlay.classList.add('opacity-100')
110
+ }
111
+ }
112
+
113
+ if (playPauseBtn) {
114
+ playPauseBtn.textContent = isPlaying ? 'Pause' : 'Play'
115
+ playPauseBtn.setAttribute('aria-label', isPlaying ? 'Pause' : 'Play')
116
+ }
117
+ }
118
+
119
+ function togglePlay() {
120
+ if (video!.paused || video!.ended) {
121
+ video!.play()
122
+ } else {
123
+ video!.pause()
124
+ }
125
+ }
126
+
127
+ function toggleMute() {
128
+ isMuted = !isMuted
129
+ video!.muted = isMuted
130
+ if (muteBtn) {
131
+ muteBtn.textContent = isMuted ? 'Unmute' : 'Mute'
132
+ muteBtn.setAttribute('aria-label', isMuted ? 'Unmute' : 'Mute')
133
+ }
134
+ }
135
+
136
+ video.addEventListener('play', updatePlayState)
137
+ video.addEventListener('pause', updatePlayState)
138
+ video.addEventListener('ended', updatePlayState)
139
+
140
+ if (playPauseBtn) {
141
+ playPauseBtn.addEventListener('click', togglePlay)
142
+ }
143
+
144
+ // Click on overlay to toggle play
145
+ if (overlay) {
146
+ overlay.addEventListener('click', togglePlay)
147
+ }
148
+
149
+ if (muteBtn) {
150
+ muteBtn.addEventListener('click', toggleMute)
151
+ }
152
+ })
153
+ </script>
@@ -0,0 +1 @@
1
+ export {}