@refraction-ui/astro 0.4.2 → 0.4.4
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.
- package/dist/accordion/index.ts +1 -0
- package/dist/ai/ai-manager.ts +121 -0
- package/dist/ai/index.ts +23 -0
- package/dist/ai/mock-providers.ts +68 -0
- package/dist/ai/sanitizer.ts +174 -0
- package/dist/ai/tts-manager.ts +80 -0
- package/dist/ai/types.ts +72 -0
- package/dist/animated-text/animated-text.styles.ts +28 -0
- package/dist/animated-text/animated-text.ts +83 -0
- package/dist/animated-text/index.ts +15 -0
- package/dist/app-shell/app-shell.ts +252 -0
- package/dist/app-shell/auth-shell.ts +84 -0
- package/dist/app-shell/index.ts +22 -0
- package/dist/app-shell/page-shell.ts +125 -0
- package/dist/astro-accordion/Button.astro +1 -1
- package/dist/astro-ai/index.ts +1 -1
- package/dist/astro-animated-text/AnimatedText.astro +1 -1
- package/dist/astro-animated-text/TypewriterText.astro +1 -1
- package/dist/astro-app-shell/AppShell.astro +1 -1
- package/dist/astro-app-shell/AuthShell.astro +1 -1
- package/dist/astro-app-shell/AuthShellCard.astro +1 -1
- package/dist/astro-app-shell/PageShell.astro +1 -1
- package/dist/astro-app-shell/PageShellSection.astro +1 -1
- package/dist/astro-app-shell/index.ts +1 -1
- package/dist/astro-auth/index.ts +2 -2
- package/dist/astro-avatar/Avatar.astro +1 -1
- package/dist/astro-avatar/AvatarFallback.astro +1 -1
- package/dist/astro-avatar/AvatarImage.astro +1 -1
- package/dist/astro-avatar-group/AvatarGroup.astro +1 -1
- package/dist/astro-badge/Badge.astro +1 -1
- package/dist/astro-bottom-nav/BottomNav.astro +1 -1
- package/dist/astro-breadcrumbs/Breadcrumbs.astro +1 -1
- package/dist/astro-button/Button.astro +1 -1
- package/dist/astro-calendar/Calendar.astro +1 -1
- package/dist/astro-card/Card.astro +1 -1
- package/dist/astro-card/CardContent.astro +1 -1
- package/dist/astro-card/CardDescription.astro +1 -1
- package/dist/astro-card/CardFooter.astro +1 -1
- package/dist/astro-card/CardHeader.astro +1 -1
- package/dist/astro-card/CardTitle.astro +1 -1
- package/dist/astro-card-grid/CardGrid.astro +1 -1
- package/dist/astro-carousel/Button.astro +1 -1
- package/dist/astro-charts/Bars.astro +1 -1
- package/dist/astro-charts/Chart.astro +1 -1
- package/dist/astro-charts/Circles.astro +1 -1
- package/dist/astro-charts/Histogram.astro +1 -1
- package/dist/astro-charts/Line.astro +1 -1
- package/dist/astro-charts/PieChart.astro +1 -1
- package/dist/astro-charts/ScatterPlot.astro +1 -1
- package/dist/astro-charts/index.ts +1 -1
- package/dist/astro-checkbox/Checkbox.astro +1 -1
- package/dist/astro-code-editor/CodeEditor.astro +1 -1
- package/dist/astro-collapsible/Collapsible.astro +1 -1
- package/dist/astro-collapsible/CollapsibleContent.astro +1 -1
- package/dist/astro-command/Command.astro +1 -1
- package/dist/astro-command/CommandGroup.astro +1 -1
- package/dist/astro-command/CommandInput.astro +1 -1
- package/dist/astro-command/CommandItem.astro +1 -1
- package/dist/astro-command-input/CommandInput.astro +1 -1
- package/dist/astro-content-protection/ContentProtection.astro +1 -1
- package/dist/astro-data-table/DataTable.astro +1 -1
- package/dist/astro-date-picker/DatePicker.astro +1 -1
- package/dist/astro-device-frame/DeviceFrame.astro +1 -1
- package/dist/astro-dialog/DialogContent.astro +1 -1
- package/dist/astro-dialog/DialogOverlay.astro +1 -1
- package/dist/astro-diff-viewer/DiffViewer.astro +1 -1
- package/dist/astro-dropdown-menu/DropdownMenuContent.astro +1 -1
- package/dist/astro-dropdown-menu/DropdownMenuItem.astro +1 -1
- package/dist/astro-emoji-picker/EmojiPicker.astro +1 -1
- package/dist/astro-feedback-dialog/FeedbackDialog.astro +1 -1
- package/dist/astro-file-upload/FileUpload.astro +1 -1
- package/dist/astro-footer/Footer.astro +1 -1
- package/dist/astro-inline-editor/InlineEditor.astro +1 -1
- package/dist/astro-input/Input.astro +1 -1
- package/dist/astro-input-group/InputGroup.astro +1 -1
- package/dist/astro-input-group/InputGroupAddon.astro +1 -1
- package/dist/astro-input-group/InputGroupButton.astro +1 -1
- package/dist/astro-install-prompt/InstallPrompt.astro +1 -1
- package/dist/astro-keyboard-shortcut/ShortcutBadge.astro +1 -1
- package/dist/astro-language-selector/LanguageSelector.astro +1 -1
- package/dist/astro-link-card/LinkCard.astro +1 -1
- package/dist/astro-markdown-renderer/MarkdownRenderer.astro +1 -1
- package/dist/astro-mobile-nav/MobileNav.astro +1 -1
- package/dist/astro-mobile-nav/MobileNavLink.astro +1 -1
- package/dist/astro-mobile-nav/MobileNavTrigger.astro +1 -1
- package/dist/astro-navbar/Navbar.astro +1 -1
- package/dist/astro-otp-input/OtpInput.astro +1 -1
- package/dist/astro-payment/Payment.astro +1 -1
- package/dist/astro-popover/PopoverContent.astro +1 -1
- package/dist/astro-presence-indicator/PresenceIndicator.astro +1 -1
- package/dist/astro-progress-display/BadgeDisplay.astro +1 -1
- package/dist/astro-progress-display/ProgressBar.astro +1 -1
- package/dist/astro-progress-display/StatsGrid.astro +1 -1
- package/dist/astro-radio/RadioGroup.astro +1 -1
- package/dist/astro-radio/RadioItem.astro +1 -1
- package/dist/astro-reaction-bar/ReactionBar.astro +1 -1
- package/dist/astro-resizable-layout/ResizableDivider.astro +1 -1
- package/dist/astro-resizable-layout/ResizableLayout.astro +1 -1
- package/dist/astro-resizable-layout/ResizablePane.astro +1 -1
- package/dist/astro-resizable-layout/index.ts +1 -1
- package/dist/astro-rich-editor/index.ts +2 -2
- package/dist/astro-search-bar/SearchBar.astro +1 -1
- package/dist/astro-search-bar/SearchResults.astro +1 -1
- package/dist/astro-select/Select.astro +1 -1
- package/dist/astro-select/SelectContent.astro +1 -1
- package/dist/astro-select/SelectItem.astro +1 -1
- package/dist/astro-select/SelectTrigger.astro +1 -1
- package/dist/astro-sidebar/Sidebar.astro +1 -1
- package/dist/astro-skeleton/Skeleton.astro +1 -1
- package/dist/astro-slide-viewer/SlideViewer.astro +1 -1
- package/dist/astro-status-indicator/StatusIndicator.astro +1 -1
- package/dist/astro-switch/Switch.astro +1 -1
- package/dist/astro-table-of-contents/TableOfContents.astro +1 -1
- package/dist/astro-tabs/Tabs.astro +1 -1
- package/dist/astro-tabs/TabsList.astro +1 -1
- package/dist/astro-tabs/TabsTrigger.astro +1 -1
- package/dist/astro-textarea/Textarea.astro +1 -1
- package/dist/astro-theme/ThemeScript.astro +1 -1
- package/dist/astro-thread-view/ThreadView.astro +1 -1
- package/dist/astro-toast/Toast.astro +1 -1
- package/dist/astro-tooltip/TooltipContent.astro +1 -1
- package/dist/astro-version-selector/VersionSelector.astro +1 -1
- package/dist/astro-video-player/VideoPlayer.astro +1 -1
- package/dist/auth/adapters/mock.ts +74 -0
- package/dist/auth/auth-machine.ts +120 -0
- package/dist/auth/index.ts +22 -0
- package/dist/auth/rbac.ts +44 -0
- package/dist/auth/types.ts +44 -0
- package/dist/avatar/avatar.styles.ts +47 -0
- package/dist/avatar/avatar.ts +73 -0
- package/dist/avatar/index.ts +15 -0
- package/dist/avatar-group/avatar-group.styles.ts +63 -0
- package/dist/avatar-group/avatar-group.ts +106 -0
- package/dist/avatar-group/index.ts +18 -0
- package/dist/badge/badge.styles.ts +24 -0
- package/dist/badge/badge.ts +48 -0
- package/dist/badge/index.ts +10 -0
- package/dist/bottom-nav/bottom-nav.styles.ts +18 -0
- package/dist/bottom-nav/bottom-nav.ts +33 -0
- package/dist/bottom-nav/index.ts +2 -0
- package/dist/breadcrumbs/breadcrumbs.styles.ts +20 -0
- package/dist/breadcrumbs/breadcrumbs.ts +72 -0
- package/dist/breadcrumbs/index.ts +8 -0
- package/dist/button/button.styles.ts +38 -0
- package/dist/button/button.ts +64 -0
- package/dist/button/index.ts +10 -0
- package/dist/calendar/calendar.styles.ts +21 -0
- package/dist/calendar/calendar.ts +202 -0
- package/dist/calendar/index.ts +9 -0
- package/dist/callout/callout.ts +1 -0
- package/dist/callout/index.ts +1 -0
- package/dist/card/card.styles.ts +55 -0
- package/dist/card/card.ts +45 -0
- package/dist/card/index.ts +20 -0
- package/dist/card-grid/card-grid.ts +9 -0
- package/dist/card-grid/index.ts +1 -0
- package/dist/carousel/index.ts +1 -0
- package/dist/charts/axis.ts +50 -0
- package/dist/charts/chart.ts +73 -0
- package/dist/charts/colorblind.ts +100 -0
- package/dist/charts/data.ts +72 -0
- package/dist/charts/geometry.ts +54 -0
- package/dist/charts/index.ts +61 -0
- package/dist/charts/scales.ts +151 -0
- package/dist/checkbox/checkbox.styles.ts +33 -0
- package/dist/checkbox/checkbox.ts +73 -0
- package/dist/checkbox/index.ts +10 -0
- package/dist/code-block/code-block.ts +1 -0
- package/dist/code-block/index.ts +1 -0
- package/dist/code-editor/code-editor.styles.ts +32 -0
- package/dist/code-editor/code-editor.ts +140 -0
- package/dist/code-editor/index.ts +9 -0
- package/dist/collapsible/collapsible.styles.ts +14 -0
- package/dist/collapsible/collapsible.ts +93 -0
- package/dist/collapsible/index.ts +7 -0
- package/dist/command/command.styles.ts +27 -0
- package/dist/command/command.ts +179 -0
- package/dist/command/index.ts +14 -0
- package/dist/command-input/command-input.ts +176 -0
- package/dist/command-input/index.ts +1 -0
- package/dist/content-protection/content-protection.styles.ts +9 -0
- package/dist/content-protection/content-protection.ts +68 -0
- package/dist/content-protection/index.ts +8 -0
- package/dist/data-table/data-table.styles.ts +55 -0
- package/dist/data-table/data-table.ts +183 -0
- package/dist/data-table/index.ts +15 -0
- package/dist/date-picker/date-picker.styles.ts +31 -0
- package/dist/date-picker/date-picker.ts +333 -0
- package/dist/date-picker/index.ts +18 -0
- package/dist/device-frame/device-frame.styles.ts +21 -0
- package/dist/device-frame/device-frame.ts +112 -0
- package/dist/device-frame/index.ts +10 -0
- package/dist/dialog/dialog.styles.ts +8 -0
- package/dist/dialog/dialog.ts +133 -0
- package/dist/dialog/index.ts +8 -0
- package/dist/diff-viewer/diff-viewer.styles.ts +124 -0
- package/dist/diff-viewer/diff-viewer.ts +255 -0
- package/dist/diff-viewer/index.ts +20 -0
- package/dist/dropdown-menu/dropdown-menu.styles.ts +9 -0
- package/dist/dropdown-menu/dropdown-menu.ts +139 -0
- package/dist/dropdown-menu/index.ts +9 -0
- package/dist/emoji-picker/emoji-data.ts +282 -0
- package/dist/emoji-picker/emoji-picker.styles.ts +32 -0
- package/dist/emoji-picker/emoji-picker.ts +165 -0
- package/dist/emoji-picker/index.ts +25 -0
- package/dist/feedback-dialog/feedback-dialog.styles.ts +15 -0
- package/dist/feedback-dialog/feedback-dialog.ts +147 -0
- package/dist/feedback-dialog/index.ts +11 -0
- package/dist/file-upload/file-upload.styles.ts +30 -0
- package/dist/file-upload/file-upload.ts +260 -0
- package/dist/file-upload/index.ts +19 -0
- package/dist/footer/footer.styles.ts +5 -0
- package/dist/footer/footer.ts +32 -0
- package/dist/footer/index.ts +9 -0
- package/dist/index.ts +1 -1
- package/dist/inline-editor/index.ts +13 -0
- package/dist/inline-editor/inline-editor.styles.ts +42 -0
- package/dist/inline-editor/inline-editor.ts +96 -0
- package/dist/input/index.ts +9 -0
- package/dist/input/input.styles.ts +15 -0
- package/dist/input/input.ts +84 -0
- package/dist/input-group/index.ts +13 -0
- package/dist/input-group/input-group.styles.ts +50 -0
- package/dist/input-group/input-group.ts +44 -0
- package/dist/install-prompt/index.ts +9 -0
- package/dist/install-prompt/install-prompt.styles.ts +5 -0
- package/dist/install-prompt/install-prompt.ts +68 -0
- package/dist/keyboard-shortcut/index.ts +12 -0
- package/dist/keyboard-shortcut/keyboard-shortcut.styles.ts +8 -0
- package/dist/keyboard-shortcut/keyboard-shortcut.ts +153 -0
- package/dist/language-selector/index.ts +8 -0
- package/dist/language-selector/language-selector.styles.ts +28 -0
- package/dist/language-selector/language-selector.ts +149 -0
- package/dist/link-card/index.ts +1 -0
- package/dist/link-card/link-card.ts +9 -0
- package/dist/markdown-renderer/index.ts +8 -0
- package/dist/markdown-renderer/markdown-renderer.styles.ts +31 -0
- package/dist/markdown-renderer/markdown-renderer.ts +251 -0
- package/dist/mobile-nav/index.ts +14 -0
- package/dist/mobile-nav/mobile-nav.styles.ts +39 -0
- package/dist/mobile-nav/mobile-nav.ts +74 -0
- package/dist/navbar/index.ts +9 -0
- package/dist/navbar/navbar.styles.ts +29 -0
- package/dist/navbar/navbar.ts +39 -0
- package/dist/otp-input/index.ts +13 -0
- package/dist/otp-input/otp-input.styles.ts +51 -0
- package/dist/otp-input/otp-input.ts +198 -0
- package/dist/pagination/index.ts +1 -0
- package/dist/payment/index.ts +1 -0
- package/dist/payment/payment.ts +16 -0
- package/dist/popover/index.ts +7 -0
- package/dist/popover/popover.styles.ts +16 -0
- package/dist/popover/popover.ts +89 -0
- package/dist/presence-indicator/index.ts +14 -0
- package/dist/presence-indicator/presence-indicator.styles.ts +29 -0
- package/dist/presence-indicator/presence-indicator.ts +63 -0
- package/dist/progress-display/index.ts +15 -0
- package/dist/progress-display/progress-display.styles.ts +72 -0
- package/dist/progress-display/progress-display.ts +60 -0
- package/dist/radio/index.ts +8 -0
- package/dist/radio/radio.styles.ts +34 -0
- package/dist/radio/radio.ts +70 -0
- package/dist/reaction-bar/index.ts +14 -0
- package/dist/reaction-bar/reaction-bar.styles.ts +23 -0
- package/dist/reaction-bar/reaction-bar.ts +87 -0
- package/dist/resizable-layout/index.ts +12 -0
- package/dist/resizable-layout/resizable-layout.styles.ts +50 -0
- package/dist/resizable-layout/resizable-layout.ts +139 -0
- package/dist/rich-editor/editor.ts +342 -0
- package/dist/rich-editor/emoji.ts +175 -0
- package/dist/rich-editor/history.ts +79 -0
- package/dist/rich-editor/index.ts +106 -0
- package/dist/rich-editor/markdown-shortcuts.ts +248 -0
- package/dist/rich-editor/mentions.ts +162 -0
- package/dist/rich-editor/model.ts +206 -0
- package/dist/rich-editor/operations.ts +772 -0
- package/dist/rich-editor/selection.ts +157 -0
- package/dist/rich-editor/serialization.ts +711 -0
- package/dist/rich-editor/slash-commands.ts +212 -0
- package/dist/search-bar/index.ts +7 -0
- package/dist/search-bar/search-bar.styles.ts +29 -0
- package/dist/search-bar/search-bar.ts +130 -0
- package/dist/select/index.ts +13 -0
- package/dist/select/select.styles.ts +52 -0
- package/dist/select/select.ts +152 -0
- package/dist/sidebar/index.ts +9 -0
- package/dist/sidebar/sidebar.styles.ts +27 -0
- package/dist/sidebar/sidebar.ts +59 -0
- package/dist/skeleton/index.ts +8 -0
- package/dist/skeleton/skeleton.styles.ts +16 -0
- package/dist/skeleton/skeleton.ts +39 -0
- package/dist/skip-to-content/index.ts +1 -0
- package/dist/skip-to-content/skip-to-content.ts +1 -0
- package/dist/slide-viewer/index.ts +16 -0
- package/dist/slide-viewer/slide-viewer.styles.ts +57 -0
- package/dist/slide-viewer/slide-viewer.ts +152 -0
- package/dist/slider/index.ts +1 -0
- package/dist/slider/slider.ts +2 -0
- package/dist/status-indicator/index.ts +15 -0
- package/dist/status-indicator/status-indicator.styles.ts +41 -0
- package/dist/status-indicator/status-indicator.ts +66 -0
- package/dist/steps/index.ts +1 -0
- package/dist/steps/steps.ts +1 -0
- package/dist/switch/index.ts +7 -0
- package/dist/switch/switch.styles.ts +55 -0
- package/dist/switch/switch.ts +63 -0
- package/dist/table-of-contents/index.ts +1 -0
- package/dist/table-of-contents/table-of-contents.ts +37 -0
- package/dist/tabs/index.ts +8 -0
- package/dist/tabs/tabs.styles.ts +9 -0
- package/dist/tabs/tabs.ts +133 -0
- package/dist/textarea/index.ts +7 -0
- package/dist/textarea/textarea.styles.ts +15 -0
- package/dist/textarea/textarea.ts +59 -0
- package/dist/theme/dom-adapters.ts +60 -0
- package/dist/theme/index.ts +18 -0
- package/dist/theme/theme-machine.ts +130 -0
- package/dist/theme/theme-script.ts +18 -0
- package/dist/thread-view/index.ts +26 -0
- package/dist/thread-view/thread-view.styles.ts +35 -0
- package/dist/thread-view/thread-view.ts +176 -0
- package/dist/toast/index.ts +13 -0
- package/dist/toast/toast.styles.ts +19 -0
- package/dist/toast/toast.ts +203 -0
- package/dist/tooltip/index.ts +7 -0
- package/dist/tooltip/tooltip.styles.ts +16 -0
- package/dist/tooltip/tooltip.ts +109 -0
- package/dist/version-selector/index.ts +12 -0
- package/dist/version-selector/version-selector.styles.ts +41 -0
- package/dist/version-selector/version-selector.ts +136 -0
- package/dist/video-player/index.ts +12 -0
- package/dist/video-player/video-player.styles.ts +42 -0
- package/dist/video-player/video-player.ts +100 -0
- 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,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,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
|
+
}
|