@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,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* AppShellMobileNav — fixed bottom navigation bar, visible only on mobile.
|
|
4
|
+
*/
|
|
5
|
+
import { cn } from '../shared/index.ts'
|
|
6
|
+
|
|
7
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {}
|
|
8
|
+
|
|
9
|
+
const { class: className, ...props } = Astro.props
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<nav
|
|
13
|
+
role="navigation"
|
|
14
|
+
aria-label="Mobile navigation"
|
|
15
|
+
class={cn(
|
|
16
|
+
'fixed bottom-0 left-0 right-0 z-30',
|
|
17
|
+
'flex items-center justify-around',
|
|
18
|
+
'border-t bg-background',
|
|
19
|
+
'h-14',
|
|
20
|
+
className,
|
|
21
|
+
)}
|
|
22
|
+
style="display:none;"
|
|
23
|
+
data-rfr-shell-mobile-nav
|
|
24
|
+
{...props}
|
|
25
|
+
>
|
|
26
|
+
<slot />
|
|
27
|
+
</nav>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* AppShellOverlay — backdrop overlay shown when mobile sidebar is open.
|
|
4
|
+
*/
|
|
5
|
+
import { cn } from '../shared/index.ts'
|
|
6
|
+
|
|
7
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {}
|
|
8
|
+
|
|
9
|
+
const { class: className, ...props } = Astro.props
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<div
|
|
13
|
+
aria-hidden="true"
|
|
14
|
+
class={cn(
|
|
15
|
+
'fixed inset-0 z-30 bg-black/50 transition-opacity',
|
|
16
|
+
className,
|
|
17
|
+
)}
|
|
18
|
+
style="display:none;"
|
|
19
|
+
data-rfr-shell-overlay
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* AppShellSidebar — sidebar navigation area within the AppShell.
|
|
4
|
+
*/
|
|
5
|
+
import { cn } from '../shared/index.ts'
|
|
6
|
+
|
|
7
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {}
|
|
8
|
+
|
|
9
|
+
const { class: className, ...props } = Astro.props
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<aside
|
|
13
|
+
role="navigation"
|
|
14
|
+
aria-label="Sidebar"
|
|
15
|
+
class={cn(
|
|
16
|
+
'flex flex-col shrink-0 overflow-y-auto overflow-x-hidden',
|
|
17
|
+
'bg-background border-r',
|
|
18
|
+
'transition-[width,transform] duration-200 ease-in-out',
|
|
19
|
+
'h-full relative w-[var(--shell-sidebar-width)]',
|
|
20
|
+
className,
|
|
21
|
+
)}
|
|
22
|
+
data-rfr-shell-sidebar
|
|
23
|
+
{...props}
|
|
24
|
+
>
|
|
25
|
+
<slot />
|
|
26
|
+
</aside>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* AuthShell — centered layout for authentication pages (sign-in, sign-up, etc.).
|
|
4
|
+
*/
|
|
5
|
+
import { createAuthShell, type AuthShellConfig } from '@refraction-ui/app-shell'
|
|
6
|
+
import { cn } from '../shared/index.ts'
|
|
7
|
+
|
|
8
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
9
|
+
/** Card max-width preset (default 'sm') */
|
|
10
|
+
maxWidth?: 'xs' | 'sm' | 'md'
|
|
11
|
+
/** Card position (default 'center') */
|
|
12
|
+
position?: 'center' | 'left'
|
|
13
|
+
/** Show decorative background (default true) */
|
|
14
|
+
showBackground?: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { maxWidth, position, showBackground, class: className, ...props } = Astro.props
|
|
18
|
+
|
|
19
|
+
const config: AuthShellConfig = { maxWidth, position, showBackground }
|
|
20
|
+
const api = createAuthShell(config)
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
<div
|
|
24
|
+
role="main"
|
|
25
|
+
aria-label="Authentication"
|
|
26
|
+
class={cn(api.containerClasses, className)}
|
|
27
|
+
data-rfr-auth-shell
|
|
28
|
+
{...props}
|
|
29
|
+
>
|
|
30
|
+
<slot />
|
|
31
|
+
</div>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* AuthShellCard — card container within AuthShell for form content.
|
|
4
|
+
*/
|
|
5
|
+
import { createAuthShell, type AuthShellConfig } from '@refraction-ui/app-shell'
|
|
6
|
+
import { cn } from '../shared/index.ts'
|
|
7
|
+
|
|
8
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
9
|
+
/** Card max-width preset (default 'sm') */
|
|
10
|
+
maxWidth?: 'xs' | 'sm' | 'md'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { maxWidth, class: className, ...props } = Astro.props
|
|
14
|
+
|
|
15
|
+
const api = createAuthShell({ maxWidth })
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<div
|
|
19
|
+
class={cn(api.cardClasses, className)}
|
|
20
|
+
data-rfr-auth-card
|
|
21
|
+
{...props}
|
|
22
|
+
>
|
|
23
|
+
<slot />
|
|
24
|
+
</div>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* PageShell — root layout for marketing/landing pages with nav, sections, footer.
|
|
4
|
+
*/
|
|
5
|
+
import { createPageShell, type PageShellConfig } from '@refraction-ui/app-shell'
|
|
6
|
+
import { cn } from '../shared/index.ts'
|
|
7
|
+
|
|
8
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
9
|
+
/** Max width for contained sections (default '80rem') */
|
|
10
|
+
maxWidth?: string
|
|
11
|
+
/** Navigation bar height (default '4rem') */
|
|
12
|
+
navHeight?: string
|
|
13
|
+
/** Whether nav is transparent (default false) */
|
|
14
|
+
navTransparent?: boolean
|
|
15
|
+
/** Whether nav is sticky (default true) */
|
|
16
|
+
navSticky?: boolean
|
|
17
|
+
/** Number of footer columns (default 4) */
|
|
18
|
+
footerColumns?: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const { maxWidth, navHeight, navTransparent, navSticky, footerColumns, class: className, ...props } = Astro.props
|
|
22
|
+
|
|
23
|
+
const config: PageShellConfig = { maxWidth, navHeight, navTransparent, navSticky, footerColumns }
|
|
24
|
+
const api = createPageShell(config)
|
|
25
|
+
const cssVars = api.getCSSVariables()
|
|
26
|
+
const styleStr = Object.entries(cssVars).map(([k, v]) => `${k}:${v}`).join(';')
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
<div
|
|
30
|
+
class={cn('flex min-h-screen flex-col', className)}
|
|
31
|
+
style={styleStr}
|
|
32
|
+
data-rfr-page-shell
|
|
33
|
+
{...props}
|
|
34
|
+
>
|
|
35
|
+
<slot />
|
|
36
|
+
</div>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* PageShellFooter — footer area within PageShell.
|
|
4
|
+
*/
|
|
5
|
+
import { cn } from '../shared/index.ts'
|
|
6
|
+
|
|
7
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
8
|
+
/** Number of grid columns (default 4) */
|
|
9
|
+
columns?: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { columns = 4, class: className, ...props } = Astro.props
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<footer
|
|
16
|
+
role="contentinfo"
|
|
17
|
+
class={cn(
|
|
18
|
+
'border-t bg-muted py-12 px-4 sm:px-6 lg:px-8',
|
|
19
|
+
className,
|
|
20
|
+
)}
|
|
21
|
+
data-rfr-page-footer
|
|
22
|
+
{...props}
|
|
23
|
+
>
|
|
24
|
+
<div
|
|
25
|
+
class="mx-auto max-w-[var(--page-max-width)] grid gap-8"
|
|
26
|
+
style={`grid-template-columns: repeat(${columns}, minmax(0, 1fr));`}
|
|
27
|
+
>
|
|
28
|
+
<slot />
|
|
29
|
+
</div>
|
|
30
|
+
</footer>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* PageShellNav — navigation bar within PageShell.
|
|
4
|
+
*/
|
|
5
|
+
import { cn } from '../shared/index.ts'
|
|
6
|
+
|
|
7
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
8
|
+
/** Whether nav is sticky (default true) */
|
|
9
|
+
sticky?: boolean
|
|
10
|
+
/** Whether nav is transparent (default false) */
|
|
11
|
+
transparent?: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const { sticky = true, transparent = false, class: className, ...props } = Astro.props
|
|
15
|
+
|
|
16
|
+
const stickyClass = sticky ? 'sticky top-0 z-40' : ''
|
|
17
|
+
const transparentClass = transparent ? 'bg-transparent' : 'bg-background border-b'
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<nav
|
|
21
|
+
role="navigation"
|
|
22
|
+
aria-label="Main navigation"
|
|
23
|
+
class={cn(
|
|
24
|
+
'flex items-center h-[var(--page-nav-height)] px-4 sm:px-6 lg:px-8',
|
|
25
|
+
stickyClass,
|
|
26
|
+
transparentClass,
|
|
27
|
+
className,
|
|
28
|
+
)}
|
|
29
|
+
data-rfr-page-nav
|
|
30
|
+
{...props}
|
|
31
|
+
>
|
|
32
|
+
<slot />
|
|
33
|
+
</nav>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* PageShellSection — a content section within PageShell.
|
|
4
|
+
*/
|
|
5
|
+
import { createPageShell } from '@refraction-ui/app-shell'
|
|
6
|
+
import { cn } from '../shared/index.ts'
|
|
7
|
+
|
|
8
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
9
|
+
/** Whether section spans edge-to-edge (default false) */
|
|
10
|
+
fullWidth?: boolean
|
|
11
|
+
/** Override max-width for this section */
|
|
12
|
+
maxWidth?: string
|
|
13
|
+
/** Whether section has padding (default true) */
|
|
14
|
+
padding?: boolean
|
|
15
|
+
/** Background variant */
|
|
16
|
+
background?: 'default' | 'muted' | 'primary' | 'none'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const { fullWidth, maxWidth, padding, background, class: className, ...props } = Astro.props
|
|
20
|
+
|
|
21
|
+
// Use the default page shell to get section classes
|
|
22
|
+
const api = createPageShell()
|
|
23
|
+
const sectionClasses = api.getSectionClasses({ fullWidth, maxWidth, padding, background })
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
<section
|
|
27
|
+
class={cn('py-12', sectionClasses, className)}
|
|
28
|
+
data-rfr-page-section
|
|
29
|
+
{...props}
|
|
30
|
+
>
|
|
31
|
+
<slot />
|
|
32
|
+
</section>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// AppShell compound components
|
|
2
|
+
export { default as AppShell } from './AppShell.astro'
|
|
3
|
+
export { default as AppShellSidebar } from './AppShellSidebar.astro'
|
|
4
|
+
export { default as AppShellHeader } from './AppShellHeader.astro'
|
|
5
|
+
export { default as AppShellMain } from './AppShellMain.astro'
|
|
6
|
+
export { default as AppShellContent } from './AppShellContent.astro'
|
|
7
|
+
export { default as AppShellMobileNav } from './AppShellMobileNav.astro'
|
|
8
|
+
export { default as AppShellOverlay } from './AppShellOverlay.astro'
|
|
9
|
+
|
|
10
|
+
// PageShell compound components
|
|
11
|
+
export { default as PageShell } from './PageShell.astro'
|
|
12
|
+
export { default as PageShellNav } from './PageShellNav.astro'
|
|
13
|
+
export { default as PageShellSection } from './PageShellSection.astro'
|
|
14
|
+
export { default as PageShellFooter } from './PageShellFooter.astro'
|
|
15
|
+
|
|
16
|
+
// AuthShell compound components
|
|
17
|
+
export { default as AuthShell } from './AuthShell.astro'
|
|
18
|
+
export { default as AuthShellCard } from './AuthShellCard.astro'
|
|
19
|
+
|
|
20
|
+
// Re-export core types
|
|
21
|
+
export { createAppShell, createPageShell, createAuthShell } from '@refraction-ui/app-shell'
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* AuthGuard — server-side component that conditionally renders children
|
|
4
|
+
* based on auth state. Since Astro is primarily server-rendered, this
|
|
5
|
+
* component checks auth at SSR time and includes a client-side script
|
|
6
|
+
* for dynamic scenarios.
|
|
7
|
+
*
|
|
8
|
+
* Slots:
|
|
9
|
+
* - default: content shown when authenticated
|
|
10
|
+
* - fallback: shown while loading / when unauthenticated (no roles check)
|
|
11
|
+
* - unauthorized: shown when user lacks required roles
|
|
12
|
+
*/
|
|
13
|
+
import { cn } from '../shared/index.ts'
|
|
14
|
+
|
|
15
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
16
|
+
/** Roles required to view the content */
|
|
17
|
+
roles?: string[]
|
|
18
|
+
/** URL to redirect to when unauthenticated (client-side) */
|
|
19
|
+
redirectTo?: string
|
|
20
|
+
/** Provider type for auto-detection */
|
|
21
|
+
provider?: 'firebase' | 'supabase' | 'mock' | 'none'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { roles, redirectTo, provider, class: className, ...props } = Astro.props
|
|
25
|
+
|
|
26
|
+
const rolesJSON = JSON.stringify(roles ?? [])
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
<div
|
|
30
|
+
class={cn('rfr-auth-guard', className)}
|
|
31
|
+
data-rfr-auth-guard
|
|
32
|
+
data-rfr-auth-roles={rolesJSON}
|
|
33
|
+
data-rfr-auth-redirect={redirectTo}
|
|
34
|
+
data-rfr-auth-provider={provider}
|
|
35
|
+
{...props}
|
|
36
|
+
>
|
|
37
|
+
<!-- Loading / fallback state (hidden once auth resolves) -->
|
|
38
|
+
<div data-rfr-auth-state="loading">
|
|
39
|
+
<slot name="fallback">
|
|
40
|
+
<div class="flex items-center justify-center p-8" aria-busy="true" aria-label="Checking authentication">
|
|
41
|
+
<svg
|
|
42
|
+
class="animate-spin h-6 w-6"
|
|
43
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
44
|
+
fill="none"
|
|
45
|
+
viewBox="0 0 24 24"
|
|
46
|
+
aria-hidden="true"
|
|
47
|
+
>
|
|
48
|
+
<circle
|
|
49
|
+
class="opacity-25"
|
|
50
|
+
cx="12"
|
|
51
|
+
cy="12"
|
|
52
|
+
r="10"
|
|
53
|
+
stroke="currentColor"
|
|
54
|
+
stroke-width="4"
|
|
55
|
+
/>
|
|
56
|
+
<path
|
|
57
|
+
class="opacity-75"
|
|
58
|
+
fill="currentColor"
|
|
59
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
60
|
+
/>
|
|
61
|
+
</svg>
|
|
62
|
+
</div>
|
|
63
|
+
</slot>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<!-- Authenticated content (hidden until auth confirmed) -->
|
|
67
|
+
<div data-rfr-auth-state="authenticated" style="display:none;">
|
|
68
|
+
<slot />
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<!-- Unauthorized (hidden unless roles check fails) -->
|
|
72
|
+
<div data-rfr-auth-state="unauthorized" style="display:none;">
|
|
73
|
+
<slot name="unauthorized">
|
|
74
|
+
<div class="flex items-center justify-center p-8 text-muted-foreground" role="alert">
|
|
75
|
+
<p>You do not have permission to view this content.</p>
|
|
76
|
+
</div>
|
|
77
|
+
</slot>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<script is:inline>
|
|
82
|
+
;(function () {
|
|
83
|
+
document.querySelectorAll('[data-rfr-auth-guard]').forEach(function (guard) {
|
|
84
|
+
var redirect = guard.getAttribute('data-rfr-auth-redirect')
|
|
85
|
+
var roles = []
|
|
86
|
+
try {
|
|
87
|
+
roles = JSON.parse(guard.getAttribute('data-rfr-auth-roles') || '[]')
|
|
88
|
+
} catch (_) { /* */ }
|
|
89
|
+
|
|
90
|
+
var loadingEl = guard.querySelector('[data-rfr-auth-state="loading"]')
|
|
91
|
+
var authedEl = guard.querySelector('[data-rfr-auth-state="authenticated"]')
|
|
92
|
+
var unauthEl = guard.querySelector('[data-rfr-auth-state="unauthorized"]')
|
|
93
|
+
|
|
94
|
+
function showState(state) {
|
|
95
|
+
if (loadingEl) loadingEl.style.display = state === 'loading' ? '' : 'none'
|
|
96
|
+
if (authedEl) authedEl.style.display = state === 'authenticated' ? '' : 'none'
|
|
97
|
+
if (unauthEl) unauthEl.style.display = state === 'unauthorized' ? '' : 'none'
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function hasAnyRole(user, requiredRoles) {
|
|
101
|
+
if (!requiredRoles || requiredRoles.length === 0) return true
|
|
102
|
+
if (!user || !user.roles) return false
|
|
103
|
+
return requiredRoles.some(function (r) { return user.roles.indexOf(r) !== -1 })
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function handleAuth(user) {
|
|
107
|
+
if (!user) {
|
|
108
|
+
if (redirect) {
|
|
109
|
+
window.location.href = redirect
|
|
110
|
+
} else {
|
|
111
|
+
showState('loading')
|
|
112
|
+
}
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
if (roles.length > 0 && !hasAnyRole(user, roles)) {
|
|
116
|
+
showState('unauthorized')
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
showState('authenticated')
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Listen for custom auth events dispatched by the auth system
|
|
123
|
+
window.addEventListener('rfr:auth:state', function (e) {
|
|
124
|
+
handleAuth(e.detail && e.detail.user)
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
// Check if auth state is already available
|
|
128
|
+
if (window.__rfr_auth && window.__rfr_auth._ready) {
|
|
129
|
+
handleAuth(window.__rfr_auth.user)
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
})()
|
|
133
|
+
</script>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { createAvatar, avatarVariants, type AvatarSize } from '@refraction-ui/avatar'
|
|
3
|
+
import { cn } from '../shared/index.ts'
|
|
4
|
+
|
|
5
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
6
|
+
size?: AvatarSize
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { size = 'md', class: className, ...props } = Astro.props
|
|
10
|
+
const api = createAvatar({ size })
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
<span
|
|
14
|
+
class={cn(avatarVariants({ size }), className)}
|
|
15
|
+
data-rfr-avatar
|
|
16
|
+
data-rfr-avatar-size={size}
|
|
17
|
+
{...api.ariaProps}
|
|
18
|
+
{...api.dataAttributes}
|
|
19
|
+
{...props}
|
|
20
|
+
>
|
|
21
|
+
<slot />
|
|
22
|
+
</span>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { avatarFallbackVariants, type AvatarSize } from '@refraction-ui/avatar'
|
|
3
|
+
import { cn } from '../shared/index.ts'
|
|
4
|
+
|
|
5
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
6
|
+
size?: AvatarSize
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { size = 'md', class: className, ...props } = Astro.props
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<span
|
|
13
|
+
class={cn(avatarFallbackVariants({ size }), className)}
|
|
14
|
+
data-rfr-avatar-fallback
|
|
15
|
+
{...props}
|
|
16
|
+
>
|
|
17
|
+
<slot />
|
|
18
|
+
</span>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { avatarImageVariants } from '@refraction-ui/avatar'
|
|
3
|
+
import { cn } from '../shared/index.ts'
|
|
4
|
+
|
|
5
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
6
|
+
src?: string
|
|
7
|
+
alt?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { src, alt = '', class: className, ...props } = Astro.props
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
<img
|
|
14
|
+
class={cn(avatarImageVariants(), className)}
|
|
15
|
+
src={src}
|
|
16
|
+
alt={alt}
|
|
17
|
+
data-rfr-avatar-image
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
|
|
21
|
+
<script>
|
|
22
|
+
document.querySelectorAll('[data-rfr-avatar-image]').forEach((img) => {
|
|
23
|
+
const imgEl = img as HTMLImageElement
|
|
24
|
+
const parent = imgEl.closest('[data-rfr-avatar]')
|
|
25
|
+
const fallback = parent?.querySelector('[data-rfr-avatar-fallback]') as HTMLElement | null
|
|
26
|
+
|
|
27
|
+
// Handle already-loaded images (cached)
|
|
28
|
+
if (imgEl.complete && imgEl.naturalWidth > 0) {
|
|
29
|
+
imgEl.style.display = ''
|
|
30
|
+
if (fallback) fallback.style.display = 'none'
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Hide image until loaded
|
|
35
|
+
imgEl.style.display = 'none'
|
|
36
|
+
if (fallback) fallback.style.display = ''
|
|
37
|
+
|
|
38
|
+
imgEl.addEventListener('load', () => {
|
|
39
|
+
imgEl.style.display = ''
|
|
40
|
+
if (fallback) fallback.style.display = 'none'
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
imgEl.addEventListener('error', () => {
|
|
44
|
+
imgEl.style.display = 'none'
|
|
45
|
+
if (fallback) fallback.style.display = ''
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
</script>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
import {
|
|
3
|
+
createAvatarGroup,
|
|
4
|
+
avatarGroupStyles,
|
|
5
|
+
avatarVariants,
|
|
6
|
+
avatarOverflowBadgeVariants,
|
|
7
|
+
avatarImageStyles,
|
|
8
|
+
avatarPresenceDotVariants,
|
|
9
|
+
type AvatarUser,
|
|
10
|
+
type AvatarSize,
|
|
11
|
+
} from '@refraction-ui/avatar-group'
|
|
12
|
+
import { cn } from '../shared/index.ts'
|
|
13
|
+
|
|
14
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
15
|
+
users: AvatarUser[]
|
|
16
|
+
max?: number
|
|
17
|
+
size?: AvatarSize
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { users, max, size = 'md', class: className, ...props } = Astro.props
|
|
21
|
+
const api = createAvatarGroup({ users, max, size })
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
<div class={cn(avatarGroupStyles, className)} {...api.ariaProps} {...props}>
|
|
25
|
+
{api.visibleUsers.map((user) => (
|
|
26
|
+
<div class={avatarVariants({ size })} {...api.getAvatarAriaProps(user)}>
|
|
27
|
+
{user.src ? (
|
|
28
|
+
<img src={user.src} alt={user.name} class={avatarImageStyles} />
|
|
29
|
+
) : (
|
|
30
|
+
<span>{api.getInitials(user.name)}</span>
|
|
31
|
+
)}
|
|
32
|
+
{user.status && (
|
|
33
|
+
<span class={avatarPresenceDotVariants({ size, status: user.status })} />
|
|
34
|
+
)}
|
|
35
|
+
</div>
|
|
36
|
+
))}
|
|
37
|
+
{api.overflowCount > 0 && (
|
|
38
|
+
<div class={avatarOverflowBadgeVariants({ size })} {...api.overflowBadgeProps}>
|
|
39
|
+
+{api.overflowCount}
|
|
40
|
+
</div>
|
|
41
|
+
)}
|
|
42
|
+
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
import {
|
|
3
|
+
createBadge,
|
|
4
|
+
badgeVariants,
|
|
5
|
+
type BadgeVariant,
|
|
6
|
+
type BadgeSize,
|
|
7
|
+
} from '@refraction-ui/badge'
|
|
8
|
+
import { cn } from '../shared/index.ts'
|
|
9
|
+
|
|
10
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
11
|
+
variant?: BadgeVariant
|
|
12
|
+
size?: BadgeSize
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { variant, size, class: className, ...props } = Astro.props
|
|
16
|
+
const api = createBadge({ variant, size })
|
|
17
|
+
const classes = cn(badgeVariants({ variant, size }), className)
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<div
|
|
21
|
+
class={classes}
|
|
22
|
+
{...api.ariaProps}
|
|
23
|
+
{...api.dataAttributes}
|
|
24
|
+
{...props}
|
|
25
|
+
>
|
|
26
|
+
<slot />
|
|
27
|
+
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
import {
|
|
3
|
+
createBottomNav,
|
|
4
|
+
bottomNavVariants,
|
|
5
|
+
bottomNavTabVariants,
|
|
6
|
+
type NavTab,
|
|
7
|
+
} from '@refraction-ui/bottom-nav'
|
|
8
|
+
import { cn } from '../shared/index.ts'
|
|
9
|
+
|
|
10
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
11
|
+
tabs?: NavTab[]
|
|
12
|
+
currentPath?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { tabs = [], currentPath, class: className, ...props } = Astro.props
|
|
16
|
+
const api = createBottomNav({ tabs, currentPath })
|
|
17
|
+
const classes = cn(bottomNavVariants(), className)
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<nav class={classes} {...api.ariaProps} {...props}>
|
|
21
|
+
<div class="flex">
|
|
22
|
+
{tabs.map((tab) => {
|
|
23
|
+
const active = api.isActive(tab.href)
|
|
24
|
+
return (
|
|
25
|
+
<a
|
|
26
|
+
href={tab.href}
|
|
27
|
+
class={bottomNavTabVariants({ active: active ? 'true' : 'false' })}
|
|
28
|
+
{...api.tabAriaProps(tab.href)}
|
|
29
|
+
>
|
|
30
|
+
{tab.icon && (
|
|
31
|
+
<span aria-hidden="true">
|
|
32
|
+
{active && tab.activeIcon ? tab.activeIcon : tab.icon}
|
|
33
|
+
</span>
|
|
34
|
+
)}
|
|
35
|
+
<span>{tab.label}</span>
|
|
36
|
+
</a>
|
|
37
|
+
)
|
|
38
|
+
})}
|
|
39
|
+
</div>
|
|
40
|
+
</nav>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|