@refraction-ui/astro 0.4.0 → 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 +55 -0
- package/dist/astro-accordion/index.ts +1 -0
- package/dist/astro-ai/AIScript.astro +69 -0
- package/dist/astro-ai/TTSScript.astro +60 -0
- package/dist/astro-ai/index.ts +16 -0
- package/dist/astro-animated-text/AnimatedText.astro +81 -0
- package/dist/astro-animated-text/TypewriterText.astro +90 -0
- package/dist/astro-animated-text/index.ts +1 -0
- package/dist/astro-app-shell/AppShell.astro +195 -0
- package/dist/astro-app-shell/AppShellContent.astro +29 -0
- package/dist/astro-app-shell/AppShellHeader.astro +54 -0
- package/dist/astro-app-shell/AppShellMain.astro +18 -0
- package/dist/astro-app-shell/AppShellMobileNav.astro +27 -0
- package/dist/astro-app-shell/AppShellOverlay.astro +21 -0
- package/dist/astro-app-shell/AppShellSidebar.astro +26 -0
- package/dist/astro-app-shell/AuthShell.astro +31 -0
- package/dist/astro-app-shell/AuthShellCard.astro +24 -0
- package/dist/astro-app-shell/PageShell.astro +36 -0
- package/dist/astro-app-shell/PageShellFooter.astro +30 -0
- package/dist/astro-app-shell/PageShellNav.astro +33 -0
- package/dist/astro-app-shell/PageShellSection.astro +32 -0
- package/dist/astro-app-shell/index.ts +21 -0
- package/dist/astro-auth/AuthGuard.astro +133 -0
- package/dist/astro-auth/index.ts +5 -0
- package/dist/astro-avatar/Avatar.astro +22 -0
- package/dist/astro-avatar/AvatarFallback.astro +18 -0
- package/dist/astro-avatar/AvatarImage.astro +48 -0
- package/dist/astro-avatar/index.ts +1 -0
- package/dist/astro-avatar-group/AvatarGroup.astro +42 -0
- package/dist/astro-avatar-group/index.ts +1 -0
- package/dist/astro-badge/Badge.astro +27 -0
- package/dist/astro-badge/index.ts +1 -0
- package/dist/astro-bottom-nav/BottomNav.astro +40 -0
- package/dist/astro-bottom-nav/index.ts +1 -0
- package/dist/astro-breadcrumbs/Breadcrumbs.astro +56 -0
- package/dist/astro-breadcrumbs/index.ts +1 -0
- package/dist/astro-button/Button.astro +55 -0
- package/dist/astro-button/index.ts +1 -0
- package/dist/astro-calendar/Calendar.astro +259 -0
- package/dist/astro-calendar/index.ts +1 -0
- package/dist/astro-callout/Callout.astro +3 -0
- package/dist/astro-callout/index.ts +1 -0
- package/dist/astro-card/Card.astro +20 -0
- package/dist/astro-card/CardContent.astro +13 -0
- package/dist/astro-card/CardDescription.astro +13 -0
- package/dist/astro-card/CardFooter.astro +13 -0
- package/dist/astro-card/CardHeader.astro +13 -0
- package/dist/astro-card/CardTitle.astro +15 -0
- package/dist/astro-card/index.ts +1 -0
- package/dist/astro-card-grid/CardGrid.astro +17 -0
- package/dist/astro-card-grid/index.ts +1 -0
- package/dist/astro-carousel/Button.astro +55 -0
- package/dist/astro-carousel/index.ts +1 -0
- package/dist/astro-charts/Bars.astro +53 -0
- package/dist/astro-charts/Chart.astro +31 -0
- package/dist/astro-charts/Circles.astro +48 -0
- package/dist/astro-charts/Gradient.astro +34 -0
- package/dist/astro-charts/Histogram.astro +107 -0
- package/dist/astro-charts/Line.astro +55 -0
- package/dist/astro-charts/PieChart.astro +63 -0
- package/dist/astro-charts/ScatterPlot.astro +95 -0
- package/dist/astro-charts/XAxis.astro +46 -0
- package/dist/astro-charts/YAxis.astro +44 -0
- package/dist/astro-charts/index.ts +26 -0
- package/dist/astro-checkbox/Checkbox.astro +129 -0
- package/dist/astro-checkbox/index.ts +1 -0
- package/dist/astro-code-block/CodeBlock.astro +3 -0
- package/dist/astro-code-block/index.ts +1 -0
- package/dist/astro-code-editor/CodeEditor.astro +123 -0
- package/dist/astro-code-editor/index.ts +1 -0
- package/dist/astro-collapsible/Collapsible.astro +58 -0
- package/dist/astro-collapsible/CollapsibleContent.astro +21 -0
- package/dist/astro-collapsible/CollapsibleTrigger.astro +17 -0
- package/dist/astro-collapsible/index.ts +1 -0
- package/dist/astro-command/Command.astro +170 -0
- package/dist/astro-command/CommandEmpty.astro +17 -0
- package/dist/astro-command/CommandGroup.astro +25 -0
- package/dist/astro-command/CommandInput.astro +21 -0
- package/dist/astro-command/CommandItem.astro +26 -0
- package/dist/astro-command/CommandList.astro +17 -0
- package/dist/astro-command/CommandSeparator.astro +14 -0
- package/dist/astro-command/index.ts +1 -0
- package/dist/astro-command-input/CommandInput.astro +6 -0
- package/dist/astro-command-input/index.ts +1 -0
- package/dist/astro-content-protection/ContentProtection.astro +104 -0
- package/dist/astro-content-protection/index.ts +1 -0
- package/dist/astro-data-table/DataTable.astro +263 -0
- package/dist/astro-data-table/index.ts +1 -0
- package/dist/astro-date-picker/DatePicker.astro +398 -0
- package/dist/astro-date-picker/index.ts +1 -0
- package/dist/astro-device-frame/DeviceFrame.astro +44 -0
- package/dist/astro-device-frame/index.ts +1 -0
- package/dist/astro-dialog/Dialog.astro +138 -0
- package/dist/astro-dialog/DialogClose.astro +16 -0
- package/dist/astro-dialog/DialogContent.astro +24 -0
- package/dist/astro-dialog/DialogDescription.astro +15 -0
- package/dist/astro-dialog/DialogFooter.astro +14 -0
- package/dist/astro-dialog/DialogHeader.astro +14 -0
- package/dist/astro-dialog/DialogOverlay.astro +17 -0
- package/dist/astro-dialog/DialogTitle.astro +15 -0
- package/dist/astro-dialog/DialogTrigger.astro +18 -0
- package/dist/astro-dialog/index.ts +1 -0
- package/dist/astro-diff-viewer/DiffViewer.astro +371 -0
- package/dist/astro-diff-viewer/index.ts +1 -0
- package/dist/astro-dropdown-menu/DropdownMenu.astro +138 -0
- package/dist/astro-dropdown-menu/DropdownMenuContent.astro +20 -0
- package/dist/astro-dropdown-menu/DropdownMenuItem.astro +21 -0
- package/dist/astro-dropdown-menu/DropdownMenuLabel.astro +14 -0
- package/dist/astro-dropdown-menu/DropdownMenuSeparator.astro +14 -0
- package/dist/astro-dropdown-menu/DropdownMenuTrigger.astro +18 -0
- package/dist/astro-dropdown-menu/index.ts +1 -0
- package/dist/astro-emoji-picker/EmojiPicker.astro +255 -0
- package/dist/astro-emoji-picker/index.ts +1 -0
- package/dist/astro-feedback-dialog/FeedbackDialog.astro +152 -0
- package/dist/astro-feedback-dialog/index.ts +1 -0
- package/dist/astro-file-tree/astro-file-tree.astro +4 -0
- package/dist/astro-file-tree/index.ts +1 -0
- package/dist/astro-file-upload/FileUpload.astro +306 -0
- package/dist/astro-file-upload/index.ts +1 -0
- package/dist/astro-footer/Footer.astro +61 -0
- package/dist/astro-footer/index.ts +1 -0
- package/dist/astro-icon-system/astro-icon-system.astro +4 -0
- package/dist/astro-icon-system/index.ts +1 -0
- package/dist/astro-inline-editor/InlineEditor.astro +169 -0
- package/dist/astro-inline-editor/index.ts +1 -0
- package/dist/astro-input/Input.astro +49 -0
- package/dist/astro-input/index.ts +1 -0
- package/dist/astro-input-group/InputGroup.astro +29 -0
- package/dist/astro-input-group/InputGroupAddon.astro +14 -0
- package/dist/astro-input-group/InputGroupButton.astro +19 -0
- package/dist/astro-input-group/InputGroupText.astro +11 -0
- package/dist/astro-input-group/index.ts +1 -0
- package/dist/astro-install-prompt/InstallPrompt.astro +115 -0
- package/dist/astro-install-prompt/index.ts +1 -0
- package/dist/astro-keyboard-shortcut/KeyboardShortcut.astro +83 -0
- package/dist/astro-keyboard-shortcut/ShortcutBadge.astro +41 -0
- package/dist/astro-keyboard-shortcut/index.ts +1 -0
- package/dist/astro-language-selector/LanguageSelector.astro +247 -0
- package/dist/astro-language-selector/index.ts +1 -0
- package/dist/astro-link-card/LinkCard.astro +17 -0
- package/dist/astro-link-card/index.ts +1 -0
- package/dist/astro-markdown-renderer/MarkdownRenderer.astro +62 -0
- package/dist/astro-markdown-renderer/index.ts +1 -0
- package/dist/astro-mobile-nav/MobileNav.astro +84 -0
- package/dist/astro-mobile-nav/MobileNavContent.astro +29 -0
- package/dist/astro-mobile-nav/MobileNavLink.astro +16 -0
- package/dist/astro-mobile-nav/MobileNavTrigger.astro +57 -0
- package/dist/astro-mobile-nav/index.ts +1 -0
- package/dist/astro-navbar/Navbar.astro +49 -0
- package/dist/astro-navbar/index.ts +1 -0
- package/dist/astro-otp-input/OtpInput.astro +253 -0
- package/dist/astro-otp-input/index.ts +1 -0
- package/dist/astro-pagination/Pagination.astro +3 -0
- package/dist/astro-pagination/index.ts +1 -0
- package/dist/astro-payment/Payment.astro +19 -0
- package/dist/astro-payment/index.ts +1 -0
- package/dist/astro-popover/Popover.astro +87 -0
- package/dist/astro-popover/PopoverClose.astro +16 -0
- package/dist/astro-popover/PopoverContent.astro +23 -0
- package/dist/astro-popover/PopoverTrigger.astro +18 -0
- package/dist/astro-popover/index.ts +1 -0
- package/dist/astro-presence-indicator/PresenceIndicator.astro +26 -0
- package/dist/astro-presence-indicator/index.ts +1 -0
- package/dist/astro-progress-display/BadgeDisplay.astro +33 -0
- package/dist/astro-progress-display/ProgressBar.astro +27 -0
- package/dist/astro-progress-display/StatsGrid.astro +32 -0
- package/dist/astro-progress-display/index.ts +1 -0
- package/dist/astro-radio/RadioGroup.astro +111 -0
- package/dist/astro-radio/RadioItem.astro +36 -0
- package/dist/astro-radio/index.ts +1 -0
- package/dist/astro-reaction-bar/ReactionBar.astro +117 -0
- package/dist/astro-reaction-bar/index.ts +1 -0
- package/dist/astro-resizable-layout/ResizableDivider.astro +27 -0
- package/dist/astro-resizable-layout/ResizableLayout.astro +162 -0
- package/dist/astro-resizable-layout/ResizablePane.astro +28 -0
- package/dist/astro-resizable-layout/index.ts +11 -0
- package/dist/astro-rich-editor/RichEditor.astro +260 -0
- package/dist/astro-rich-editor/index.ts +25 -0
- package/dist/astro-search-bar/SearchBar.astro +140 -0
- package/dist/astro-search-bar/SearchResultItem.astro +16 -0
- package/dist/astro-search-bar/SearchResults.astro +17 -0
- package/dist/astro-search-bar/index.ts +1 -0
- package/dist/astro-select/Select.astro +165 -0
- package/dist/astro-select/SelectContent.astro +19 -0
- package/dist/astro-select/SelectItem.astro +42 -0
- package/dist/astro-select/SelectTrigger.astro +36 -0
- package/dist/astro-select/index.ts +1 -0
- package/dist/astro-sidebar/Sidebar.astro +64 -0
- package/dist/astro-sidebar/index.ts +1 -0
- package/dist/astro-skeleton/Skeleton.astro +39 -0
- package/dist/astro-skeleton/SkeletonText.astro +22 -0
- package/dist/astro-skeleton/index.ts +1 -0
- package/dist/astro-skip-to-content/SkipToContent.astro +3 -0
- package/dist/astro-skip-to-content/index.ts +1 -0
- package/dist/astro-slide-viewer/SlideViewer.astro +231 -0
- package/dist/astro-slide-viewer/index.ts +1 -0
- package/dist/astro-slider/index.ts +1 -0
- package/dist/astro-status-indicator/StatusIndicator.astro +31 -0
- package/dist/astro-status-indicator/index.ts +1 -0
- package/dist/astro-steps/Steps.astro +3 -0
- package/dist/astro-steps/index.ts +1 -0
- package/dist/astro-switch/Switch.astro +89 -0
- package/dist/astro-switch/index.ts +1 -0
- package/dist/astro-table-of-contents/TableOfContents.astro +53 -0
- package/dist/astro-table-of-contents/index.ts +1 -0
- package/dist/astro-tabs/Tabs.astro +96 -0
- package/dist/astro-tabs/TabsContent.astro +20 -0
- package/dist/astro-tabs/TabsList.astro +16 -0
- package/dist/astro-tabs/TabsTrigger.astro +24 -0
- package/dist/astro-tabs/index.ts +1 -0
- package/dist/astro-textarea/Textarea.astro +51 -0
- package/dist/astro-textarea/index.ts +1 -0
- package/dist/astro-theme/ThemeScript.astro +13 -0
- package/dist/astro-theme/ThemeToggle.astro +279 -0
- package/dist/astro-theme/index.ts +1 -0
- package/dist/astro-thread-view/ThreadView.astro +159 -0
- package/dist/astro-thread-view/index.ts +1 -0
- package/dist/astro-toast/Toast.astro +49 -0
- package/dist/astro-toast/Toaster.astro +142 -0
- package/dist/astro-toast/index.ts +1 -0
- package/dist/astro-tooltip/Tooltip.astro +95 -0
- package/dist/astro-tooltip/TooltipContent.astro +22 -0
- package/dist/astro-tooltip/TooltipTrigger.astro +16 -0
- package/dist/astro-tooltip/index.ts +1 -0
- package/dist/astro-version-selector/VersionSelector.astro +174 -0
- package/dist/astro-version-selector/index.ts +1 -0
- package/dist/astro-video-player/VideoPlayer.astro +153 -0
- package/dist/astro-video-player/index.ts +1 -0
- 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 +77 -0
- 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/shared/a11y.ts +33 -0
- package/dist/shared/aria.ts +30 -0
- package/dist/shared/cn.ts +35 -0
- package/dist/shared/cva.ts +79 -0
- package/dist/shared/engine-adapter.ts +10 -0
- package/dist/shared/focus-trap.ts +123 -0
- package/dist/shared/index.ts +60 -0
- package/dist/shared/keyboard.ts +36 -0
- package/dist/shared/live-region.ts +90 -0
- package/dist/shared/motion.ts +15 -0
- package/dist/shared/skip-link.ts +35 -0
- package/dist/shared/state-machine.ts +57 -0
- package/dist/shared/tokens.ts +16 -0
- package/dist/shared/types.ts +45 -0
- package/dist/shared/variants.ts +17 -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 +20 -19
- package/src/index.ts +0 -94
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slash command system.
|
|
3
|
+
* When user types "/" at the start of a block or after a space, a menu appears
|
|
4
|
+
* with available commands that can be filtered by typing.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Document, BlockType } from './model.js'
|
|
8
|
+
import type { Selection } from './selection.js'
|
|
9
|
+
import type { EditorState } from './operations.js'
|
|
10
|
+
import { changeBlockType, insertBlock, deleteSelection } from './operations.js'
|
|
11
|
+
import { createCollapsedSelection, createSelection, createPosition } from './selection.js'
|
|
12
|
+
import { findBlockById, getBlockText, cloneDocument, createTextSegment } from './model.js'
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Types
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
export interface SlashCommand {
|
|
19
|
+
name: string
|
|
20
|
+
label: string
|
|
21
|
+
description: string
|
|
22
|
+
icon?: string
|
|
23
|
+
keywords: string[]
|
|
24
|
+
action: (doc: Document, sel: Selection) => EditorState
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface SlashCommandMenu {
|
|
28
|
+
isOpen: boolean
|
|
29
|
+
query: string
|
|
30
|
+
filteredCommands: SlashCommand[]
|
|
31
|
+
selectedIndex: number
|
|
32
|
+
open(query?: string): void
|
|
33
|
+
close(): void
|
|
34
|
+
search(query: string): void
|
|
35
|
+
selectNext(): void
|
|
36
|
+
selectPrevious(): void
|
|
37
|
+
execute(doc: Document, sel: Selection): EditorState | null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Built-in commands
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
function makeBlockCommand(
|
|
45
|
+
name: string,
|
|
46
|
+
label: string,
|
|
47
|
+
description: string,
|
|
48
|
+
keywords: string[],
|
|
49
|
+
blockType: BlockType,
|
|
50
|
+
meta?: Record<string, unknown>,
|
|
51
|
+
): SlashCommand {
|
|
52
|
+
return {
|
|
53
|
+
name,
|
|
54
|
+
label,
|
|
55
|
+
description,
|
|
56
|
+
keywords,
|
|
57
|
+
action: (doc: Document, sel: Selection): EditorState => {
|
|
58
|
+
return changeBlockType(doc, sel, blockType, meta)
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const BUILT_IN_COMMANDS: SlashCommand[] = [
|
|
64
|
+
makeBlockCommand('heading1', 'Heading 1', 'Large section heading', ['h1', 'title', 'heading'], 'heading', { level: 1 }),
|
|
65
|
+
makeBlockCommand('heading2', 'Heading 2', 'Medium section heading', ['h2', 'subtitle', 'heading'], 'heading', { level: 2 }),
|
|
66
|
+
makeBlockCommand('heading3', 'Heading 3', 'Small section heading', ['h3', 'heading'], 'heading', { level: 3 }),
|
|
67
|
+
makeBlockCommand('bullet-list', 'Bullet List', 'Create a bulleted list', ['ul', 'unordered', 'bullet', 'list'], 'list-item', { listType: 'bullet' }),
|
|
68
|
+
makeBlockCommand('numbered-list', 'Numbered List', 'Create a numbered list', ['ol', 'ordered', 'number', 'list'], 'list-item', { listType: 'ordered' }),
|
|
69
|
+
makeBlockCommand('quote', 'Quote', 'Create a blockquote', ['blockquote', 'citation', 'quote'], 'blockquote'),
|
|
70
|
+
makeBlockCommand('code', 'Code Block', 'Create a code block', ['code', 'pre', 'snippet', 'programming'], 'code-block'),
|
|
71
|
+
makeBlockCommand('divider', 'Divider', 'Insert a horizontal divider', ['hr', 'horizontal', 'rule', 'separator', 'line'], 'divider'),
|
|
72
|
+
{
|
|
73
|
+
name: 'image',
|
|
74
|
+
label: 'Image',
|
|
75
|
+
description: 'Insert an image',
|
|
76
|
+
keywords: ['img', 'picture', 'photo'],
|
|
77
|
+
action: (doc: Document, sel: Selection): EditorState => {
|
|
78
|
+
return changeBlockType(doc, sel, 'image')
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Fuzzy filter
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
function matchesQuery(command: SlashCommand, query: string): boolean {
|
|
87
|
+
const q = query.toLowerCase()
|
|
88
|
+
if (q === '') return true
|
|
89
|
+
if (command.name.toLowerCase().includes(q)) return true
|
|
90
|
+
if (command.label.toLowerCase().includes(q)) return true
|
|
91
|
+
if (command.description.toLowerCase().includes(q)) return true
|
|
92
|
+
return command.keywords.some((kw) => kw.toLowerCase().includes(q))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// Menu factory
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
export function createSlashCommandMenu(commands?: SlashCommand[]): SlashCommandMenu {
|
|
100
|
+
const allCommands = commands ?? BUILT_IN_COMMANDS
|
|
101
|
+
|
|
102
|
+
const state = {
|
|
103
|
+
isOpen: false,
|
|
104
|
+
query: '',
|
|
105
|
+
filteredCommands: [] as SlashCommand[],
|
|
106
|
+
selectedIndex: 0,
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function updateFiltered() {
|
|
110
|
+
state.filteredCommands = allCommands.filter((cmd) => matchesQuery(cmd, state.query))
|
|
111
|
+
if (state.selectedIndex >= state.filteredCommands.length) {
|
|
112
|
+
state.selectedIndex = Math.max(0, state.filteredCommands.length - 1)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const menu: SlashCommandMenu = {
|
|
117
|
+
get isOpen() { return state.isOpen },
|
|
118
|
+
get query() { return state.query },
|
|
119
|
+
get filteredCommands() { return state.filteredCommands },
|
|
120
|
+
get selectedIndex() { return state.selectedIndex },
|
|
121
|
+
|
|
122
|
+
open(query?: string) {
|
|
123
|
+
state.isOpen = true
|
|
124
|
+
state.query = query ?? ''
|
|
125
|
+
state.selectedIndex = 0
|
|
126
|
+
updateFiltered()
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
close() {
|
|
130
|
+
state.isOpen = false
|
|
131
|
+
state.query = ''
|
|
132
|
+
state.filteredCommands = []
|
|
133
|
+
state.selectedIndex = 0
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
search(query: string) {
|
|
137
|
+
state.query = query
|
|
138
|
+
updateFiltered()
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
selectNext() {
|
|
142
|
+
if (state.filteredCommands.length === 0) return
|
|
143
|
+
state.selectedIndex = (state.selectedIndex + 1) % state.filteredCommands.length
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
selectPrevious() {
|
|
147
|
+
if (state.filteredCommands.length === 0) return
|
|
148
|
+
state.selectedIndex =
|
|
149
|
+
(state.selectedIndex - 1 + state.filteredCommands.length) % state.filteredCommands.length
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
execute(doc: Document, sel: Selection): EditorState | null {
|
|
153
|
+
if (!state.isOpen || state.filteredCommands.length === 0) return null
|
|
154
|
+
const command = state.filteredCommands[state.selectedIndex]
|
|
155
|
+
if (!command) return null
|
|
156
|
+
|
|
157
|
+
// Remove the slash and query text from the block before executing
|
|
158
|
+
const block = findBlockById(doc, sel.anchor.blockId)
|
|
159
|
+
if (block) {
|
|
160
|
+
const text = getBlockText(block)
|
|
161
|
+
// Find the "/" that started this command
|
|
162
|
+
const slashIdx = text.lastIndexOf('/', sel.anchor.offset)
|
|
163
|
+
if (slashIdx !== -1) {
|
|
164
|
+
const d = cloneDocument(doc)
|
|
165
|
+
const b = d.blocks.find((bl) => bl.id === block.id)!
|
|
166
|
+
const beforeSlash = text.slice(0, slashIdx)
|
|
167
|
+
const afterCursor = text.slice(sel.anchor.offset)
|
|
168
|
+
b.content = [createTextSegment(beforeSlash + afterCursor)]
|
|
169
|
+
const newSel = createCollapsedSelection(b.id, slashIdx)
|
|
170
|
+
const result = command.action(d, newSel)
|
|
171
|
+
menu.close()
|
|
172
|
+
return result
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const result = command.action(doc, sel)
|
|
177
|
+
menu.close()
|
|
178
|
+
return result
|
|
179
|
+
},
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return menu
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Detect whether the user has typed a "/" that should trigger the slash command menu.
|
|
187
|
+
*/
|
|
188
|
+
export function detectSlashTrigger(
|
|
189
|
+
doc: Document,
|
|
190
|
+
sel: Selection,
|
|
191
|
+
): { triggered: boolean; query: string } {
|
|
192
|
+
const block = findBlockById(doc, sel.anchor.blockId)
|
|
193
|
+
if (!block) return { triggered: false, query: '' }
|
|
194
|
+
|
|
195
|
+
const text = getBlockText(block)
|
|
196
|
+
const textBeforeCursor = text.slice(0, sel.anchor.offset)
|
|
197
|
+
|
|
198
|
+
// Look for "/" preceded by start-of-text or a space
|
|
199
|
+
const slashIdx = textBeforeCursor.lastIndexOf('/')
|
|
200
|
+
if (slashIdx === -1) return { triggered: false, query: '' }
|
|
201
|
+
|
|
202
|
+
// The slash must be at position 0 or preceded by a space
|
|
203
|
+
if (slashIdx > 0 && textBeforeCursor[slashIdx - 1] !== ' ') {
|
|
204
|
+
return { triggered: false, query: '' }
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const query = textBeforeCursor.slice(slashIdx + 1)
|
|
208
|
+
// If query contains spaces, the slash command is no longer active
|
|
209
|
+
if (query.includes(' ')) return { triggered: false, query: '' }
|
|
210
|
+
|
|
211
|
+
return { triggered: true, query }
|
|
212
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { cva } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
export const searchBarVariants = cva({
|
|
4
|
+
base: 'relative flex items-center w-full rounded-md border bg-background text-foreground',
|
|
5
|
+
variants: {
|
|
6
|
+
size: {
|
|
7
|
+
sm: 'h-8 text-sm px-2',
|
|
8
|
+
md: 'h-10 text-base px-3',
|
|
9
|
+
lg: 'h-12 text-lg px-4',
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
defaultVariants: {
|
|
13
|
+
size: 'md',
|
|
14
|
+
},
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
export const searchResultVariants = cva({
|
|
18
|
+
base: 'absolute top-full left-0 w-full mt-1 rounded-md border bg-popover text-popover-foreground shadow-md z-50 overflow-hidden',
|
|
19
|
+
variants: {
|
|
20
|
+
size: {
|
|
21
|
+
sm: 'text-sm',
|
|
22
|
+
md: 'text-base',
|
|
23
|
+
lg: 'text-lg',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
defaultVariants: {
|
|
27
|
+
size: 'md',
|
|
28
|
+
},
|
|
29
|
+
})
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { KeyboardHandlerMap } from '../shared/index.ts'
|
|
2
|
+
import { generateId, Keys, createKeyboardHandler } from '../shared/index.ts'
|
|
3
|
+
|
|
4
|
+
export interface SearchBarProps {
|
|
5
|
+
value?: string
|
|
6
|
+
onValueChange?: (value: string) => void
|
|
7
|
+
onSearch?: (value: string) => void
|
|
8
|
+
debounceMs?: number
|
|
9
|
+
placeholder?: string
|
|
10
|
+
loading?: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface SearchBarAPI {
|
|
14
|
+
/** Current state */
|
|
15
|
+
state: { value: string; isSearching: boolean }
|
|
16
|
+
/** Props to spread on the input element */
|
|
17
|
+
inputProps: {
|
|
18
|
+
role: 'combobox'
|
|
19
|
+
'aria-expanded': boolean
|
|
20
|
+
'aria-controls': string
|
|
21
|
+
'aria-autocomplete': 'list'
|
|
22
|
+
}
|
|
23
|
+
/** Props to spread on the results list element */
|
|
24
|
+
listProps: {
|
|
25
|
+
role: 'listbox'
|
|
26
|
+
id: string
|
|
27
|
+
}
|
|
28
|
+
/** Set the search value */
|
|
29
|
+
setValue: (value: string) => void
|
|
30
|
+
/** Clear the search value */
|
|
31
|
+
clear: () => void
|
|
32
|
+
/** Keyboard handler map */
|
|
33
|
+
keyboardHandlers: KeyboardHandlerMap
|
|
34
|
+
/** Pre-built keyboard event handler */
|
|
35
|
+
handleKeyDown: (event: KeyboardEvent) => void
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function createSearchBar(props: SearchBarProps = {}): SearchBarAPI {
|
|
39
|
+
const {
|
|
40
|
+
value: initialValue = '',
|
|
41
|
+
onValueChange,
|
|
42
|
+
onSearch,
|
|
43
|
+
debounceMs = 300,
|
|
44
|
+
placeholder: _placeholder,
|
|
45
|
+
loading = false,
|
|
46
|
+
} = props
|
|
47
|
+
|
|
48
|
+
const listId = generateId('rfr-search-list')
|
|
49
|
+
|
|
50
|
+
let currentValue = initialValue
|
|
51
|
+
let isSearching = loading
|
|
52
|
+
let debounceTimer: ReturnType<typeof setTimeout> | undefined
|
|
53
|
+
|
|
54
|
+
const state = { value: currentValue, isSearching }
|
|
55
|
+
|
|
56
|
+
function setValue(value: string) {
|
|
57
|
+
currentValue = value
|
|
58
|
+
state.value = value
|
|
59
|
+
onValueChange?.(value)
|
|
60
|
+
|
|
61
|
+
if (debounceTimer !== undefined) {
|
|
62
|
+
clearTimeout(debounceTimer)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (value.length > 0) {
|
|
66
|
+
isSearching = true
|
|
67
|
+
state.isSearching = true
|
|
68
|
+
debounceTimer = setTimeout(() => {
|
|
69
|
+
onSearch?.(value)
|
|
70
|
+
isSearching = false
|
|
71
|
+
state.isSearching = false
|
|
72
|
+
}, debounceMs)
|
|
73
|
+
} else {
|
|
74
|
+
isSearching = false
|
|
75
|
+
state.isSearching = false
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function clear() {
|
|
80
|
+
if (debounceTimer !== undefined) {
|
|
81
|
+
clearTimeout(debounceTimer)
|
|
82
|
+
}
|
|
83
|
+
currentValue = ''
|
|
84
|
+
state.value = ''
|
|
85
|
+
isSearching = false
|
|
86
|
+
state.isSearching = false
|
|
87
|
+
onValueChange?.('')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const inputProps = {
|
|
91
|
+
role: 'combobox' as const,
|
|
92
|
+
'aria-expanded': currentValue.length > 0,
|
|
93
|
+
'aria-controls': listId,
|
|
94
|
+
'aria-autocomplete': 'list' as const,
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const listProps = {
|
|
98
|
+
role: 'listbox' as const,
|
|
99
|
+
id: listId,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const keyboardHandlers: KeyboardHandlerMap = {
|
|
103
|
+
[Keys.ArrowDown]: () => {
|
|
104
|
+
// Focus first result - signaled via handler
|
|
105
|
+
},
|
|
106
|
+
[Keys.Escape]: () => {
|
|
107
|
+
clear()
|
|
108
|
+
},
|
|
109
|
+
[Keys.Enter]: () => {
|
|
110
|
+
if (debounceTimer !== undefined) {
|
|
111
|
+
clearTimeout(debounceTimer)
|
|
112
|
+
}
|
|
113
|
+
onSearch?.(currentValue)
|
|
114
|
+
isSearching = false
|
|
115
|
+
state.isSearching = false
|
|
116
|
+
},
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const handleKeyDown = createKeyboardHandler(keyboardHandlers)
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
state,
|
|
123
|
+
inputProps,
|
|
124
|
+
listProps,
|
|
125
|
+
setValue,
|
|
126
|
+
clear,
|
|
127
|
+
keyboardHandlers,
|
|
128
|
+
handleKeyDown,
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { cva } from '../shared/index.ts'
|
|
2
|
+
import type { TokenContract } from '../shared/index.ts'
|
|
3
|
+
|
|
4
|
+
export const selectTokens: TokenContract = {
|
|
5
|
+
name: 'select',
|
|
6
|
+
tokens: {
|
|
7
|
+
bg: { variable: '--rfr-select-bg', fallback: 'hsl(var(--background))' },
|
|
8
|
+
fg: { variable: '--rfr-select-fg', fallback: 'hsl(var(--foreground))' },
|
|
9
|
+
border: { variable: '--rfr-select-border', fallback: 'hsl(var(--border))' },
|
|
10
|
+
ring: { variable: '--rfr-select-ring', fallback: 'hsl(var(--ring))' },
|
|
11
|
+
},
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const selectTriggerVariants = cva({
|
|
15
|
+
base: 'flex h-9 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
|
16
|
+
variants: {
|
|
17
|
+
size: {
|
|
18
|
+
sm: 'h-8 text-xs',
|
|
19
|
+
default: 'h-9 text-sm',
|
|
20
|
+
lg: 'h-10 text-base',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
defaultVariants: {
|
|
24
|
+
size: 'default',
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
export const selectContentVariants = cva({
|
|
29
|
+
base: 'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95',
|
|
30
|
+
variants: {
|
|
31
|
+
position: {
|
|
32
|
+
popper: 'translate-y-1',
|
|
33
|
+
'item-aligned': '',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
defaultVariants: {
|
|
37
|
+
position: 'popper',
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
export const selectItemVariants = cva({
|
|
42
|
+
base: 'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
|
43
|
+
variants: {
|
|
44
|
+
selected: {
|
|
45
|
+
true: 'bg-accent/50',
|
|
46
|
+
false: '',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
defaultVariants: {
|
|
50
|
+
selected: 'false',
|
|
51
|
+
},
|
|
52
|
+
})
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { AccessibilityProps, KeyboardHandlerMap } from '../shared/index.ts'
|
|
2
|
+
import { Keys, generateId } from '../shared/index.ts'
|
|
3
|
+
|
|
4
|
+
export interface SelectOption {
|
|
5
|
+
value: string
|
|
6
|
+
label: string
|
|
7
|
+
disabled?: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface SelectProps {
|
|
11
|
+
value?: string
|
|
12
|
+
options?: SelectOption[]
|
|
13
|
+
placeholder?: string
|
|
14
|
+
disabled?: boolean
|
|
15
|
+
open?: boolean
|
|
16
|
+
name?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface SelectAPI {
|
|
20
|
+
/** Trigger element ARIA + keyboard props */
|
|
21
|
+
triggerProps: {
|
|
22
|
+
ariaProps: Partial<AccessibilityProps>
|
|
23
|
+
keyboardHandlers: KeyboardHandlerMap
|
|
24
|
+
dataAttributes: Record<string, string>
|
|
25
|
+
}
|
|
26
|
+
/** Content (dropdown) props */
|
|
27
|
+
contentProps: {
|
|
28
|
+
ariaProps: Partial<AccessibilityProps>
|
|
29
|
+
dataAttributes: Record<string, string>
|
|
30
|
+
}
|
|
31
|
+
/** Get props for a single option */
|
|
32
|
+
getOptionProps: (option: SelectOption) => {
|
|
33
|
+
ariaProps: Partial<AccessibilityProps>
|
|
34
|
+
dataAttributes: Record<string, string>
|
|
35
|
+
isSelected: boolean
|
|
36
|
+
}
|
|
37
|
+
/** Whether interaction should be blocked */
|
|
38
|
+
isInteractive: boolean
|
|
39
|
+
/** Current state */
|
|
40
|
+
state: {
|
|
41
|
+
open: boolean
|
|
42
|
+
selectedValue: string | undefined
|
|
43
|
+
selectedLabel: string | undefined
|
|
44
|
+
disabled: boolean
|
|
45
|
+
placeholder: string
|
|
46
|
+
}
|
|
47
|
+
/** Generated IDs for trigger/content association */
|
|
48
|
+
ids: {
|
|
49
|
+
trigger: string
|
|
50
|
+
content: string
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function createSelect(props: SelectProps = {}): SelectAPI {
|
|
55
|
+
const {
|
|
56
|
+
value,
|
|
57
|
+
options = [],
|
|
58
|
+
placeholder = 'Select an option',
|
|
59
|
+
disabled = false,
|
|
60
|
+
open = false,
|
|
61
|
+
} = props
|
|
62
|
+
|
|
63
|
+
const isInteractive = !disabled
|
|
64
|
+
const selectedOption = options.find((o) => o.value === value)
|
|
65
|
+
|
|
66
|
+
const triggerId = generateId('select-trigger')
|
|
67
|
+
const contentId = generateId('select-content')
|
|
68
|
+
|
|
69
|
+
const triggerAriaProps: Partial<AccessibilityProps> = {
|
|
70
|
+
role: 'combobox',
|
|
71
|
+
'aria-expanded': open,
|
|
72
|
+
'aria-controls': contentId,
|
|
73
|
+
'aria-labelledby': triggerId,
|
|
74
|
+
}
|
|
75
|
+
if (disabled) {
|
|
76
|
+
triggerAriaProps['aria-disabled'] = true
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const triggerKeyboardHandlers: KeyboardHandlerMap = {}
|
|
80
|
+
if (!isInteractive) {
|
|
81
|
+
triggerKeyboardHandlers[Keys.Enter] = (e) => e.preventDefault()
|
|
82
|
+
triggerKeyboardHandlers[Keys.Space] = (e) => e.preventDefault()
|
|
83
|
+
triggerKeyboardHandlers[Keys.ArrowDown] = (e) => e.preventDefault()
|
|
84
|
+
triggerKeyboardHandlers[Keys.ArrowUp] = (e) => e.preventDefault()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const triggerDataAttributes: Record<string, string> = {
|
|
88
|
+
'data-state': open ? 'open' : 'closed',
|
|
89
|
+
}
|
|
90
|
+
if (disabled) {
|
|
91
|
+
triggerDataAttributes['data-disabled'] = ''
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const contentAriaProps: Partial<AccessibilityProps> = {
|
|
95
|
+
role: 'listbox',
|
|
96
|
+
'aria-labelledby': triggerId,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const contentDataAttributes: Record<string, string> = {
|
|
100
|
+
'data-state': open ? 'open' : 'closed',
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function getOptionProps(option: SelectOption) {
|
|
104
|
+
const isSelected = option.value === value
|
|
105
|
+
const optionAriaProps: Partial<AccessibilityProps> = {
|
|
106
|
+
role: 'option',
|
|
107
|
+
'aria-selected': isSelected,
|
|
108
|
+
}
|
|
109
|
+
if (option.disabled) {
|
|
110
|
+
optionAriaProps['aria-disabled'] = true
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const optionDataAttributes: Record<string, string> = {}
|
|
114
|
+
if (isSelected) {
|
|
115
|
+
optionDataAttributes['data-state'] = 'checked'
|
|
116
|
+
}
|
|
117
|
+
if (option.disabled) {
|
|
118
|
+
optionDataAttributes['data-disabled'] = ''
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
ariaProps: optionAriaProps,
|
|
123
|
+
dataAttributes: optionDataAttributes,
|
|
124
|
+
isSelected,
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
triggerProps: {
|
|
130
|
+
ariaProps: triggerAriaProps,
|
|
131
|
+
keyboardHandlers: triggerKeyboardHandlers,
|
|
132
|
+
dataAttributes: triggerDataAttributes,
|
|
133
|
+
},
|
|
134
|
+
contentProps: {
|
|
135
|
+
ariaProps: contentAriaProps,
|
|
136
|
+
dataAttributes: contentDataAttributes,
|
|
137
|
+
},
|
|
138
|
+
getOptionProps,
|
|
139
|
+
isInteractive,
|
|
140
|
+
state: {
|
|
141
|
+
open,
|
|
142
|
+
selectedValue: selectedOption?.value,
|
|
143
|
+
selectedLabel: selectedOption?.label,
|
|
144
|
+
disabled,
|
|
145
|
+
placeholder,
|
|
146
|
+
},
|
|
147
|
+
ids: {
|
|
148
|
+
trigger: triggerId,
|
|
149
|
+
content: contentId,
|
|
150
|
+
},
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accessibility constants and utilities used across all components.
|
|
3
|
+
*
|
|
4
|
+
* Refraction UI Accessibility Principles:
|
|
5
|
+
* 1. NEVER convey meaning through color alone — always provide icon, text, or pattern
|
|
6
|
+
* 2. All interactive elements are keyboard navigable
|
|
7
|
+
* 3. All dynamic content has aria-live announcements
|
|
8
|
+
* 4. All colors meet WCAG AA contrast ratios (4.5:1 normal, 3:1 large)
|
|
9
|
+
* 5. Respect prefers-reduced-motion
|
|
10
|
+
* 6. Respect prefers-contrast
|
|
11
|
+
* 7. Focus indicators are always visible on keyboard navigation
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Standard icon names used across components for status indication */
|
|
15
|
+
export const STATUS_ICONS = {
|
|
16
|
+
success: 'check-circle',
|
|
17
|
+
error: 'x-circle',
|
|
18
|
+
warning: 'alert-triangle',
|
|
19
|
+
info: 'info-circle',
|
|
20
|
+
} as const
|
|
21
|
+
|
|
22
|
+
export type StatusIconName = typeof STATUS_ICONS[keyof typeof STATUS_ICONS]
|
|
23
|
+
|
|
24
|
+
/** Check if a component state relies on color alone (for testing) */
|
|
25
|
+
export function hasNonColorIndicator(config: {
|
|
26
|
+
hasIcon?: boolean
|
|
27
|
+
hasText?: boolean
|
|
28
|
+
hasPattern?: boolean
|
|
29
|
+
hasBorder?: boolean
|
|
30
|
+
hasShape?: boolean
|
|
31
|
+
}): boolean {
|
|
32
|
+
return Object.values(config).some(Boolean)
|
|
33
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Merge multiple ARIA prop objects, later values override earlier ones */
|
|
2
|
+
export function mergeAriaProps(
|
|
3
|
+
...propSets: Array<Record<string, unknown>>
|
|
4
|
+
): Record<string, unknown> {
|
|
5
|
+
const result: Record<string, unknown> = {}
|
|
6
|
+
for (const props of propSets) {
|
|
7
|
+
for (const [key, value] of Object.entries(props)) {
|
|
8
|
+
if (value !== undefined) {
|
|
9
|
+
result[key] = value
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return result
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let idCounter = 0
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Generate a unique ID, safe for SSR (deterministic within a render pass).
|
|
20
|
+
* In browsers, uses crypto.randomUUID when available.
|
|
21
|
+
*/
|
|
22
|
+
export function generateId(prefix = 'rfr'): string {
|
|
23
|
+
idCounter++
|
|
24
|
+
return `${prefix}-${idCounter}`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Reset the ID counter (useful for tests) */
|
|
28
|
+
export function resetIdCounter(): void {
|
|
29
|
+
idCounter = 0
|
|
30
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight class name utility — our own implementation.
|
|
3
|
+
* Handles conditional classes, arrays, and falsy values.
|
|
4
|
+
* No external dependencies (no clsx, no tailwind-merge).
|
|
5
|
+
*
|
|
6
|
+
* For Tailwind class conflict resolution (e.g., 'p-2 p-4' → 'p-4'),
|
|
7
|
+
* consumers can use @refraction-ui/tailwind-config which provides
|
|
8
|
+
* a tw-merge-aware variant of this function.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
type ClassValue = string | number | boolean | undefined | null | ClassValue[]
|
|
12
|
+
type ClassRecord = Record<string, boolean | undefined | null>
|
|
13
|
+
|
|
14
|
+
export function cn(...inputs: Array<ClassValue | ClassRecord>): string {
|
|
15
|
+
const classes: string[] = []
|
|
16
|
+
|
|
17
|
+
for (const input of inputs) {
|
|
18
|
+
if (!input) continue
|
|
19
|
+
|
|
20
|
+
if (typeof input === 'string') {
|
|
21
|
+
classes.push(input)
|
|
22
|
+
} else if (typeof input === 'number') {
|
|
23
|
+
classes.push(String(input))
|
|
24
|
+
} else if (Array.isArray(input)) {
|
|
25
|
+
const nested = cn(...input)
|
|
26
|
+
if (nested) classes.push(nested)
|
|
27
|
+
} else if (typeof input === 'object') {
|
|
28
|
+
for (const [key, value] of Object.entries(input)) {
|
|
29
|
+
if (value) classes.push(key)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return classes.join(' ')
|
|
35
|
+
}
|