@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.
- package/README.md +305 -0
- package/dist/lib/components/Alert.svelte +51 -0
- package/dist/lib/components/Alert.svelte.d.ts +9 -0
- package/dist/lib/components/AlertDescription.svelte +16 -0
- package/dist/lib/components/AlertDescription.svelte.d.ts +9 -0
- package/dist/lib/components/AlertDialog.svelte +136 -0
- package/dist/lib/components/AlertDialog.svelte.d.ts +79 -0
- package/dist/lib/components/AlertTitle.svelte +16 -0
- package/dist/lib/components/AlertTitle.svelte.d.ts +9 -0
- package/dist/lib/components/Avatar.svelte +56 -0
- package/dist/lib/components/Avatar.svelte.d.ts +26 -0
- package/dist/lib/components/AvatarFallback.svelte +31 -0
- package/dist/lib/components/AvatarFallback.svelte.d.ts +17 -0
- package/dist/lib/components/AvatarImage.svelte +29 -0
- package/dist/lib/components/AvatarImage.svelte.d.ts +12 -0
- package/dist/lib/components/Badge.svelte +73 -0
- package/dist/lib/components/Badge.svelte.d.ts +11 -0
- package/dist/lib/components/Button.svelte +130 -0
- package/dist/lib/components/Button.svelte.d.ts +17 -0
- package/dist/lib/components/Card.svelte +58 -0
- package/dist/lib/components/Card.svelte.d.ts +26 -0
- package/dist/lib/components/CardContent.svelte +16 -0
- package/dist/lib/components/CardContent.svelte.d.ts +9 -0
- package/dist/lib/components/CardDescription.svelte +16 -0
- package/dist/lib/components/CardDescription.svelte.d.ts +9 -0
- package/dist/lib/components/CardFooter.svelte +16 -0
- package/dist/lib/components/CardFooter.svelte.d.ts +9 -0
- package/dist/lib/components/CardHeader.svelte +16 -0
- package/dist/lib/components/CardHeader.svelte.d.ts +9 -0
- package/dist/lib/components/CardTitle.svelte +16 -0
- package/dist/lib/components/CardTitle.svelte.d.ts +9 -0
- package/dist/lib/components/Checkbox.svelte +65 -0
- package/dist/lib/components/Checkbox.svelte.d.ts +14 -0
- package/dist/lib/components/DataTable.svelte +334 -0
- package/dist/lib/components/DataTable.svelte.d.ts +103 -0
- package/dist/lib/components/Dialog.svelte +111 -0
- package/dist/lib/components/Dialog.svelte.d.ts +22 -0
- package/dist/lib/components/DropdownMenu.svelte +135 -0
- package/dist/lib/components/DropdownMenu.svelte.d.ts +33 -0
- package/dist/lib/components/FileUpload.svelte +448 -0
- package/dist/lib/components/FileUpload.svelte.d.ts +42 -0
- package/dist/lib/components/FormField.svelte +134 -0
- package/dist/lib/components/FormField.svelte.d.ts +37 -0
- package/dist/lib/components/Input.svelte +61 -0
- package/dist/lib/components/Input.svelte.d.ts +19 -0
- package/dist/lib/components/Label.svelte +33 -0
- package/dist/lib/components/Label.svelte.d.ts +11 -0
- package/dist/lib/components/LoadingLogo.svelte +124 -0
- package/dist/lib/components/LoadingLogo.svelte.d.ts +16 -0
- package/dist/lib/components/LogoMain.svelte +237 -0
- package/dist/lib/components/LogoMain.svelte.d.ts +20 -0
- package/dist/lib/components/PageHeader.svelte +90 -0
- package/dist/lib/components/PageHeader.svelte.d.ts +28 -0
- package/dist/lib/components/Section.svelte +44 -0
- package/dist/lib/components/Section.svelte.d.ts +28 -0
- package/dist/lib/components/Select.svelte +174 -0
- package/dist/lib/components/Select.svelte.d.ts +32 -0
- package/dist/lib/components/Separator.svelte +29 -0
- package/dist/lib/components/Separator.svelte.d.ts +9 -0
- package/dist/lib/components/Skeleton.svelte +35 -0
- package/dist/lib/components/Skeleton.svelte.d.ts +7 -0
- package/dist/lib/components/Spinner.svelte +50 -0
- package/dist/lib/components/Spinner.svelte.d.ts +8 -0
- package/dist/lib/components/Switch.svelte +56 -0
- package/dist/lib/components/Switch.svelte.d.ts +14 -0
- package/dist/lib/components/TabPanel.svelte +44 -0
- package/dist/lib/components/TabPanel.svelte.d.ts +12 -0
- package/dist/lib/components/Tabs.svelte +125 -0
- package/dist/lib/components/Tabs.svelte.d.ts +19 -0
- package/dist/lib/components/Textarea.svelte +54 -0
- package/dist/lib/components/Textarea.svelte.d.ts +16 -0
- package/dist/lib/components/Toast.svelte +116 -0
- package/dist/lib/components/Toast.svelte.d.ts +12 -0
- package/dist/lib/components/ToastContainer.svelte +56 -0
- package/dist/lib/components/ToastContainer.svelte.d.ts +8 -0
- package/dist/lib/components/Tooltip.svelte +55 -0
- package/dist/lib/components/Tooltip.svelte.d.ts +18 -0
- package/dist/lib/components/layout/AppShell.svelte +82 -0
- package/dist/lib/components/layout/AppShell.svelte.d.ts +44 -0
- package/dist/lib/components/layout/DashboardLayout.svelte +248 -0
- package/dist/lib/components/layout/DashboardLayout.svelte.d.ts +62 -0
- package/dist/lib/components/layout/Footer.svelte +130 -0
- package/dist/lib/components/layout/Footer.svelte.d.ts +32 -0
- package/dist/lib/components/layout/FormPageLayout.svelte +92 -0
- package/dist/lib/components/layout/FormPageLayout.svelte.d.ts +33 -0
- package/dist/lib/components/layout/Header.svelte +94 -0
- package/dist/lib/components/layout/Header.svelte.d.ts +30 -0
- package/dist/lib/components/layout/PublicLayout.svelte +180 -0
- package/dist/lib/components/layout/PublicLayout.svelte.d.ts +39 -0
- package/dist/lib/components/layout/QuickLinks.svelte +112 -0
- package/dist/lib/components/layout/QuickLinks.svelte.d.ts +27 -0
- package/dist/lib/components/layout/Sidebar.svelte +243 -0
- package/dist/lib/components/layout/Sidebar.svelte.d.ts +48 -0
- package/dist/lib/composables/index.d.ts +8 -0
- package/dist/lib/composables/index.js +10 -0
- package/dist/lib/composables/useAsync.svelte.d.ts +102 -0
- package/dist/lib/composables/useAsync.svelte.js +210 -0
- package/dist/lib/composables/useForm.svelte.d.ts +123 -0
- package/dist/lib/composables/useForm.svelte.js +245 -0
- package/dist/lib/index.d.ts +65 -0
- package/dist/lib/index.js +83 -0
- package/dist/lib/performance.d.ts +79 -0
- package/dist/lib/performance.js +170 -0
- package/dist/lib/schemas/auth.d.ts +410 -0
- package/dist/lib/schemas/auth.js +216 -0
- package/dist/lib/schemas/common.d.ts +267 -0
- package/dist/lib/schemas/common.js +268 -0
- package/dist/lib/schemas/index.d.ts +24 -0
- package/dist/lib/schemas/index.js +32 -0
- package/dist/lib/stores/sidebar.svelte.d.ts +25 -0
- package/dist/lib/stores/sidebar.svelte.js +38 -0
- package/dist/lib/stores/theme.svelte.d.ts +72 -0
- package/dist/lib/stores/theme.svelte.js +150 -0
- package/dist/lib/stores/toast.svelte.d.ts +62 -0
- package/dist/lib/stores/toast.svelte.js +93 -0
- package/dist/lib/types/components.d.ts +85 -0
- package/dist/lib/types/components.js +7 -0
- package/dist/lib/types/layout.d.ts +258 -0
- package/dist/lib/types/layout.js +7 -0
- package/dist/lib/utils.d.ts +6 -0
- package/dist/lib/utils.js +9 -0
- package/dist/lib/validation.d.ts +101 -0
- package/dist/lib/validation.js +170 -0
- package/package.json +56 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout Types for Classic Theme
|
|
3
|
+
*
|
|
4
|
+
* These types define the interfaces for layout components including
|
|
5
|
+
* navigation, user data, and layout props.
|
|
6
|
+
*/
|
|
7
|
+
import type { Snippet } from 'svelte';
|
|
8
|
+
/**
|
|
9
|
+
* Navigation item for sidebar and header navigation
|
|
10
|
+
*/
|
|
11
|
+
export interface NavItem {
|
|
12
|
+
/** Unique identifier for the item */
|
|
13
|
+
id: string;
|
|
14
|
+
/** Display name */
|
|
15
|
+
name: string;
|
|
16
|
+
/** URL to navigate to */
|
|
17
|
+
href: string;
|
|
18
|
+
/** Icon name (app renders via snippet) */
|
|
19
|
+
icon?: string;
|
|
20
|
+
/** Optional badge (notification count, etc.) */
|
|
21
|
+
badge?: string | number;
|
|
22
|
+
/** Role-based visibility */
|
|
23
|
+
roles?: string[];
|
|
24
|
+
/** Opens in new tab */
|
|
25
|
+
external?: boolean;
|
|
26
|
+
/** Whether this item is currently active */
|
|
27
|
+
active?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Navigation section for grouping nav items
|
|
31
|
+
*/
|
|
32
|
+
export interface NavSection {
|
|
33
|
+
/** Unique identifier for the section */
|
|
34
|
+
id: string;
|
|
35
|
+
/** Optional section header */
|
|
36
|
+
title?: string;
|
|
37
|
+
/** Items in this section */
|
|
38
|
+
items: NavItem[];
|
|
39
|
+
/** Can section be collapsed */
|
|
40
|
+
collapsible?: boolean;
|
|
41
|
+
/** Is section currently expanded (for collapsible sections) */
|
|
42
|
+
expanded?: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* User data for layout components
|
|
46
|
+
*/
|
|
47
|
+
export interface User {
|
|
48
|
+
/** Unique identifier */
|
|
49
|
+
id: string;
|
|
50
|
+
/** Display name */
|
|
51
|
+
name: string;
|
|
52
|
+
/** Email address */
|
|
53
|
+
email?: string;
|
|
54
|
+
/** Avatar URL */
|
|
55
|
+
avatar?: string;
|
|
56
|
+
/** User roles for authorization */
|
|
57
|
+
roles?: string[];
|
|
58
|
+
/** User initials (fallback for avatar) */
|
|
59
|
+
initials?: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Back link configuration for dashboard navigation
|
|
63
|
+
*/
|
|
64
|
+
export interface BackLink {
|
|
65
|
+
/** Link label (e.g., "Back to Dashboard") */
|
|
66
|
+
label: string;
|
|
67
|
+
/** Link URL */
|
|
68
|
+
href: string;
|
|
69
|
+
/** Icon name for the icon snippet */
|
|
70
|
+
icon?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Quick link item for sidebar quick links section
|
|
74
|
+
*/
|
|
75
|
+
export interface QuickLink {
|
|
76
|
+
/** Unique identifier */
|
|
77
|
+
id: string;
|
|
78
|
+
/** Display label */
|
|
79
|
+
label: string;
|
|
80
|
+
/** URL to navigate to */
|
|
81
|
+
href: string;
|
|
82
|
+
/** Icon name (app renders via snippet) */
|
|
83
|
+
icon?: string;
|
|
84
|
+
/** Opens in new tab */
|
|
85
|
+
external?: boolean;
|
|
86
|
+
/** Accessible label for screen readers (used in icon-only mode) */
|
|
87
|
+
ariaLabel?: string;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Props for QuickLinks component
|
|
91
|
+
*/
|
|
92
|
+
export interface QuickLinksProps {
|
|
93
|
+
/** Array of quick link items */
|
|
94
|
+
links: QuickLink[];
|
|
95
|
+
/** Display mode: 'list' for stacked with labels, 'icons' for horizontal icons only */
|
|
96
|
+
display?: 'list' | 'icons';
|
|
97
|
+
/** Visual variant - light (default) or dark */
|
|
98
|
+
variant?: 'light' | 'dark';
|
|
99
|
+
/** Custom icon renderer */
|
|
100
|
+
icon?: Snippet<[QuickLink]>;
|
|
101
|
+
/** Additional classes */
|
|
102
|
+
class?: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Props for DashboardLayout component
|
|
106
|
+
*/
|
|
107
|
+
export interface DashboardLayoutProps {
|
|
108
|
+
/** Navigation sections for sidebar */
|
|
109
|
+
navigation: NavSection[];
|
|
110
|
+
/** Current user data */
|
|
111
|
+
user?: User;
|
|
112
|
+
/** Application name */
|
|
113
|
+
appName?: string;
|
|
114
|
+
/** Page title displayed in header */
|
|
115
|
+
pageTitle?: string;
|
|
116
|
+
/** Back link shown at top of sidebar navigation */
|
|
117
|
+
backLink?: BackLink;
|
|
118
|
+
/** Sidebar visual variant */
|
|
119
|
+
sidebarVariant?: 'light' | 'dark';
|
|
120
|
+
/** Whether sidebar is collapsed by default */
|
|
121
|
+
sidebarCollapsed?: boolean;
|
|
122
|
+
/** Whether to show the header */
|
|
123
|
+
showHeader?: boolean;
|
|
124
|
+
/** Custom logo snippet */
|
|
125
|
+
logo?: Snippet;
|
|
126
|
+
/** Custom icon renderer for nav items */
|
|
127
|
+
icon?: Snippet<[NavItem]>;
|
|
128
|
+
/** Quick links displayed at bottom of sidebar */
|
|
129
|
+
quickLinks?: QuickLink[];
|
|
130
|
+
/** Quick links display mode */
|
|
131
|
+
quickLinksDisplay?: 'list' | 'icons';
|
|
132
|
+
/** Custom icon renderer for quick links */
|
|
133
|
+
quickLinkIcon?: Snippet<[QuickLink]>;
|
|
134
|
+
/** Custom content at start of header */
|
|
135
|
+
headerStart?: Snippet;
|
|
136
|
+
/** Custom content at end of header */
|
|
137
|
+
headerEnd?: Snippet;
|
|
138
|
+
/** Custom user menu snippet */
|
|
139
|
+
userMenu?: Snippet<[User]>;
|
|
140
|
+
/** Sidebar footer content */
|
|
141
|
+
sidebarFooter?: Snippet;
|
|
142
|
+
/** Main content */
|
|
143
|
+
children: Snippet;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Props for PublicLayout component
|
|
147
|
+
*/
|
|
148
|
+
export interface PublicLayoutProps {
|
|
149
|
+
/** Navigation items for header */
|
|
150
|
+
navigation?: NavItem[];
|
|
151
|
+
/** Whether to show footer */
|
|
152
|
+
showFooter?: boolean;
|
|
153
|
+
/** Footer link sections */
|
|
154
|
+
footerLinks?: NavSection[];
|
|
155
|
+
/** Footer copyright text */
|
|
156
|
+
copyright?: string;
|
|
157
|
+
/** Custom logo snippet */
|
|
158
|
+
logo?: Snippet;
|
|
159
|
+
/** Custom header end content */
|
|
160
|
+
headerEnd?: Snippet;
|
|
161
|
+
/** Main content */
|
|
162
|
+
children: Snippet;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Props for Sidebar component
|
|
166
|
+
*/
|
|
167
|
+
export interface SidebarProps {
|
|
168
|
+
/** Navigation sections */
|
|
169
|
+
navigation: NavSection[];
|
|
170
|
+
/** Visual variant - light (default) or dark */
|
|
171
|
+
variant?: 'light' | 'dark';
|
|
172
|
+
/** Whether sidebar is collapsed */
|
|
173
|
+
collapsed?: boolean;
|
|
174
|
+
/** Whether currently on mobile */
|
|
175
|
+
isMobile?: boolean;
|
|
176
|
+
/** Whether mobile sidebar is open */
|
|
177
|
+
mobileOpen?: boolean;
|
|
178
|
+
/** Callback when mobile sidebar should close */
|
|
179
|
+
onClose?: () => void;
|
|
180
|
+
/** Custom logo snippet */
|
|
181
|
+
logo?: Snippet;
|
|
182
|
+
/** Custom icon renderer for nav items */
|
|
183
|
+
icon?: Snippet<[NavItem]>;
|
|
184
|
+
/** Quick links displayed at bottom of sidebar */
|
|
185
|
+
quickLinks?: QuickLink[];
|
|
186
|
+
/** Quick links display mode */
|
|
187
|
+
quickLinksDisplay?: 'list' | 'icons';
|
|
188
|
+
/** Custom icon renderer for quick links */
|
|
189
|
+
quickLinkIcon?: Snippet<[QuickLink]>;
|
|
190
|
+
/** Custom footer content */
|
|
191
|
+
footer?: Snippet;
|
|
192
|
+
/** Use stronger/thicker border */
|
|
193
|
+
strongBorder?: boolean;
|
|
194
|
+
/** Additional classes */
|
|
195
|
+
class?: string;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Props for Header component
|
|
199
|
+
*/
|
|
200
|
+
export interface HeaderProps {
|
|
201
|
+
/** Whether to show hamburger menu button (mobile) */
|
|
202
|
+
showMenuButton?: boolean;
|
|
203
|
+
/** Callback when menu button is clicked */
|
|
204
|
+
onMenuClick?: () => void;
|
|
205
|
+
/** Custom start content (left side) */
|
|
206
|
+
start?: Snippet;
|
|
207
|
+
/** Custom end content (right side) */
|
|
208
|
+
end?: Snippet;
|
|
209
|
+
/** Additional classes */
|
|
210
|
+
class?: string;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Props for Footer component
|
|
214
|
+
*/
|
|
215
|
+
export interface FooterProps {
|
|
216
|
+
/** Link sections */
|
|
217
|
+
links?: NavSection[];
|
|
218
|
+
/** Copyright text */
|
|
219
|
+
copyright?: string;
|
|
220
|
+
/** Show logo in footer */
|
|
221
|
+
showLogo?: boolean;
|
|
222
|
+
/** Custom content */
|
|
223
|
+
children?: Snippet;
|
|
224
|
+
/** Additional classes */
|
|
225
|
+
class?: string;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Props for AppShell component
|
|
229
|
+
*/
|
|
230
|
+
export interface AppShellProps {
|
|
231
|
+
/** Skip link target ID */
|
|
232
|
+
skipToId?: string;
|
|
233
|
+
/** Skip link text */
|
|
234
|
+
skipToText?: string;
|
|
235
|
+
/** Main content */
|
|
236
|
+
children: Snippet;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Props for FormPageLayout component
|
|
240
|
+
*/
|
|
241
|
+
export interface FormPageLayoutProps {
|
|
242
|
+
/** Page title (h1) */
|
|
243
|
+
title: string;
|
|
244
|
+
/** Page description text */
|
|
245
|
+
description: string;
|
|
246
|
+
/** Footer help text (supports HTML) */
|
|
247
|
+
helpText?: string;
|
|
248
|
+
/** Show the notices section (renders notices snippet if provided) */
|
|
249
|
+
showNotices?: boolean;
|
|
250
|
+
/** Main form content */
|
|
251
|
+
children: Snippet;
|
|
252
|
+
/** Optional notices content (alert cards, etc.) */
|
|
253
|
+
notices?: Snippet;
|
|
254
|
+
/** Optional sidebar content */
|
|
255
|
+
sidebar?: Snippet;
|
|
256
|
+
/** Additional classes for the container */
|
|
257
|
+
class?: string;
|
|
258
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { clsx } from 'clsx';
|
|
2
|
+
import { twMerge } from 'tailwind-merge';
|
|
3
|
+
/**
|
|
4
|
+
* Utility function to merge Tailwind CSS classes
|
|
5
|
+
* Uses clsx for conditional classes and tailwind-merge to handle conflicts
|
|
6
|
+
*/
|
|
7
|
+
export function cn(...inputs) {
|
|
8
|
+
return twMerge(clsx(inputs));
|
|
9
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime validation utilities for Classic Theme components
|
|
3
|
+
*
|
|
4
|
+
* These utilities provide development-time warnings for common prop mistakes.
|
|
5
|
+
* They are designed to be tree-shaken in production builds.
|
|
6
|
+
*/
|
|
7
|
+
export interface ValidationResult {
|
|
8
|
+
valid: boolean;
|
|
9
|
+
message?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Validate that a required array prop is non-empty
|
|
13
|
+
*
|
|
14
|
+
* @param value - The array to validate
|
|
15
|
+
* @param propName - Name of the prop for error messages
|
|
16
|
+
* @param componentName - Name of the component for error messages
|
|
17
|
+
* @returns Validation result with valid status and optional message
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* $effect(() => {
|
|
22
|
+
* validateNonEmptyArray(options, 'options', 'Select');
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function validateNonEmptyArray<T>(value: T[] | undefined, propName: string, componentName: string): ValidationResult;
|
|
27
|
+
/**
|
|
28
|
+
* Validate that a required prop is defined
|
|
29
|
+
*
|
|
30
|
+
* @param value - The value to validate
|
|
31
|
+
* @param propName - Name of the prop for error messages
|
|
32
|
+
* @param componentName - Name of the component for error messages
|
|
33
|
+
* @returns Validation result with valid status and optional message
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* validateRequired(id, 'id', 'FormField');
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function validateRequired<T>(value: T | undefined | null, propName: string, componentName: string): ValidationResult;
|
|
41
|
+
/**
|
|
42
|
+
* Validate prop is one of allowed values
|
|
43
|
+
*
|
|
44
|
+
* @param value - The value to validate
|
|
45
|
+
* @param allowedValues - Array of allowed values
|
|
46
|
+
* @param propName - Name of the prop for error messages
|
|
47
|
+
* @param componentName - Name of the component for error messages
|
|
48
|
+
* @returns Validation result with valid status and optional message
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* validateOneOf(variant, ['default', 'destructive', 'outline'], 'variant', 'Button');
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function validateOneOf<T>(value: T, allowedValues: readonly T[], propName: string, componentName: string): ValidationResult;
|
|
56
|
+
/**
|
|
57
|
+
* Validate a number is within a range
|
|
58
|
+
*
|
|
59
|
+
* @param value - The number to validate
|
|
60
|
+
* @param min - Minimum allowed value (inclusive)
|
|
61
|
+
* @param max - Maximum allowed value (inclusive)
|
|
62
|
+
* @param propName - Name of the prop for error messages
|
|
63
|
+
* @param componentName - Name of the component for error messages
|
|
64
|
+
* @returns Validation result with valid status and optional message
|
|
65
|
+
*/
|
|
66
|
+
export declare function validateRange(value: number, min: number, max: number, propName: string, componentName: string): ValidationResult;
|
|
67
|
+
/**
|
|
68
|
+
* Batch validate multiple conditions
|
|
69
|
+
*
|
|
70
|
+
* @param validations - Array of validation results
|
|
71
|
+
* @returns true if all validations pass, false otherwise
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* const isValid = validateProps([
|
|
76
|
+
* validateRequired(id, 'id', 'Component'),
|
|
77
|
+
* validateNonEmptyArray(items, 'items', 'Component'),
|
|
78
|
+
* ]);
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export declare function validateProps(validations: ValidationResult[]): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Create a validator function for a specific component
|
|
84
|
+
* Useful for creating reusable validation in component files
|
|
85
|
+
*
|
|
86
|
+
* @param componentName - Name of the component
|
|
87
|
+
* @returns Object with bound validation functions
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* const validate = createValidator('Select');
|
|
92
|
+
* validate.nonEmptyArray(options, 'options');
|
|
93
|
+
* validate.required(value, 'value');
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export declare function createValidator(componentName: string): {
|
|
97
|
+
nonEmptyArray: <T>(value: T[] | undefined, propName: string) => ValidationResult;
|
|
98
|
+
required: <T>(value: T | undefined | null, propName: string) => ValidationResult;
|
|
99
|
+
oneOf: <T>(value: T, allowedValues: readonly T[], propName: string) => ValidationResult;
|
|
100
|
+
range: (value: number, min: number, max: number, propName: string) => ValidationResult;
|
|
101
|
+
};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime validation utilities for Classic Theme components
|
|
3
|
+
*
|
|
4
|
+
* These utilities provide development-time warnings for common prop mistakes.
|
|
5
|
+
* They are designed to be tree-shaken in production builds.
|
|
6
|
+
*/
|
|
7
|
+
// Detect development mode in a way that works across different environments
|
|
8
|
+
const getIsDev = () => {
|
|
9
|
+
try {
|
|
10
|
+
// Vite/SvelteKit environment
|
|
11
|
+
// @ts-expect-error - import.meta.env may not exist in all environments
|
|
12
|
+
if (typeof import.meta !== 'undefined' && import.meta.env?.DEV !== undefined) {
|
|
13
|
+
// @ts-expect-error - accessing env.DEV
|
|
14
|
+
return import.meta.env.DEV === true;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// Ignore errors from import.meta access
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
// Node.js environment
|
|
22
|
+
// @ts-expect-error - process may not exist in browser
|
|
23
|
+
if (typeof process !== 'undefined' && process.env?.NODE_ENV !== undefined) {
|
|
24
|
+
// @ts-expect-error - accessing process.env
|
|
25
|
+
return process.env.NODE_ENV !== 'production';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Ignore errors from process access
|
|
30
|
+
}
|
|
31
|
+
// Default to false (production mode) if we can't determine
|
|
32
|
+
return false;
|
|
33
|
+
};
|
|
34
|
+
const isDev = getIsDev();
|
|
35
|
+
/**
|
|
36
|
+
* Validate that a required array prop is non-empty
|
|
37
|
+
*
|
|
38
|
+
* @param value - The array to validate
|
|
39
|
+
* @param propName - Name of the prop for error messages
|
|
40
|
+
* @param componentName - Name of the component for error messages
|
|
41
|
+
* @returns Validation result with valid status and optional message
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* $effect(() => {
|
|
46
|
+
* validateNonEmptyArray(options, 'options', 'Select');
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export function validateNonEmptyArray(value, propName, componentName) {
|
|
51
|
+
if (!isDev)
|
|
52
|
+
return { valid: true };
|
|
53
|
+
if (!value || !Array.isArray(value)) {
|
|
54
|
+
const message = `[${componentName}] "${propName}" is required and must be an array`;
|
|
55
|
+
console.warn(message);
|
|
56
|
+
return { valid: false, message };
|
|
57
|
+
}
|
|
58
|
+
if (value.length === 0) {
|
|
59
|
+
const message = `[${componentName}] "${propName}" should not be empty`;
|
|
60
|
+
console.warn(message);
|
|
61
|
+
return { valid: false, message };
|
|
62
|
+
}
|
|
63
|
+
return { valid: true };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Validate that a required prop is defined
|
|
67
|
+
*
|
|
68
|
+
* @param value - The value to validate
|
|
69
|
+
* @param propName - Name of the prop for error messages
|
|
70
|
+
* @param componentName - Name of the component for error messages
|
|
71
|
+
* @returns Validation result with valid status and optional message
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* validateRequired(id, 'id', 'FormField');
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export function validateRequired(value, propName, componentName) {
|
|
79
|
+
if (!isDev)
|
|
80
|
+
return { valid: true };
|
|
81
|
+
if (value === undefined || value === null) {
|
|
82
|
+
const message = `[${componentName}] "${propName}" is required`;
|
|
83
|
+
console.warn(message);
|
|
84
|
+
return { valid: false, message };
|
|
85
|
+
}
|
|
86
|
+
return { valid: true };
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Validate prop is one of allowed values
|
|
90
|
+
*
|
|
91
|
+
* @param value - The value to validate
|
|
92
|
+
* @param allowedValues - Array of allowed values
|
|
93
|
+
* @param propName - Name of the prop for error messages
|
|
94
|
+
* @param componentName - Name of the component for error messages
|
|
95
|
+
* @returns Validation result with valid status and optional message
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* validateOneOf(variant, ['default', 'destructive', 'outline'], 'variant', 'Button');
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export function validateOneOf(value, allowedValues, propName, componentName) {
|
|
103
|
+
if (!isDev)
|
|
104
|
+
return { valid: true };
|
|
105
|
+
if (!allowedValues.includes(value)) {
|
|
106
|
+
const message = `[${componentName}] "${propName}" must be one of: ${allowedValues.join(', ')}. Got: ${String(value)}`;
|
|
107
|
+
console.warn(message);
|
|
108
|
+
return { valid: false, message };
|
|
109
|
+
}
|
|
110
|
+
return { valid: true };
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Validate a number is within a range
|
|
114
|
+
*
|
|
115
|
+
* @param value - The number to validate
|
|
116
|
+
* @param min - Minimum allowed value (inclusive)
|
|
117
|
+
* @param max - Maximum allowed value (inclusive)
|
|
118
|
+
* @param propName - Name of the prop for error messages
|
|
119
|
+
* @param componentName - Name of the component for error messages
|
|
120
|
+
* @returns Validation result with valid status and optional message
|
|
121
|
+
*/
|
|
122
|
+
export function validateRange(value, min, max, propName, componentName) {
|
|
123
|
+
if (!isDev)
|
|
124
|
+
return { valid: true };
|
|
125
|
+
if (value < min || value > max) {
|
|
126
|
+
const message = `[${componentName}] "${propName}" must be between ${min} and ${max}. Got: ${value}`;
|
|
127
|
+
console.warn(message);
|
|
128
|
+
return { valid: false, message };
|
|
129
|
+
}
|
|
130
|
+
return { valid: true };
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Batch validate multiple conditions
|
|
134
|
+
*
|
|
135
|
+
* @param validations - Array of validation results
|
|
136
|
+
* @returns true if all validations pass, false otherwise
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts
|
|
140
|
+
* const isValid = validateProps([
|
|
141
|
+
* validateRequired(id, 'id', 'Component'),
|
|
142
|
+
* validateNonEmptyArray(items, 'items', 'Component'),
|
|
143
|
+
* ]);
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
export function validateProps(validations) {
|
|
147
|
+
return validations.every((v) => v.valid);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Create a validator function for a specific component
|
|
151
|
+
* Useful for creating reusable validation in component files
|
|
152
|
+
*
|
|
153
|
+
* @param componentName - Name of the component
|
|
154
|
+
* @returns Object with bound validation functions
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```ts
|
|
158
|
+
* const validate = createValidator('Select');
|
|
159
|
+
* validate.nonEmptyArray(options, 'options');
|
|
160
|
+
* validate.required(value, 'value');
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
export function createValidator(componentName) {
|
|
164
|
+
return {
|
|
165
|
+
nonEmptyArray: (value, propName) => validateNonEmptyArray(value, propName, componentName),
|
|
166
|
+
required: (value, propName) => validateRequired(value, propName, componentName),
|
|
167
|
+
oneOf: (value, allowedValues, propName) => validateOneOf(value, allowedValues, propName, componentName),
|
|
168
|
+
range: (value, min, max, propName) => validateRange(value, min, max, propName, componentName),
|
|
169
|
+
};
|
|
170
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@classic-homes/theme-svelte",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Svelte components for the Classic theme system",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"svelte": "./dist/lib/index.js",
|
|
7
|
+
"types": "./dist/lib/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/lib/index.d.ts",
|
|
11
|
+
"svelte": "./dist/lib/index.js",
|
|
12
|
+
"default": "./dist/lib/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./styles.css": "./dist/styles.css"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "svelte-package -i src",
|
|
21
|
+
"dev": "svelte-package -i src --watch",
|
|
22
|
+
"clean": "rm -rf dist",
|
|
23
|
+
"typecheck": "svelte-check --tsconfig ./tsconfig.json"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"svelte",
|
|
27
|
+
"components",
|
|
28
|
+
"design-system"
|
|
29
|
+
],
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/Classic-Homes/classic-theme.git",
|
|
34
|
+
"directory": "packages/svelte"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"svelte": "^5.0.0"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@classic-homes/theme-tokens": "*",
|
|
44
|
+
"bits-ui": "^1.0.0",
|
|
45
|
+
"clsx": "^2.1.0",
|
|
46
|
+
"tailwind-merge": "^2.2.0",
|
|
47
|
+
"tailwind-variants": "^0.3.0",
|
|
48
|
+
"zod": "^3.23.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@sveltejs/package": "^2.3.0",
|
|
52
|
+
"svelte": "^5.0.0",
|
|
53
|
+
"svelte-check": "^4.0.0",
|
|
54
|
+
"typescript": "^5.3.0"
|
|
55
|
+
}
|
|
56
|
+
}
|