@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,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,7 @@
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
+ export {};
@@ -0,0 +1,6 @@
1
+ import { type ClassValue } from 'clsx';
2
+ /**
3
+ * Utility function to merge Tailwind CSS classes
4
+ * Uses clsx for conditional classes and tailwind-merge to handle conflicts
5
+ */
6
+ export declare function cn(...inputs: ClassValue[]): string;
@@ -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
+ }