@classic-homes/theme-svelte 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/README.md +305 -0
  2. package/dist/lib/components/Alert.svelte +51 -0
  3. package/dist/lib/components/Alert.svelte.d.ts +9 -0
  4. package/dist/lib/components/AlertDescription.svelte +16 -0
  5. package/dist/lib/components/AlertDescription.svelte.d.ts +9 -0
  6. package/dist/lib/components/AlertDialog.svelte +136 -0
  7. package/dist/lib/components/AlertDialog.svelte.d.ts +79 -0
  8. package/dist/lib/components/AlertTitle.svelte +16 -0
  9. package/dist/lib/components/AlertTitle.svelte.d.ts +9 -0
  10. package/dist/lib/components/Avatar.svelte +56 -0
  11. package/dist/lib/components/Avatar.svelte.d.ts +26 -0
  12. package/dist/lib/components/AvatarFallback.svelte +31 -0
  13. package/dist/lib/components/AvatarFallback.svelte.d.ts +17 -0
  14. package/dist/lib/components/AvatarImage.svelte +29 -0
  15. package/dist/lib/components/AvatarImage.svelte.d.ts +12 -0
  16. package/dist/lib/components/Badge.svelte +73 -0
  17. package/dist/lib/components/Badge.svelte.d.ts +11 -0
  18. package/dist/lib/components/Button.svelte +130 -0
  19. package/dist/lib/components/Button.svelte.d.ts +17 -0
  20. package/dist/lib/components/Card.svelte +58 -0
  21. package/dist/lib/components/Card.svelte.d.ts +26 -0
  22. package/dist/lib/components/CardContent.svelte +16 -0
  23. package/dist/lib/components/CardContent.svelte.d.ts +9 -0
  24. package/dist/lib/components/CardDescription.svelte +16 -0
  25. package/dist/lib/components/CardDescription.svelte.d.ts +9 -0
  26. package/dist/lib/components/CardFooter.svelte +16 -0
  27. package/dist/lib/components/CardFooter.svelte.d.ts +9 -0
  28. package/dist/lib/components/CardHeader.svelte +16 -0
  29. package/dist/lib/components/CardHeader.svelte.d.ts +9 -0
  30. package/dist/lib/components/CardTitle.svelte +16 -0
  31. package/dist/lib/components/CardTitle.svelte.d.ts +9 -0
  32. package/dist/lib/components/Checkbox.svelte +65 -0
  33. package/dist/lib/components/Checkbox.svelte.d.ts +14 -0
  34. package/dist/lib/components/DataTable.svelte +334 -0
  35. package/dist/lib/components/DataTable.svelte.d.ts +103 -0
  36. package/dist/lib/components/Dialog.svelte +111 -0
  37. package/dist/lib/components/Dialog.svelte.d.ts +22 -0
  38. package/dist/lib/components/DropdownMenu.svelte +135 -0
  39. package/dist/lib/components/DropdownMenu.svelte.d.ts +33 -0
  40. package/dist/lib/components/FileUpload.svelte +448 -0
  41. package/dist/lib/components/FileUpload.svelte.d.ts +42 -0
  42. package/dist/lib/components/FormField.svelte +134 -0
  43. package/dist/lib/components/FormField.svelte.d.ts +37 -0
  44. package/dist/lib/components/Input.svelte +61 -0
  45. package/dist/lib/components/Input.svelte.d.ts +19 -0
  46. package/dist/lib/components/Label.svelte +33 -0
  47. package/dist/lib/components/Label.svelte.d.ts +11 -0
  48. package/dist/lib/components/LoadingLogo.svelte +124 -0
  49. package/dist/lib/components/LoadingLogo.svelte.d.ts +16 -0
  50. package/dist/lib/components/LogoMain.svelte +237 -0
  51. package/dist/lib/components/LogoMain.svelte.d.ts +20 -0
  52. package/dist/lib/components/PageHeader.svelte +90 -0
  53. package/dist/lib/components/PageHeader.svelte.d.ts +28 -0
  54. package/dist/lib/components/Section.svelte +44 -0
  55. package/dist/lib/components/Section.svelte.d.ts +28 -0
  56. package/dist/lib/components/Select.svelte +174 -0
  57. package/dist/lib/components/Select.svelte.d.ts +32 -0
  58. package/dist/lib/components/Separator.svelte +29 -0
  59. package/dist/lib/components/Separator.svelte.d.ts +9 -0
  60. package/dist/lib/components/Skeleton.svelte +35 -0
  61. package/dist/lib/components/Skeleton.svelte.d.ts +7 -0
  62. package/dist/lib/components/Spinner.svelte +50 -0
  63. package/dist/lib/components/Spinner.svelte.d.ts +8 -0
  64. package/dist/lib/components/Switch.svelte +56 -0
  65. package/dist/lib/components/Switch.svelte.d.ts +14 -0
  66. package/dist/lib/components/TabPanel.svelte +44 -0
  67. package/dist/lib/components/TabPanel.svelte.d.ts +12 -0
  68. package/dist/lib/components/Tabs.svelte +125 -0
  69. package/dist/lib/components/Tabs.svelte.d.ts +19 -0
  70. package/dist/lib/components/Textarea.svelte +54 -0
  71. package/dist/lib/components/Textarea.svelte.d.ts +16 -0
  72. package/dist/lib/components/Toast.svelte +116 -0
  73. package/dist/lib/components/Toast.svelte.d.ts +12 -0
  74. package/dist/lib/components/ToastContainer.svelte +56 -0
  75. package/dist/lib/components/ToastContainer.svelte.d.ts +8 -0
  76. package/dist/lib/components/Tooltip.svelte +55 -0
  77. package/dist/lib/components/Tooltip.svelte.d.ts +18 -0
  78. package/dist/lib/components/layout/AppShell.svelte +82 -0
  79. package/dist/lib/components/layout/AppShell.svelte.d.ts +44 -0
  80. package/dist/lib/components/layout/DashboardLayout.svelte +248 -0
  81. package/dist/lib/components/layout/DashboardLayout.svelte.d.ts +62 -0
  82. package/dist/lib/components/layout/Footer.svelte +130 -0
  83. package/dist/lib/components/layout/Footer.svelte.d.ts +32 -0
  84. package/dist/lib/components/layout/FormPageLayout.svelte +92 -0
  85. package/dist/lib/components/layout/FormPageLayout.svelte.d.ts +33 -0
  86. package/dist/lib/components/layout/Header.svelte +94 -0
  87. package/dist/lib/components/layout/Header.svelte.d.ts +30 -0
  88. package/dist/lib/components/layout/PublicLayout.svelte +180 -0
  89. package/dist/lib/components/layout/PublicLayout.svelte.d.ts +39 -0
  90. package/dist/lib/components/layout/QuickLinks.svelte +112 -0
  91. package/dist/lib/components/layout/QuickLinks.svelte.d.ts +27 -0
  92. package/dist/lib/components/layout/Sidebar.svelte +243 -0
  93. package/dist/lib/components/layout/Sidebar.svelte.d.ts +48 -0
  94. package/dist/lib/composables/index.d.ts +8 -0
  95. package/dist/lib/composables/index.js +10 -0
  96. package/dist/lib/composables/useAsync.svelte.d.ts +102 -0
  97. package/dist/lib/composables/useAsync.svelte.js +210 -0
  98. package/dist/lib/composables/useForm.svelte.d.ts +123 -0
  99. package/dist/lib/composables/useForm.svelte.js +245 -0
  100. package/dist/lib/index.d.ts +65 -0
  101. package/dist/lib/index.js +83 -0
  102. package/dist/lib/performance.d.ts +79 -0
  103. package/dist/lib/performance.js +170 -0
  104. package/dist/lib/schemas/auth.d.ts +410 -0
  105. package/dist/lib/schemas/auth.js +216 -0
  106. package/dist/lib/schemas/common.d.ts +267 -0
  107. package/dist/lib/schemas/common.js +268 -0
  108. package/dist/lib/schemas/index.d.ts +24 -0
  109. package/dist/lib/schemas/index.js +32 -0
  110. package/dist/lib/stores/sidebar.svelte.d.ts +25 -0
  111. package/dist/lib/stores/sidebar.svelte.js +38 -0
  112. package/dist/lib/stores/theme.svelte.d.ts +72 -0
  113. package/dist/lib/stores/theme.svelte.js +150 -0
  114. package/dist/lib/stores/toast.svelte.d.ts +62 -0
  115. package/dist/lib/stores/toast.svelte.js +93 -0
  116. package/dist/lib/types/components.d.ts +85 -0
  117. package/dist/lib/types/components.js +7 -0
  118. package/dist/lib/types/layout.d.ts +258 -0
  119. package/dist/lib/types/layout.js +7 -0
  120. package/dist/lib/utils.d.ts +6 -0
  121. package/dist/lib/utils.js +9 -0
  122. package/dist/lib/validation.d.ts +101 -0
  123. package/dist/lib/validation.js +170 -0
  124. package/package.json +56 -0
