@refraction-ui/astro 0.3.1 → 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.
- 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/index.ts +77 -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/package.json +22 -20
- package/src/index.ts +0 -92
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
import {
|
|
3
|
+
createProgressDisplay,
|
|
4
|
+
statsGridVariants,
|
|
5
|
+
statCardVariants,
|
|
6
|
+
type StatCardData,
|
|
7
|
+
type BadgeData,
|
|
8
|
+
} from '@refraction-ui/progress-display'
|
|
9
|
+
import { cn } from '../shared/index.ts'
|
|
10
|
+
|
|
11
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
12
|
+
stats: StatCardData[]
|
|
13
|
+
badges?: BadgeData[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { stats, badges = [], class: className, ...props } = Astro.props
|
|
17
|
+
const api = createProgressDisplay({ stats, badges })
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<div class={cn(statsGridVariants(), className)} {...api.ariaProps} {...props}>
|
|
21
|
+
{api.stats.map((stat) => (
|
|
22
|
+
<div
|
|
23
|
+
class={statCardVariants({
|
|
24
|
+
color: (stat.color as 'default' | 'primary' | 'success' | 'warning' | 'destructive') ?? 'default',
|
|
25
|
+
})}
|
|
26
|
+
>
|
|
27
|
+
{stat.icon && <span class="text-2xl">{stat.icon}</span>}
|
|
28
|
+
<div class="text-2xl font-bold">{stat.value}</div>
|
|
29
|
+
<div class="text-sm text-muted-foreground">{stat.label}</div>
|
|
30
|
+
</div>
|
|
31
|
+
))}
|
|
32
|
+
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { radioGroupVariants } from '@refraction-ui/radio'
|
|
3
|
+
import { cn } from '../shared/index.ts'
|
|
4
|
+
|
|
5
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
6
|
+
value?: string
|
|
7
|
+
name?: string
|
|
8
|
+
disabled?: boolean
|
|
9
|
+
orientation?: 'horizontal' | 'vertical'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { value, name, disabled = false, orientation = 'vertical', class: className, ...props } = Astro.props
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<div
|
|
16
|
+
role="radiogroup"
|
|
17
|
+
aria-orientation={orientation}
|
|
18
|
+
aria-disabled={disabled || undefined}
|
|
19
|
+
class={cn(radioGroupVariants({ orientation }), className)}
|
|
20
|
+
data-rfr-radio-group
|
|
21
|
+
data-rfr-radio-value={value || ''}
|
|
22
|
+
data-rfr-radio-name={name || ''}
|
|
23
|
+
data-rfr-radio-disabled={disabled ? '' : undefined}
|
|
24
|
+
{...props}
|
|
25
|
+
>
|
|
26
|
+
<slot />
|
|
27
|
+
{name && <input type="hidden" name={name} value={value || ''} data-rfr-radio-input />}
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<script>
|
|
31
|
+
document.querySelectorAll<HTMLElement>('[data-rfr-radio-group]').forEach((group) => {
|
|
32
|
+
const items = () => group.querySelectorAll<HTMLButtonElement>('[data-rfr-radio-item]')
|
|
33
|
+
|
|
34
|
+
function selectItem(item: HTMLButtonElement) {
|
|
35
|
+
const groupDisabled = group.hasAttribute('data-rfr-radio-disabled')
|
|
36
|
+
if (groupDisabled || item.disabled) return
|
|
37
|
+
|
|
38
|
+
const value = item.getAttribute('data-rfr-radio-item-value') || ''
|
|
39
|
+
|
|
40
|
+
// Update all items in this group
|
|
41
|
+
items().forEach((btn) => {
|
|
42
|
+
const btnValue = btn.getAttribute('data-rfr-radio-item-value') || ''
|
|
43
|
+
const isSelected = btnValue === value
|
|
44
|
+
btn.setAttribute('aria-checked', String(isSelected))
|
|
45
|
+
btn.setAttribute('tabindex', isSelected ? '0' : '-1')
|
|
46
|
+
|
|
47
|
+
const label = btn.closest('[data-rfr-radio-item-label]') as HTMLElement | null
|
|
48
|
+
if (label) {
|
|
49
|
+
label.setAttribute('data-state', isSelected ? 'checked' : 'unchecked')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Update circle styling
|
|
53
|
+
const circle = btn as HTMLElement
|
|
54
|
+
if (isSelected) {
|
|
55
|
+
circle.classList.add('border-primary', 'bg-primary')
|
|
56
|
+
circle.classList.remove('border-input')
|
|
57
|
+
// Ensure inner dot exists
|
|
58
|
+
if (!circle.querySelector('[data-rfr-radio-dot]')) {
|
|
59
|
+
const dot = document.createElement('span')
|
|
60
|
+
dot.className = 'block h-2 w-2 rounded-full bg-primary-foreground mx-auto'
|
|
61
|
+
dot.setAttribute('data-rfr-radio-dot', '')
|
|
62
|
+
circle.appendChild(dot)
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
circle.classList.remove('border-primary', 'bg-primary')
|
|
66
|
+
circle.classList.add('border-input')
|
|
67
|
+
const dot = circle.querySelector('[data-rfr-radio-dot]')
|
|
68
|
+
if (dot) dot.remove()
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// Update group data attribute
|
|
73
|
+
group.setAttribute('data-rfr-radio-value', value)
|
|
74
|
+
|
|
75
|
+
// Update hidden input
|
|
76
|
+
const input = group.querySelector('[data-rfr-radio-input]') as HTMLInputElement | null
|
|
77
|
+
if (input) input.value = value
|
|
78
|
+
|
|
79
|
+
group.dispatchEvent(new CustomEvent('change', { detail: { value } }))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
group.addEventListener('click', (e) => {
|
|
83
|
+
const item = (e.target as HTMLElement).closest<HTMLButtonElement>('[data-rfr-radio-item]')
|
|
84
|
+
if (item) selectItem(item)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
group.addEventListener('keydown', (e) => {
|
|
88
|
+
const target = e.target as HTMLElement
|
|
89
|
+
if (!target.hasAttribute('data-rfr-radio-item')) return
|
|
90
|
+
|
|
91
|
+
const allItems = Array.from(items()).filter((i) => !i.disabled)
|
|
92
|
+
const currentIndex = allItems.indexOf(target as HTMLButtonElement)
|
|
93
|
+
if (currentIndex === -1) return
|
|
94
|
+
|
|
95
|
+
let nextIndex = -1
|
|
96
|
+
if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
|
|
97
|
+
e.preventDefault()
|
|
98
|
+
nextIndex = (currentIndex + 1) % allItems.length
|
|
99
|
+
} else if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') {
|
|
100
|
+
e.preventDefault()
|
|
101
|
+
nextIndex = (currentIndex - 1 + allItems.length) % allItems.length
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (nextIndex >= 0) {
|
|
105
|
+
const nextItem = allItems[nextIndex]
|
|
106
|
+
nextItem.focus()
|
|
107
|
+
selectItem(nextItem)
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
</script>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { radioItemVariants, radioCircleVariants } from '@refraction-ui/radio'
|
|
3
|
+
import { cn } from '../shared/index.ts'
|
|
4
|
+
|
|
5
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
6
|
+
value: string
|
|
7
|
+
disabled?: boolean
|
|
8
|
+
checked?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { value, disabled = false, checked = false, class: className, ...props } = Astro.props
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<label
|
|
15
|
+
class={cn(radioItemVariants({ disabled: disabled ? 'true' : 'false' }), className)}
|
|
16
|
+
data-state={checked ? 'checked' : 'unchecked'}
|
|
17
|
+
data-rfr-radio-item-label
|
|
18
|
+
{...props}
|
|
19
|
+
>
|
|
20
|
+
<button
|
|
21
|
+
type="button"
|
|
22
|
+
role="radio"
|
|
23
|
+
aria-checked={checked}
|
|
24
|
+
aria-disabled={disabled || undefined}
|
|
25
|
+
tabindex={checked ? 0 : -1}
|
|
26
|
+
disabled={disabled || undefined}
|
|
27
|
+
class={cn(radioCircleVariants({ checked: checked ? 'true' : 'false' }))}
|
|
28
|
+
data-rfr-radio-item
|
|
29
|
+
data-rfr-radio-item-value={value}
|
|
30
|
+
>
|
|
31
|
+
{checked && (
|
|
32
|
+
<span class="block h-2 w-2 rounded-full bg-primary-foreground mx-auto" data-rfr-radio-dot />
|
|
33
|
+
)}
|
|
34
|
+
</button>
|
|
35
|
+
<span class="text-sm"><slot /></span>
|
|
36
|
+
</label>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
import {
|
|
3
|
+
createReactionBar,
|
|
4
|
+
reactionBarStyles,
|
|
5
|
+
reactionPillVariants,
|
|
6
|
+
reactionAddButtonStyles,
|
|
7
|
+
reactionEmojiStyles,
|
|
8
|
+
reactionCountStyles,
|
|
9
|
+
type Reaction,
|
|
10
|
+
} from '@refraction-ui/reaction-bar'
|
|
11
|
+
import { cn } from '../shared/index.ts'
|
|
12
|
+
|
|
13
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
14
|
+
/** Array of reactions to display */
|
|
15
|
+
reactions: Reaction[]
|
|
16
|
+
/** Whether to show the add reaction button. Default: true */
|
|
17
|
+
showAddButton?: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
reactions,
|
|
22
|
+
showAddButton = true,
|
|
23
|
+
class: className,
|
|
24
|
+
...props
|
|
25
|
+
} = Astro.props
|
|
26
|
+
|
|
27
|
+
const api = createReactionBar({ reactions })
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
<div
|
|
31
|
+
data-rfr-reaction-bar
|
|
32
|
+
data-rfr-rb-reactions={JSON.stringify(reactions)}
|
|
33
|
+
class={cn(reactionBarStyles, className)}
|
|
34
|
+
role="group"
|
|
35
|
+
aria-label="Reactions"
|
|
36
|
+
{...props}
|
|
37
|
+
>
|
|
38
|
+
{api.reactions.map((reaction) => (
|
|
39
|
+
<button
|
|
40
|
+
type="button"
|
|
41
|
+
data-rfr-rb-emoji={reaction.emoji}
|
|
42
|
+
class={reactionPillVariants({ state: reaction.userReacted ? 'active' : 'inactive' })}
|
|
43
|
+
role="button"
|
|
44
|
+
aria-pressed={reaction.userReacted ? 'true' : 'false'}
|
|
45
|
+
aria-label={`${reaction.emoji} ${reaction.count} reaction${reaction.count === 1 ? '' : 's'}${reaction.userReacted ? ', you reacted' : ''}`}
|
|
46
|
+
>
|
|
47
|
+
<span class={reactionEmojiStyles}>{reaction.emoji}</span>
|
|
48
|
+
<span class={reactionCountStyles} data-rfr-rb-count>{reaction.count}</span>
|
|
49
|
+
</button>
|
|
50
|
+
))}
|
|
51
|
+
{showAddButton && (
|
|
52
|
+
<button
|
|
53
|
+
type="button"
|
|
54
|
+
data-rfr-rb-add
|
|
55
|
+
class={reactionAddButtonStyles}
|
|
56
|
+
role="button"
|
|
57
|
+
aria-label="Add reaction"
|
|
58
|
+
>
|
|
59
|
+
+
|
|
60
|
+
</button>
|
|
61
|
+
)}
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<script>
|
|
65
|
+
function initReactionBars() {
|
|
66
|
+
document.querySelectorAll<HTMLElement>('[data-rfr-reaction-bar]').forEach((el) => {
|
|
67
|
+
if (el.hasAttribute('data-rfr-rb-init')) return
|
|
68
|
+
el.setAttribute('data-rfr-rb-init', '')
|
|
69
|
+
|
|
70
|
+
// Handle emoji button clicks (toggle reaction)
|
|
71
|
+
el.querySelectorAll<HTMLElement>('[data-rfr-rb-emoji]').forEach((btn) => {
|
|
72
|
+
btn.addEventListener('click', () => {
|
|
73
|
+
const emoji = btn.getAttribute('data-rfr-rb-emoji')
|
|
74
|
+
if (!emoji) return
|
|
75
|
+
|
|
76
|
+
const isPressed = btn.getAttribute('aria-pressed') === 'true'
|
|
77
|
+
const countEl = btn.querySelector<HTMLElement>('[data-rfr-rb-count]')
|
|
78
|
+
const currentCount = countEl ? Number(countEl.textContent || 0) : 0
|
|
79
|
+
|
|
80
|
+
// Toggle state
|
|
81
|
+
const newPressed = !isPressed
|
|
82
|
+
const newCount = newPressed ? currentCount + 1 : Math.max(0, currentCount - 1)
|
|
83
|
+
|
|
84
|
+
btn.setAttribute('aria-pressed', String(newPressed))
|
|
85
|
+
if (countEl) countEl.textContent = String(newCount)
|
|
86
|
+
|
|
87
|
+
// Update styling
|
|
88
|
+
const activeClass = 'border-primary bg-primary/10 text-primary'
|
|
89
|
+
const inactiveClass = 'border-border bg-background text-foreground'
|
|
90
|
+
const baseClass = 'inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs cursor-pointer transition-colors hover:bg-accent'
|
|
91
|
+
|
|
92
|
+
btn.className = `${baseClass} ${newPressed ? activeClass : inactiveClass}`
|
|
93
|
+
|
|
94
|
+
// Update aria-label
|
|
95
|
+
btn.setAttribute('aria-label', `${emoji} ${newCount} reaction${newCount === 1 ? '' : 's'}${newPressed ? ', you reacted' : ''}`)
|
|
96
|
+
|
|
97
|
+
el.dispatchEvent(new CustomEvent('rfr-reaction-toggle', {
|
|
98
|
+
detail: { emoji, userReacted: newPressed, count: newCount },
|
|
99
|
+
bubbles: true,
|
|
100
|
+
}))
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
// Handle add button click
|
|
105
|
+
const addBtn = el.querySelector<HTMLElement>('[data-rfr-rb-add]')
|
|
106
|
+
addBtn?.addEventListener('click', () => {
|
|
107
|
+
el.dispatchEvent(new CustomEvent('rfr-reaction-add', {
|
|
108
|
+
detail: { emoji: '\u{1F44D}' },
|
|
109
|
+
bubbles: true,
|
|
110
|
+
}))
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
initReactionBars()
|
|
116
|
+
document.addEventListener('astro:after-swap', initReactionBars)
|
|
117
|
+
</script>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* ResizableDivider — draggable handle between two panes.
|
|
4
|
+
* Pointer events are handled by the parent ResizableLayout script.
|
|
5
|
+
*/
|
|
6
|
+
import { resizableDividerVariants } from '@refraction-ui/resizable-layout'
|
|
7
|
+
import { cn } from '../shared/index.ts'
|
|
8
|
+
import type { Orientation } from '../shared/index.ts'
|
|
9
|
+
|
|
10
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
11
|
+
/** Index of the divider (between pane[index] and pane[index+1]) */
|
|
12
|
+
index: number
|
|
13
|
+
/** Orientation inherited from parent (default 'horizontal') */
|
|
14
|
+
orientation?: Orientation
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { index, orientation = 'horizontal', class: className, ...props } = Astro.props
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<div
|
|
21
|
+
role="separator"
|
|
22
|
+
aria-orientation={orientation}
|
|
23
|
+
tabindex="0"
|
|
24
|
+
class={cn(resizableDividerVariants({ orientation }), className)}
|
|
25
|
+
data-rfr-divider-index={index}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* ResizableLayout — flex container with resizable panes separated by draggable dividers.
|
|
4
|
+
* Includes a client-side script for pointer-based drag resize.
|
|
5
|
+
*/
|
|
6
|
+
import {
|
|
7
|
+
resizableLayoutVariants,
|
|
8
|
+
} from '@refraction-ui/resizable-layout'
|
|
9
|
+
import { cn } from '../shared/index.ts'
|
|
10
|
+
import type { Orientation } from '../shared/index.ts'
|
|
11
|
+
|
|
12
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
13
|
+
/** Layout direction (default 'horizontal') */
|
|
14
|
+
orientation?: Orientation
|
|
15
|
+
/** Initial pane sizes as percentages (must sum to 100) */
|
|
16
|
+
defaultSizes?: number[]
|
|
17
|
+
/** Minimum size for each pane (percentage) */
|
|
18
|
+
minSizes?: number[]
|
|
19
|
+
/** Maximum size for each pane (percentage) */
|
|
20
|
+
maxSizes?: number[]
|
|
21
|
+
/** localStorage key for persisting sizes */
|
|
22
|
+
persistKey?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const {
|
|
26
|
+
orientation = 'horizontal',
|
|
27
|
+
defaultSizes = [50, 50],
|
|
28
|
+
minSizes = [],
|
|
29
|
+
maxSizes = [],
|
|
30
|
+
persistKey,
|
|
31
|
+
class: className,
|
|
32
|
+
style,
|
|
33
|
+
...props
|
|
34
|
+
} = Astro.props
|
|
35
|
+
|
|
36
|
+
// Build initial CSS variables for pane sizes
|
|
37
|
+
const cssVars = defaultSizes.map((s, i) => `--rfr-pane-${i}-size:${s}%`).join(';')
|
|
38
|
+
const fullStyle = [cssVars, typeof style === 'string' ? style : ''].filter(Boolean).join(';')
|
|
39
|
+
|
|
40
|
+
const configJSON = JSON.stringify({ orientation, defaultSizes, minSizes, maxSizes, persistKey })
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
<div
|
|
44
|
+
class={cn(resizableLayoutVariants({ orientation }), className)}
|
|
45
|
+
style={fullStyle}
|
|
46
|
+
data-rfr-resizable-layout
|
|
47
|
+
data-rfr-resizable-config={configJSON}
|
|
48
|
+
data-orientation={orientation}
|
|
49
|
+
{...props}
|
|
50
|
+
>
|
|
51
|
+
<slot />
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<script is:inline>
|
|
55
|
+
;(function () {
|
|
56
|
+
document.querySelectorAll('[data-rfr-resizable-layout]').forEach(function (layout) {
|
|
57
|
+
var config = {}
|
|
58
|
+
try { config = JSON.parse(layout.getAttribute('data-rfr-resizable-config') || '{}') } catch (_) { /* */ }
|
|
59
|
+
|
|
60
|
+
var orientation = config.orientation || 'horizontal'
|
|
61
|
+
var sizes = null
|
|
62
|
+
var persistKey = config.persistKey
|
|
63
|
+
var minSizes = config.minSizes || []
|
|
64
|
+
var maxSizes = config.maxSizes || []
|
|
65
|
+
|
|
66
|
+
// Try loading from localStorage
|
|
67
|
+
if (persistKey) {
|
|
68
|
+
try {
|
|
69
|
+
var stored = localStorage.getItem(persistKey)
|
|
70
|
+
if (stored) {
|
|
71
|
+
var parsed = JSON.parse(stored)
|
|
72
|
+
if (Array.isArray(parsed) && parsed.length === (config.defaultSizes || []).length) {
|
|
73
|
+
sizes = parsed
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
} catch (_) { /* */ }
|
|
77
|
+
}
|
|
78
|
+
if (!sizes) {
|
|
79
|
+
sizes = (config.defaultSizes || [50, 50]).slice()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Apply initial sizes
|
|
83
|
+
function applySizes() {
|
|
84
|
+
for (var i = 0; i < sizes.length; i++) {
|
|
85
|
+
layout.style.setProperty('--rfr-pane-' + i + '-size', sizes[i] + '%')
|
|
86
|
+
}
|
|
87
|
+
// Update pane flex-basis
|
|
88
|
+
var panes = layout.querySelectorAll('[data-rfr-pane-index]')
|
|
89
|
+
panes.forEach(function (pane) {
|
|
90
|
+
var idx = parseInt(pane.getAttribute('data-rfr-pane-index'), 10)
|
|
91
|
+
if (!isNaN(idx) && sizes[idx] !== undefined) {
|
|
92
|
+
pane.style.flexBasis = sizes[idx] + '%'
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
applySizes()
|
|
98
|
+
|
|
99
|
+
function clamp(val, min, max) {
|
|
100
|
+
return Math.min(Math.max(val, min), max)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function getMin(i) { return minSizes[i] || 0 }
|
|
104
|
+
function getMax(i) { return maxSizes[i] || 100 }
|
|
105
|
+
|
|
106
|
+
// Handle divider drag
|
|
107
|
+
var resizingIndex = null
|
|
108
|
+
var startPos = 0
|
|
109
|
+
var containerSize = 0
|
|
110
|
+
var sizesBeforeResize = []
|
|
111
|
+
|
|
112
|
+
layout.addEventListener('pointerdown', function (e) {
|
|
113
|
+
var divider = e.target.closest('[data-rfr-divider-index]')
|
|
114
|
+
if (!divider) return
|
|
115
|
+
|
|
116
|
+
e.preventDefault()
|
|
117
|
+
divider.setPointerCapture(e.pointerId)
|
|
118
|
+
|
|
119
|
+
resizingIndex = parseInt(divider.getAttribute('data-rfr-divider-index'), 10)
|
|
120
|
+
sizesBeforeResize = sizes.slice()
|
|
121
|
+
|
|
122
|
+
var rect = layout.getBoundingClientRect()
|
|
123
|
+
containerSize = orientation === 'horizontal' ? rect.width : rect.height
|
|
124
|
+
startPos = orientation === 'horizontal' ? e.clientX : e.clientY
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
layout.addEventListener('pointermove', function (e) {
|
|
128
|
+
if (resizingIndex === null) return
|
|
129
|
+
|
|
130
|
+
var currentPos = orientation === 'horizontal' ? e.clientX : e.clientY
|
|
131
|
+
var pxDelta = currentPos - startPos
|
|
132
|
+
var pctDelta = (pxDelta / containerSize) * 100
|
|
133
|
+
|
|
134
|
+
var i = resizingIndex
|
|
135
|
+
var j = i + 1
|
|
136
|
+
if (j >= sizes.length) return
|
|
137
|
+
|
|
138
|
+
var total = sizesBeforeResize[i] + sizesBeforeResize[j]
|
|
139
|
+
var newI = clamp(sizesBeforeResize[i] + pctDelta, getMin(i), getMax(i))
|
|
140
|
+
var newJ = clamp(total - newI, getMin(j), getMax(j))
|
|
141
|
+
newI = total - newJ
|
|
142
|
+
|
|
143
|
+
sizes[i] = newI
|
|
144
|
+
sizes[j] = newJ
|
|
145
|
+
applySizes()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
layout.addEventListener('pointerup', function (e) {
|
|
149
|
+
var divider = e.target.closest('[data-rfr-divider-index]')
|
|
150
|
+
if (divider && resizingIndex !== null) {
|
|
151
|
+
divider.releasePointerCapture(e.pointerId)
|
|
152
|
+
}
|
|
153
|
+
resizingIndex = null
|
|
154
|
+
sizesBeforeResize = []
|
|
155
|
+
|
|
156
|
+
if (persistKey) {
|
|
157
|
+
try { localStorage.setItem(persistKey, JSON.stringify(sizes)) } catch (_) { /* */ }
|
|
158
|
+
}
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
})()
|
|
162
|
+
</script>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* ResizablePane — a single pane whose size is driven by CSS custom properties.
|
|
4
|
+
*/
|
|
5
|
+
import { resizablePaneVariants } from '@refraction-ui/resizable-layout'
|
|
6
|
+
import { cn } from '../shared/index.ts'
|
|
7
|
+
import type { Orientation } from '../shared/index.ts'
|
|
8
|
+
|
|
9
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
10
|
+
/** Zero-based index of this pane */
|
|
11
|
+
index: number
|
|
12
|
+
/** Orientation inherited from parent (default 'horizontal') */
|
|
13
|
+
orientation?: Orientation
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { index, orientation = 'horizontal', class: className, style, ...props } = Astro.props
|
|
17
|
+
|
|
18
|
+
const paneStyle = `flex-basis:var(--rfr-pane-${index}-size, 50%);flex-grow:0;flex-shrink:0;${typeof style === 'string' ? style : ''}`
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
<div
|
|
22
|
+
class={cn(resizablePaneVariants({ orientation }), className)}
|
|
23
|
+
style={paneStyle}
|
|
24
|
+
data-rfr-pane-index={index}
|
|
25
|
+
{...props}
|
|
26
|
+
>
|
|
27
|
+
<slot />
|
|
28
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { default as ResizableLayout } from './ResizableLayout.astro'
|
|
2
|
+
export { default as ResizablePane } from './ResizablePane.astro'
|
|
3
|
+
export { default as ResizableDivider } from './ResizableDivider.astro'
|
|
4
|
+
|
|
5
|
+
// Re-export core types and utilities
|
|
6
|
+
export {
|
|
7
|
+
createResizableLayout,
|
|
8
|
+
resizableLayoutVariants,
|
|
9
|
+
resizableDividerVariants,
|
|
10
|
+
resizablePaneVariants,
|
|
11
|
+
} from '@refraction-ui/resizable-layout'
|