@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
@@ -0,0 +1,125 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { setContext } from 'svelte';
4
+ import { cn } from '../utils.js';
5
+ import type { Tab } from '../types/components.js';
6
+
7
+ interface Props {
8
+ /** Array of tab definitions */
9
+ tabs: Tab[];
10
+ /** Active tab ID (bindable) */
11
+ value?: string;
12
+ /** Unique ID for ARIA relationships */
13
+ id?: string;
14
+ /** Additional classes for container */
15
+ class?: string;
16
+ /** Tab panel content */
17
+ children?: Snippet;
18
+ /** Callback when active tab changes */
19
+ onValueChange?: (value: string) => void;
20
+ }
21
+
22
+ let {
23
+ tabs,
24
+ value = $bindable(tabs[0]?.id ?? ''),
25
+ id = `tabs-${Math.random().toString(36).slice(2, 11)}`,
26
+ class: className,
27
+ children,
28
+ onValueChange,
29
+ }: Props = $props();
30
+
31
+ // Create a reactive object for context
32
+ // Initialize with empty values - the effect will set them immediately
33
+ const tabsState = $state({ activeTab: '', tabsId: '' });
34
+
35
+ // Keep context state in sync with props (runs immediately and on changes)
36
+ $effect(() => {
37
+ tabsState.activeTab = value;
38
+ tabsState.tabsId = id;
39
+ });
40
+
41
+ // Set up context for TabPanel children (done once at component init)
42
+ setContext('tabs', tabsState);
43
+
44
+ function selectTab(tabId: string) {
45
+ if (tabs.find((t) => t.id === tabId && t.disabled)) return;
46
+ value = tabId;
47
+ tabsState.activeTab = tabId;
48
+ onValueChange?.(tabId);
49
+ }
50
+
51
+ function handleKeyDown(e: KeyboardEvent, currentIndex: number) {
52
+ const enabledTabs = tabs.filter((t) => !t.disabled);
53
+ const currentEnabledIndex = enabledTabs.findIndex((t) => t.id === tabs[currentIndex].id);
54
+
55
+ let newIndex = currentEnabledIndex;
56
+
57
+ switch (e.key) {
58
+ case 'ArrowLeft':
59
+ e.preventDefault();
60
+ newIndex = currentEnabledIndex > 0 ? currentEnabledIndex - 1 : enabledTabs.length - 1;
61
+ break;
62
+ case 'ArrowRight':
63
+ e.preventDefault();
64
+ newIndex = currentEnabledIndex < enabledTabs.length - 1 ? currentEnabledIndex + 1 : 0;
65
+ break;
66
+ case 'Home':
67
+ e.preventDefault();
68
+ newIndex = 0;
69
+ break;
70
+ case 'End':
71
+ e.preventDefault();
72
+ newIndex = enabledTabs.length - 1;
73
+ break;
74
+ default:
75
+ return;
76
+ }
77
+
78
+ const newTab = enabledTabs[newIndex];
79
+ if (newTab) {
80
+ selectTab(newTab.id);
81
+ // Focus the new tab
82
+ const tabElement = document.getElementById(`${id}-tab-${newTab.id}`);
83
+ tabElement?.focus();
84
+ }
85
+ }
86
+ </script>
87
+
88
+ <div class={cn('w-full', className)}>
89
+ <!-- Tab List -->
90
+ <div
91
+ role="tablist"
92
+ aria-label="Tabs"
93
+ class="inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground"
94
+ >
95
+ {#each tabs as tab, index (tab.id)}
96
+ <button
97
+ id={`${id}-tab-${tab.id}`}
98
+ role="tab"
99
+ type="button"
100
+ aria-selected={value === tab.id}
101
+ aria-controls={`${id}-panel-${tab.id}`}
102
+ tabindex={value === tab.id ? 0 : -1}
103
+ disabled={tab.disabled}
104
+ class={cn(
105
+ 'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
106
+ value === tab.id
107
+ ? 'bg-background text-foreground shadow-sm'
108
+ : 'hover:bg-background/50 hover:text-foreground',
109
+ tab.disabled && 'opacity-50 pointer-events-none cursor-not-allowed'
110
+ )}
111
+ onclick={() => selectTab(tab.id)}
112
+ onkeydown={(e) => handleKeyDown(e, index)}
113
+ >
114
+ {tab.label}
115
+ </button>
116
+ {/each}
117
+ </div>
118
+
119
+ <!-- Tab Panels Container -->
120
+ {#if children}
121
+ <div class="mt-2">
122
+ {@render children()}
123
+ </div>
124
+ {/if}
125
+ </div>
@@ -0,0 +1,19 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { Tab } from '../types/components.js';
3
+ interface Props {
4
+ /** Array of tab definitions */
5
+ tabs: Tab[];
6
+ /** Active tab ID (bindable) */
7
+ value?: string;
8
+ /** Unique ID for ARIA relationships */
9
+ id?: string;
10
+ /** Additional classes for container */
11
+ class?: string;
12
+ /** Tab panel content */
13
+ children?: Snippet;
14
+ /** Callback when active tab changes */
15
+ onValueChange?: (value: string) => void;
16
+ }
17
+ declare const Tabs: import("svelte").Component<Props, {}, "value">;
18
+ type Tabs = ReturnType<typeof Tabs>;
19
+ export default Tabs;
@@ -0,0 +1,54 @@
1
+ <script lang="ts">
2
+ import { cn } from '../utils.js';
3
+
4
+ interface Props {
5
+ value?: string;
6
+ placeholder?: string;
7
+ disabled?: boolean;
8
+ readonly?: boolean;
9
+ required?: boolean;
10
+ name?: string;
11
+ id?: string;
12
+ rows?: number;
13
+ class?: string;
14
+ onValueChange?: (value: string) => void;
15
+ [key: string]: unknown;
16
+ }
17
+
18
+ let {
19
+ value = $bindable(''),
20
+ placeholder,
21
+ disabled = false,
22
+ readonly = false,
23
+ required = false,
24
+ name,
25
+ id,
26
+ rows = 3,
27
+ class: className,
28
+ onValueChange,
29
+ ...restProps
30
+ }: Props = $props();
31
+
32
+ function handleInput(e: Event) {
33
+ const target = e.target as HTMLTextAreaElement;
34
+ value = target.value;
35
+ onValueChange?.(target.value);
36
+ }
37
+ </script>
38
+
39
+ <textarea
40
+ {value}
41
+ {placeholder}
42
+ {disabled}
43
+ {readonly}
44
+ {required}
45
+ {name}
46
+ {id}
47
+ {rows}
48
+ class={cn(
49
+ 'flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-gray-500 dark:placeholder:text-gray-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 resize-y',
50
+ className
51
+ )}
52
+ oninput={handleInput}
53
+ {...restProps}
54
+ ></textarea>
@@ -0,0 +1,16 @@
1
+ interface Props {
2
+ value?: string;
3
+ placeholder?: string;
4
+ disabled?: boolean;
5
+ readonly?: boolean;
6
+ required?: boolean;
7
+ name?: string;
8
+ id?: string;
9
+ rows?: number;
10
+ class?: string;
11
+ onValueChange?: (value: string) => void;
12
+ [key: string]: unknown;
13
+ }
14
+ declare const Textarea: import("svelte").Component<Props, {}, "value">;
15
+ type Textarea = ReturnType<typeof Textarea>;
16
+ export default Textarea;
@@ -0,0 +1,116 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { fly } from 'svelte/transition';
4
+ import { cn } from '../utils.js';
5
+ import { tv, type VariantProps } from 'tailwind-variants';
6
+
7
+ type ToastType = 'success' | 'error' | 'warning' | 'info';
8
+
9
+ const toastVariants = tv({
10
+ base: 'group pointer-events-auto relative flex w-full items-start gap-3 overflow-hidden rounded-lg border p-4 shadow-lg transition-all',
11
+ variants: {
12
+ type: {
13
+ success:
14
+ 'bg-green-50 text-green-800 border-green-200 dark:bg-green-900/20 dark:text-green-400 dark:border-green-800',
15
+ error:
16
+ 'bg-red-50 text-red-800 border-red-200 dark:bg-red-900/20 dark:text-red-400 dark:border-red-800',
17
+ warning:
18
+ 'bg-yellow-50 text-yellow-800 border-yellow-200 dark:bg-yellow-900/20 dark:text-yellow-400 dark:border-yellow-800',
19
+ info: 'bg-blue-50 text-blue-800 border-blue-200 dark:bg-blue-900/20 dark:text-blue-400 dark:border-blue-800',
20
+ },
21
+ },
22
+ defaultVariants: {
23
+ type: 'info',
24
+ },
25
+ });
26
+
27
+ type ToastVariants = VariantProps<typeof toastVariants>;
28
+
29
+ // Icon paths for each toast type
30
+ const icons: Record<ToastType, string> = {
31
+ success: 'M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z',
32
+ error: 'M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z',
33
+ warning:
34
+ 'M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z',
35
+ info: 'M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z',
36
+ };
37
+
38
+ const iconColors: Record<ToastType, string> = {
39
+ success: 'text-green-500 dark:text-green-400',
40
+ error: 'text-red-500 dark:text-red-400',
41
+ warning: 'text-yellow-500 dark:text-yellow-400',
42
+ info: 'text-blue-500 dark:text-blue-400',
43
+ };
44
+
45
+ interface Props {
46
+ type?: ToastVariants['type'];
47
+ title?: string;
48
+ message: string;
49
+ class?: string;
50
+ onclose?: () => void;
51
+ action?: Snippet;
52
+ [key: string]: unknown;
53
+ }
54
+
55
+ let {
56
+ type = 'info',
57
+ title,
58
+ message,
59
+ class: className,
60
+ onclose,
61
+ action,
62
+ ...restProps
63
+ }: Props = $props();
64
+
65
+ const classes = $derived(cn(toastVariants({ type }), className));
66
+ const toastType = $derived(type as ToastType);
67
+ </script>
68
+
69
+ <div class={classes} role="alert" transition:fly={{ y: 50, duration: 300 }} {...restProps}>
70
+ <svg
71
+ class="h-5 w-5 flex-shrink-0 {iconColors[toastType]}"
72
+ fill="none"
73
+ stroke="currentColor"
74
+ viewBox="0 0 24 24"
75
+ xmlns="http://www.w3.org/2000/svg"
76
+ aria-hidden="true"
77
+ >
78
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d={icons[toastType]} />
79
+ </svg>
80
+
81
+ <div class="flex-1">
82
+ {#if title}
83
+ <div class="text-sm font-semibold">{title}</div>
84
+ {/if}
85
+ <p class="text-sm font-medium">{message}</p>
86
+ </div>
87
+
88
+ {#if action}
89
+ {@render action()}
90
+ {/if}
91
+
92
+ {#if onclose}
93
+ <button
94
+ type="button"
95
+ class="flex-shrink-0 rounded-md p-1 hover:bg-black/5 dark:hover:bg-white/10 transition-colors"
96
+ onclick={onclose}
97
+ aria-label="Close"
98
+ >
99
+ <svg
100
+ class="h-4 w-4"
101
+ fill="none"
102
+ stroke="currentColor"
103
+ viewBox="0 0 24 24"
104
+ xmlns="http://www.w3.org/2000/svg"
105
+ aria-hidden="true"
106
+ >
107
+ <path
108
+ stroke-linecap="round"
109
+ stroke-linejoin="round"
110
+ stroke-width="2"
111
+ d="M6 18L18 6M6 6l12 12"
112
+ />
113
+ </svg>
114
+ </button>
115
+ {/if}
116
+ </div>
@@ -0,0 +1,12 @@
1
+ import type { Snippet } from 'svelte';
2
+ declare const Toast: import("svelte").Component<{
3
+ [key: string]: unknown;
4
+ type?: "error" | "success" | "warning" | "info" | undefined;
5
+ title?: string;
6
+ message: string;
7
+ class?: string;
8
+ onclose?: () => void;
9
+ action?: Snippet;
10
+ }, {}, "">;
11
+ type Toast = ReturnType<typeof Toast>;
12
+ export default Toast;
@@ -0,0 +1,56 @@
1
+ <script lang="ts">
2
+ import { cn } from '../utils.js';
3
+ import { toastStore } from '../stores/toast.svelte.js';
4
+ import Toast from './Toast.svelte';
5
+
6
+ interface Props {
7
+ position?:
8
+ | 'top-left'
9
+ | 'top-right'
10
+ | 'top-center'
11
+ | 'bottom-left'
12
+ | 'bottom-right'
13
+ | 'bottom-center';
14
+ class?: string;
15
+ [key: string]: unknown;
16
+ }
17
+
18
+ let { position = 'bottom-right', class: className, ...restProps }: Props = $props();
19
+
20
+ const positionClasses = {
21
+ 'top-left': 'top-4 left-4',
22
+ 'top-right': 'top-4 right-4',
23
+ 'top-center': 'top-4 left-1/2 -translate-x-1/2',
24
+ 'bottom-left': 'bottom-4 left-4',
25
+ 'bottom-right': 'bottom-4 right-4',
26
+ 'bottom-center': 'bottom-4 left-1/2 -translate-x-1/2',
27
+ };
28
+
29
+ // Stack order: bottom positions use flex-col-reverse so newest appears at bottom
30
+ const stackOrder = $derived(position.startsWith('bottom') ? 'flex-col' : 'flex-col-reverse');
31
+ </script>
32
+
33
+ {#if toastStore.toasts.length > 0}
34
+ <div
35
+ class={cn(
36
+ 'fixed z-50 flex w-full max-w-sm gap-2 pointer-events-none',
37
+ stackOrder,
38
+ positionClasses[position],
39
+ className
40
+ )}
41
+ aria-live="polite"
42
+ aria-atomic="true"
43
+ {...restProps}
44
+ >
45
+ {#each toastStore.toasts as toast (toast.id)}
46
+ <div class="pointer-events-auto">
47
+ <Toast
48
+ type={toast.type}
49
+ title={toast.title}
50
+ message={toast.message}
51
+ onclose={() => toastStore.remove(toast.id)}
52
+ />
53
+ </div>
54
+ {/each}
55
+ </div>
56
+ {/if}
@@ -0,0 +1,8 @@
1
+ interface Props {
2
+ position?: 'top-left' | 'top-right' | 'top-center' | 'bottom-left' | 'bottom-right' | 'bottom-center';
3
+ class?: string;
4
+ [key: string]: unknown;
5
+ }
6
+ declare const ToastContainer: import("svelte").Component<Props, {}, "">;
7
+ type ToastContainer = ReturnType<typeof ToastContainer>;
8
+ export default ToastContainer;
@@ -0,0 +1,55 @@
1
+ <script lang="ts">
2
+ import { Tooltip as TooltipPrimitive } from 'bits-ui';
3
+ import { cn } from '../utils.js';
4
+ import type { Snippet } from 'svelte';
5
+
6
+ interface Props {
7
+ /** Content to display in the tooltip */
8
+ content: string;
9
+ /** Side of the trigger to show tooltip */
10
+ side?: 'top' | 'right' | 'bottom' | 'left';
11
+ /** Alignment of tooltip relative to trigger */
12
+ align?: 'start' | 'center' | 'end';
13
+ /** Delay in ms before showing tooltip */
14
+ delayDuration?: number;
15
+ /** Additional class for tooltip content */
16
+ class?: string;
17
+ /** Trigger element */
18
+ children: Snippet;
19
+ }
20
+
21
+ let {
22
+ content,
23
+ side = 'top',
24
+ align = 'center',
25
+ delayDuration = 200,
26
+ class: className,
27
+ children,
28
+ }: Props = $props();
29
+ </script>
30
+
31
+ <TooltipPrimitive.Provider>
32
+ <TooltipPrimitive.Root {delayDuration}>
33
+ <TooltipPrimitive.Trigger asChild>
34
+ {#snippet child({ props })}
35
+ <span {...props} class="inline-flex">
36
+ {@render children()}
37
+ </span>
38
+ {/snippet}
39
+ </TooltipPrimitive.Trigger>
40
+ <TooltipPrimitive.Portal>
41
+ <TooltipPrimitive.Content
42
+ {side}
43
+ {align}
44
+ sideOffset={4}
45
+ class={cn(
46
+ 'z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
47
+ className
48
+ )}
49
+ >
50
+ {content}
51
+ <TooltipPrimitive.Arrow class="fill-primary" />
52
+ </TooltipPrimitive.Content>
53
+ </TooltipPrimitive.Portal>
54
+ </TooltipPrimitive.Root>
55
+ </TooltipPrimitive.Provider>
@@ -0,0 +1,18 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ /** Content to display in the tooltip */
4
+ content: string;
5
+ /** Side of the trigger to show tooltip */
6
+ side?: 'top' | 'right' | 'bottom' | 'left';
7
+ /** Alignment of tooltip relative to trigger */
8
+ align?: 'start' | 'center' | 'end';
9
+ /** Delay in ms before showing tooltip */
10
+ delayDuration?: number;
11
+ /** Additional class for tooltip content */
12
+ class?: string;
13
+ /** Trigger element */
14
+ children: Snippet;
15
+ }
16
+ declare const Tooltip: import("svelte").Component<Props, {}, "">;
17
+ type Tooltip = ReturnType<typeof Tooltip>;
18
+ export default Tooltip;
@@ -0,0 +1,82 @@
1
+ <script lang="ts">
2
+ /**
3
+ * AppShell - Base wrapper component for applications
4
+ *
5
+ * Provides:
6
+ * - Skip link for accessibility
7
+ * - Toast container for notifications
8
+ * - Consistent base structure
9
+ *
10
+ * ## Error Handling
11
+ *
12
+ * AppShell does not include built-in error boundaries to give you full control
13
+ * over error handling in your application. We recommend implementing error
14
+ * handling at your application's root level.
15
+ *
16
+ * ### Recommended Pattern
17
+ *
18
+ * Create an ErrorBoundary component that catches errors and wrap AppShell with it.
19
+ * See the package README for a complete ErrorBoundary implementation example.
20
+ *
21
+ * Usage:
22
+ * ErrorBoundary > AppShell > YourContent
23
+ *
24
+ * The ErrorBoundary should:
25
+ * 1. Listen for window 'error' events
26
+ * 2. Store error state using $state
27
+ * 3. Render fallback UI when error is present
28
+ * 4. Provide a way to reset/retry
29
+ *
30
+ * @see README.md for complete ErrorBoundary implementation
31
+ */
32
+ import type { Snippet } from 'svelte';
33
+ import ToastContainer from '../ToastContainer.svelte';
34
+
35
+ interface Props {
36
+ /** Skip link target ID */
37
+ skipToId?: string;
38
+ /** Skip link text */
39
+ skipToText?: string;
40
+ /** Toast container position */
41
+ toastPosition?:
42
+ | 'top-right'
43
+ | 'top-left'
44
+ | 'bottom-right'
45
+ | 'bottom-left'
46
+ | 'top-center'
47
+ | 'bottom-center';
48
+ /** Main content */
49
+ children: Snippet;
50
+ }
51
+
52
+ let {
53
+ skipToId = 'main-content',
54
+ skipToText = 'Skip to main content',
55
+ toastPosition = 'top-right',
56
+ children,
57
+ }: Props = $props();
58
+ </script>
59
+
60
+ <div class="app-shell min-h-screen bg-background text-foreground">
61
+ <!-- Skip Link for Accessibility -->
62
+ <a
63
+ href="#{skipToId}"
64
+ class="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-[100] focus:rounded-md focus:bg-primary focus:px-4 focus:py-2 focus:text-primary-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
65
+ >
66
+ {skipToText}
67
+ </a>
68
+
69
+ <!-- Main Content -->
70
+ {@render children()}
71
+
72
+ <!-- Toast Container -->
73
+ <ToastContainer position={toastPosition} />
74
+ </div>
75
+
76
+ <style>
77
+ .app-shell {
78
+ /* Ensure the app shell fills the viewport */
79
+ display: flex;
80
+ flex-direction: column;
81
+ }
82
+ </style>
@@ -0,0 +1,44 @@
1
+ /**
2
+ * AppShell - Base wrapper component for applications
3
+ *
4
+ * Provides:
5
+ * - Skip link for accessibility
6
+ * - Toast container for notifications
7
+ * - Consistent base structure
8
+ *
9
+ * ## Error Handling
10
+ *
11
+ * AppShell does not include built-in error boundaries to give you full control
12
+ * over error handling in your application. We recommend implementing error
13
+ * handling at your application's root level.
14
+ *
15
+ * ### Recommended Pattern
16
+ *
17
+ * Create an ErrorBoundary component that catches errors and wrap AppShell with it.
18
+ * See the package README for a complete ErrorBoundary implementation example.
19
+ *
20
+ * Usage:
21
+ * ErrorBoundary > AppShell > YourContent
22
+ *
23
+ * The ErrorBoundary should:
24
+ * 1. Listen for window 'error' events
25
+ * 2. Store error state using $state
26
+ * 3. Render fallback UI when error is present
27
+ * 4. Provide a way to reset/retry
28
+ *
29
+ * @see README.md for complete ErrorBoundary implementation
30
+ */
31
+ import type { Snippet } from 'svelte';
32
+ interface Props {
33
+ /** Skip link target ID */
34
+ skipToId?: string;
35
+ /** Skip link text */
36
+ skipToText?: string;
37
+ /** Toast container position */
38
+ toastPosition?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'top-center' | 'bottom-center';
39
+ /** Main content */
40
+ children: Snippet;
41
+ }
42
+ declare const AppShell: import("svelte").Component<Props, {}, "">;
43
+ type AppShell = ReturnType<typeof AppShell>;
44
+ export default AppShell;