package/README.md ADDED
@@ -0,0 +1,305 @@
1
+ # @classic-homes/theme-svelte
2
+
3
+ Svelte 5 components for the Classic Theme design system. Built on [Bits UI](https://bits-ui.com) primitives and styled with Tailwind CSS.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @classic-homes/theme-svelte @classic-homes/theme-tokens
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ ### 1. Configure Tailwind CSS
14
+
15
+ Add the Classic Theme preset to your `tailwind.config.js`:
16
+
17
+ ```js
18
+ import preset from '@classic-homes/theme-tailwind-preset';
19
+
20
+ export default {
21
+ presets: [preset],
22
+ content: [
23
+ './src/**/*.{html,js,svelte,ts}',
24
+ './node_modules/@classic-homes/theme-svelte/**/*.{html,js,svelte,ts}',
25
+ ],
26
+ };
27
+ ```
28
+
29
+ ### 2. Import Design Tokens
30
+
31
+ Import the CSS tokens in your app's root CSS file:
32
+
33
+ ```css
34
+ @import '@classic-homes/theme-tokens/css';
35
+ ```
36
+
37
+ Or in your root layout:
38
+
39
+ ```svelte
40
+ <script>
41
+ import '@classic-homes/theme-tokens/css';
42
+ </script>
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ ```svelte
48
+ <script>
49
+ import { Button, Card, CardHeader, CardTitle, CardContent } from '@classic-homes/theme-svelte';
50
+ </script>
51
+
52
+ <Card>
53
+ <CardHeader>
54
+ <CardTitle>Welcome</CardTitle>
55
+ </CardHeader>
56
+ <CardContent>
57
+ <Button variant="default">Click me</Button>
58
+ </CardContent>
59
+ </Card>
60
+ ```
61
+
62
+ ## Components
63
+
64
+ ### Core UI
65
+
66
+ - `Button` - Buttons with variants (default, secondary, destructive, outline, ghost, link)
67
+ - `Input` - Text input field
68
+ - `Textarea` - Multi-line text input
69
+ - `Label` - Form labels
70
+ - `Checkbox` - Checkbox input
71
+ - `Switch` - Toggle switch
72
+ - `Select` - Dropdown select
73
+ - `FormField` - Combined label + input + error handling
74
+
75
+ ### Layout
76
+
77
+ - `AppShell` - Application wrapper with skip links and toast container
78
+ - `DashboardLayout` - Sidebar + header layout for dashboards
79
+ - `PublicLayout` - Header + footer layout for public pages
80
+ - `FormPageLayout` - Centered form page layout
81
+ - `Sidebar` - Navigation sidebar
82
+ - `Header` - Page header
83
+ - `Footer` - Page footer
84
+
85
+ ### Feedback
86
+
87
+ - `Alert` - Alert messages (default, destructive, success, warning, info)
88
+ - `AlertDialog` - Confirmation dialogs
89
+ - `Dialog` - Modal dialogs
90
+ - `Toast` - Toast notifications
91
+ - `ToastContainer` - Toast notification container
92
+ - `Spinner` - Loading spinner
93
+ - `Skeleton` - Loading skeleton
94
+
95
+ ### Data Display
96
+
97
+ - `Card` - Card container with header, content, footer
98
+ - `Badge` - Status badges
99
+ - `DataTable` - Sortable data table
100
+ - `Tabs` - Tab navigation
101
+ - `Tooltip` - Hover tooltips
102
+ - `DropdownMenu` - Dropdown menus
103
+ - `Avatar` - User avatars
104
+
105
+ ### Branding
106
+
107
+ - `LogoMain` - Main logo component
108
+ - `LoadingLogo` - Animated loading logo
109
+
110
+ ## Error Handling
111
+
112
+ ### Overview
113
+
114
+ Components in this library do not include built-in error boundaries to give you full control over error handling. We recommend implementing error handling at your application's root level.
115
+
116
+ ### Recommended: Error Boundary Component
117
+
118
+ Create an ErrorBoundary component to catch and handle errors gracefully:
119
+
120
+ ```svelte
121
+ <!-- src/lib/ErrorBoundary.svelte -->
122
+ <script lang="ts">
123
+ import type { Snippet } from 'svelte';
124
+ import { Button, Alert, AlertDescription } from '@classic-homes/theme-svelte';
125
+
126
+ interface Props {
127
+ children: Snippet;
128
+ fallback?: Snippet<[Error, () => void]>;
129
+ }
130
+
131
+ let { children, fallback }: Props = $props();
132
+ let error = $state<Error | null>(null);
133
+
134
+ function reset() {
135
+ error = null;
136
+ }
137
+
138
+ $effect(() => {
139
+ const handleError = (e: ErrorEvent) => {
140
+ e.preventDefault();
141
+ error = e.error || new Error(e.message);
142
+ };
143
+
144
+ const handleUnhandledRejection = (e: PromiseRejectionEvent) => {
145
+ e.preventDefault();
146
+ error = e.reason instanceof Error ? e.reason : new Error(String(e.reason));
147
+ };
148
+
149
+ window.addEventListener('error', handleError);
150
+ window.addEventListener('unhandledrejection', handleUnhandledRejection);
151
+
152
+ return () => {
153
+ window.removeEventListener('error', handleError);
154
+ window.removeEventListener('unhandledrejection', handleUnhandledRejection);
155
+ };
156
+ });
157
+ </script>
158
+
159
+ {#if error}
160
+ {#if fallback}
161
+ {@render fallback(error, reset)}
162
+ {:else}
163
+ <div class="flex min-h-screen items-center justify-center p-4">
164
+ <div class="w-full max-w-md space-y-4">
165
+ <Alert variant="destructive">
166
+ <AlertDescription>
167
+ <strong>Something went wrong</strong>
168
+ <p class="mt-2 text-sm">{error.message}</p>
169
+ </AlertDescription>
170
+ </Alert>
171
+ <Button onclick={reset} variant="outline" class="w-full">Try again</Button>
172
+ </div>
173
+ </div>
174
+ {/if}
175
+ {:else}
176
+ {@render children()}
177
+ {/if}
178
+ ```
179
+
180
+ ### Usage with AppShell
181
+
182
+ Wrap your `AppShell` with the ErrorBoundary:
183
+
184
+ ```svelte
185
+ <!-- src/routes/+layout.svelte -->
186
+ <script>
187
+ import { AppShell } from '@classic-homes/theme-svelte';
188
+ import ErrorBoundary from '$lib/ErrorBoundary.svelte';
189
+ </script>
190
+
191
+ <ErrorBoundary>
192
+ <AppShell>
193
+ <slot />
194
+ </AppShell>
195
+ </ErrorBoundary>
196
+ ```
197
+
198
+ ### Custom Fallback UI
199
+
200
+ You can provide a custom fallback UI:
201
+
202
+ ```svelte
203
+ <ErrorBoundary>
204
+ {#snippet fallback(error, reset)}
205
+ <div class="error-page">
206
+ <h1>Oops!</h1>
207
+ <p>{error.message}</p>
208
+ <button onclick={reset}>Reload</button>
209
+ </div>
210
+ {/snippet}
211
+
212
+ <AppShell>
213
+ <slot />
214
+ </AppShell>
215
+ </ErrorBoundary>
216
+ ```
217
+
218
+ ## Toast Notifications
219
+
220
+ The library includes a toast store for managing notifications:
221
+
222
+ ```svelte
223
+ <script>
224
+ import { toastStore, Button } from '@classic-homes/theme-svelte';
225
+
226
+ function showSuccess() {
227
+ toastStore.success('Operation completed successfully!');
228
+ }
229
+
230
+ function showError() {
231
+ toastStore.error('Something went wrong', { title: 'Error' });
232
+ }
233
+ </script>
234
+
235
+ <Button onclick={showSuccess}>Show Success</Button>
236
+ <Button onclick={showError} variant="destructive">Show Error</Button>
237
+ ```
238
+
239
+ Toast methods:
240
+
241
+ - `toastStore.success(message, options?)` - Green success toast
242
+ - `toastStore.error(message, options?)` - Red error toast (persistent by default)
243
+ - `toastStore.warning(message, options?)` - Yellow warning toast
244
+ - `toastStore.info(message, options?)` - Blue info toast
245
+ - `toastStore.add(toast)` - Add custom toast
246
+ - `toastStore.remove(id)` - Remove toast by ID
247
+ - `toastStore.clear()` - Remove all toasts
248
+
249
+ ## Sidebar State
250
+
251
+ For layouts with collapsible sidebars, use the sidebar store:
252
+
253
+ ```svelte
254
+ <script>
255
+ import { sidebarStore } from '@classic-homes/theme-svelte';
256
+
257
+ function toggleSidebar() {
258
+ sidebarStore.toggle();
259
+ }
260
+ </script>
261
+
262
+ <button onclick={toggleSidebar}>
263
+ {sidebarStore.isOpen ? 'Close' : 'Open'} Sidebar
264
+ </button>
265
+ ```
266
+
267
+ ## TypeScript Support
268
+
269
+ All components are fully typed. Import types as needed:
270
+
271
+ ```typescript
272
+ import type {
273
+ NavItem,
274
+ NavSection,
275
+ User,
276
+ Tab,
277
+ FileMetadata,
278
+ DataTableColumn,
279
+ SelectOption,
280
+ } from '@classic-homes/theme-svelte';
281
+ ```
282
+
283
+ ## Svelte 5 Runes
284
+
285
+ All components use Svelte 5 runes syntax:
286
+
287
+ - `$props()` for component props
288
+ - `$state()` for reactive state
289
+ - `$derived()` for computed values
290
+ - `$effect()` for side effects
291
+ - `$bindable()` for two-way binding
292
+
293
+ ## Accessibility
294
+
295
+ Components are built with accessibility in mind:
296
+
297
+ - Proper ARIA attributes
298
+ - Keyboard navigation support
299
+ - Focus management
300
+ - Screen reader friendly
301
+ - Skip links in AppShell
302
+
303
+ ## License
304
+
305
+ MIT
@@ -0,0 +1,51 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { cn } from '../utils.js';
4
+ import { tv, type VariantProps } from 'tailwind-variants';
5
+
6
+ const alertVariants = tv({
7
+ base: 'relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground',
8
+ variants: {
9
+ variant: {
10
+ default:
11
+ 'bg-gray-50 dark:bg-gray-800 border-gray-200 dark:border-gray-700 text-gray-900 dark:text-gray-100',
12
+ destructive:
13
+ 'bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800 text-red-700 dark:text-red-400 [&>svg]:text-red-700 dark:[&>svg]:text-red-400',
14
+ error:
15
+ 'bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800 text-red-700 dark:text-red-400 [&>svg]:text-red-700 dark:[&>svg]:text-red-400',
16
+ success:
17
+ 'bg-green-50 dark:bg-green-900/20 border-green-200 dark:border-green-800 text-green-700 dark:text-green-400 [&>svg]:text-green-700 dark:[&>svg]:text-green-400',
18
+ warning:
19
+ 'bg-yellow-50 dark:bg-yellow-900/20 border-yellow-200 dark:border-yellow-800 text-yellow-700 dark:text-yellow-400 [&>svg]:text-yellow-700 dark:[&>svg]:text-yellow-400',
20
+ info: 'bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800 text-blue-700 dark:text-blue-400 [&>svg]:text-blue-700 dark:[&>svg]:text-blue-400',
21
+ },
22
+ },
23
+ defaultVariants: {
24
+ variant: 'default',
25
+ },
26
+ });
27
+
28
+ type AlertVariants = VariantProps<typeof alertVariants>;
29
+
30
+ interface Props {
31
+ variant?: AlertVariants['variant'];
32
+ class?: string;
33
+ children: Snippet;
34
+ [key: string]: unknown;
35
+ }
36
+
37
+ let { variant = 'default', class: className, children, ...restProps }: Props = $props();
38
+
39
+ const classes = $derived(cn(alertVariants({ variant }), className));
40
+
41
+ // Use assertive for errors/warnings, polite for info/success/default
42
+ const ariaLive = $derived(
43
+ variant === 'error' || variant === 'destructive' || variant === 'warning'
44
+ ? 'assertive'
45
+ : 'polite'
46
+ );
47
+ </script>
48
+
49
+ <div role="alert" aria-live={ariaLive} class={classes} {...restProps}>
50
+ {@render children()}
51
+ </div>
@@ -0,0 +1,9 @@
1
+ import type { Snippet } from 'svelte';
2
+ declare const Alert: import("svelte").Component<{
3
+ [key: string]: unknown;
4
+ variant?: "default" | "destructive" | "error" | "success" | "warning" | "info" | undefined;
5
+ class?: string;
6
+ children: Snippet;
7
+ }, {}, "">;
8
+ type Alert = ReturnType<typeof Alert>;
9
+ export default Alert;
@@ -0,0 +1,16 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { cn } from '../utils.js';
4
+
5
+ interface Props {
6
+ class?: string;
7
+ children: Snippet;
8
+ [key: string]: unknown;
9
+ }
10
+
11
+ let { class: className, children, ...restProps }: Props = $props();
12
+ </script>
13
+
14
+ <div class={cn('text-sm [&_p]:leading-relaxed', className)} {...restProps}>
15
+ {@render children()}
16
+ </div>
@@ -0,0 +1,9 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ class?: string;
4
+ children: Snippet;
5
+ [key: string]: unknown;
6
+ }
7
+ declare const AlertDescription: import("svelte").Component<Props, {}, "">;
8
+ type AlertDescription = ReturnType<typeof AlertDescription>;
9
+ export default AlertDescription;
@@ -0,0 +1,136 @@
1
+ <script lang="ts">
2
+ import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
3
+ import { cn } from '../utils.js';
4
+ import type { Snippet } from 'svelte';
5
+ import { tv, type VariantProps } from 'tailwind-variants';
6
+
7
+ const buttonVariants = tv({
8
+ base: 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
9
+ variants: {
10
+ variant: {
11
+ default: 'bg-primary text-primary-foreground hover:bg-primary/90',
12
+ destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
13
+ outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
14
+ ghost: 'hover:bg-accent hover:text-accent-foreground',
15
+ },
16
+ },
17
+ defaultVariants: {
18
+ variant: 'default',
19
+ },
20
+ });
21
+
22
+ interface Props {
23
+ /** Whether the dialog is open */
24
+ open?: boolean;
25
+ /** Callback when open state changes */
26
+ onOpenChange?: (open: boolean) => void;
27
+ /** Dialog title */
28
+ title: string;
29
+ /** Dialog description */
30
+ description?: string;
31
+ /** Cancel button text */
32
+ cancelText?: string;
33
+ /** Confirm button text */
34
+ confirmText?: string;
35
+ /** Confirm button variant */
36
+ confirmVariant?: VariantProps<typeof buttonVariants>['variant'];
37
+ /** Callback when user confirms */
38
+ onConfirm?: () => void;
39
+ /** Callback when user cancels */
40
+ onCancel?: () => void;
41
+ /** Additional class for dialog content */
42
+ class?: string;
43
+ /** Optional trigger element */
44
+ trigger?: Snippet;
45
+ /** Optional custom content */
46
+ children?: Snippet;
47
+ }
48
+
49
+ let {
50
+ open = $bindable(false),
51
+ onOpenChange,
52
+ title,
53
+ description,
54
+ cancelText = 'Cancel',
55
+ confirmText = 'Continue',
56
+ confirmVariant = 'default',
57
+ onConfirm,
58
+ onCancel,
59
+ class: className,
60
+ trigger,
61
+ children,
62
+ }: Props = $props();
63
+
64
+ function handleOpenChange(newOpen: boolean) {
65
+ open = newOpen;
66
+ onOpenChange?.(newOpen);
67
+ }
68
+
69
+ function handleCancel() {
70
+ onCancel?.();
71
+ open = false;
72
+ onOpenChange?.(false);
73
+ }
74
+
75
+ function handleConfirm() {
76
+ onConfirm?.();
77
+ open = false;
78
+ onOpenChange?.(false);
79
+ }
80
+ </script>
81
+
82
+ <AlertDialogPrimitive.Root bind:open onOpenChange={handleOpenChange}>
83
+ {#if trigger}
84
+ <AlertDialogPrimitive.Trigger asChild>
85
+ {#snippet child({ props })}
86
+ <span {...props}>
87
+ {@render trigger()}
88
+ </span>
89
+ {/snippet}
90
+ </AlertDialogPrimitive.Trigger>
91
+ {/if}
92
+
93
+ <AlertDialogPrimitive.Portal>
94
+ <AlertDialogPrimitive.Overlay
95
+ class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
96
+ />
97
+ <AlertDialogPrimitive.Content
98
+ class={cn(
99
+ 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
100
+ className
101
+ )}
102
+ >
103
+ <div class="flex flex-col space-y-2 text-center sm:text-left">
104
+ <AlertDialogPrimitive.Title class="text-lg font-semibold">
105
+ {title}
106
+ </AlertDialogPrimitive.Title>
107
+ {#if description}
108
+ <AlertDialogPrimitive.Description class="text-sm text-muted-foreground">
109
+ {description}
110
+ </AlertDialogPrimitive.Description>
111
+ {/if}
112
+ </div>
113
+
114
+ {#if children}
115
+ <div class="py-2">
116
+ {@render children()}
117
+ </div>
118
+ {/if}
119
+
120
+ <div class="flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2">
121
+ <AlertDialogPrimitive.Cancel
122
+ class={cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0 h-10 px-4 py-2')}
123
+ onclick={handleCancel}
124
+ >
125
+ {cancelText}
126
+ </AlertDialogPrimitive.Cancel>
127
+ <AlertDialogPrimitive.Action
128
+ class={cn(buttonVariants({ variant: confirmVariant }), 'h-10 px-4 py-2')}
129
+ onclick={handleConfirm}
130
+ >
131
+ {confirmText}
132
+ </AlertDialogPrimitive.Action>
133
+ </div>
134
+ </AlertDialogPrimitive.Content>
135
+ </AlertDialogPrimitive.Portal>
136
+ </AlertDialogPrimitive.Root>
@@ -0,0 +1,79 @@
1
+ import type { Snippet } from 'svelte';
2
+ import { type VariantProps } from 'tailwind-variants';
3
+ declare const AlertDialog: import("svelte").Component<{
4
+ /** Whether the dialog is open */
5
+ open?: boolean;
6
+ /** Callback when open state changes */
7
+ onOpenChange?: (open: boolean) => void;
8
+ /** Dialog title */
9
+ title: string;
10
+ /** Dialog description */
11
+ description?: string;
12
+ /** Cancel button text */
13
+ cancelText?: string;
14
+ /** Confirm button text */
15
+ confirmText?: string;
16
+ /** Confirm button variant */
17
+ confirmVariant?: VariantProps<import("tailwind-variants").TVReturnType<{
18
+ variant: {
19
+ default: string;
20
+ destructive: string;
21
+ outline: string;
22
+ ghost: string;
23
+ };
24
+ }, undefined, "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", import("tailwind-variants/dist/config.js").TVConfig<{
25
+ variant: {
26
+ default: string;
27
+ destructive: string;
28
+ outline: string;
29
+ ghost: string;
30
+ };
31
+ }, {
32
+ variant: {
33
+ default: string;
34
+ destructive: string;
35
+ outline: string;
36
+ ghost: string;
37
+ };
38
+ }>, {
39
+ variant: {
40
+ default: string;
41
+ destructive: string;
42
+ outline: string;
43
+ ghost: string;
44
+ };
45
+ }, undefined, import("tailwind-variants").TVReturnType<{
46
+ variant: {
47
+ default: string;
48
+ destructive: string;
49
+ outline: string;
50
+ ghost: string;
51
+ };
52
+ }, undefined, "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", import("tailwind-variants/dist/config.js").TVConfig<{
53
+ variant: {
54
+ default: string;
55
+ destructive: string;
56
+ outline: string;
57
+ ghost: string;
58
+ };
59
+ }, {
60
+ variant: {
61
+ default: string;
62
+ destructive: string;
63
+ outline: string;
64
+ ghost: string;
65
+ };
66
+ }>, unknown, unknown, undefined>>>["variant"];
67
+ /** Callback when user confirms */
68
+ onConfirm?: () => void;
69
+ /** Callback when user cancels */
70
+ onCancel?: () => void;
71
+ /** Additional class for dialog content */
72
+ class?: string;
73
+ /** Optional trigger element */
74
+ trigger?: Snippet;
75
+ /** Optional custom content */
76
+ children?: Snippet;
77
+ }, {}, "open">;
78
+ type AlertDialog = ReturnType<typeof AlertDialog>;
79
+ export default AlertDialog;
@@ -0,0 +1,16 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { cn } from '../utils.js';
4
+
5
+ interface Props {
6
+ class?: string;
7
+ children: Snippet;
8
+ [key: string]: unknown;
9
+ }
10
+
11
+ let { class: className, children, ...restProps }: Props = $props();
12
+ </script>
13
+
14
+ <h5 class={cn('mb-1 font-medium leading-none tracking-tight', className)} {...restProps}>
15
+ {@render children()}
16
+ </h5>
@@ -0,0 +1,9 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ class?: string;
4
+ children: Snippet;
5
+ [key: string]: unknown;
6
+ }
7
+ declare const AlertTitle: import("svelte").Component<Props, {}, "">;
8
+ type AlertTitle = ReturnType<typeof AlertTitle>;
9
+ export default AlertTitle;
@@ -0,0 +1,56 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Avatar - User/entity representation component
4
+ *
5
+ * Built on Bits UI Avatar primitive for accessibility and loading state handling.
6
+ * Displays user images with fallback placeholder support.
7
+ *
8
+ * Features:
9
+ * - Multiple size variants (xs, sm, md, lg)
10
+ * - Automatic image loading state management
11
+ * - Fallback display when image fails/loading
12
+ * - Circular design by default
13
+ */
14
+ import type { Snippet } from 'svelte';
15
+ import { Avatar as AvatarPrimitive } from 'bits-ui';
16
+ import { cn } from '../utils.js';
17
+ import { tv, type VariantProps } from 'tailwind-variants';
18
+
19
+ const avatarVariants = tv({
20
+ base: 'relative flex shrink-0 overflow-hidden rounded-full',
21
+ variants: {
22
+ size: {
23
+ xs: 'h-6 w-6',
24
+ sm: 'h-8 w-8',
25
+ md: 'h-10 w-10',
26
+ lg: 'h-12 w-12',
27
+ xl: 'h-16 w-16',
28
+ },
29
+ },
30
+ defaultVariants: {
31
+ size: 'md',
32
+ },
33
+ });
34
+
35
+ type AvatarVariants = VariantProps<typeof avatarVariants>;
36
+
37
+ interface Props {
38
+ /** Size variant */
39
+ size?: AvatarVariants['size'];
40
+ /** Delay before showing fallback (ms) */
41
+ delayMs?: number;
42
+ /** Additional classes */
43
+ class?: string;
44
+ /** Avatar content (AvatarImage and/or AvatarFallback) */
45
+ children: Snippet;
46
+ [key: string]: unknown;
47
+ }
48
+
49
+ let { size = 'md', delayMs = 0, class: className, children, ...restProps }: Props = $props();
50
+
51
+ const classes = $derived(cn(avatarVariants({ size }), className));
52
+ </script>
53
+
54
+ <AvatarPrimitive.Root class={classes} {delayMs} {...restProps}>
55
+ {@render children()}
56
+ </AvatarPrimitive.Root>