@classic-homes/theme-svelte 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/components/Alert.svelte.d.ts +1 -1
- package/dist/lib/components/Checkbox.svelte +66 -27
- package/dist/lib/components/Checkbox.svelte.d.ts +3 -0
- package/dist/lib/components/DateTimePicker.svelte +55 -38
- package/dist/lib/components/DateTimePicker.svelte.d.ts +2 -0
- package/dist/lib/components/Toast.svelte.d.ts +1 -1
- package/dist/lib/components/layout/FormPageLayout.svelte +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
declare const Alert: import("svelte").Component<{
|
|
3
3
|
[key: string]: unknown;
|
|
4
|
-
variant?: "default" | "destructive" | "
|
|
4
|
+
variant?: "default" | "destructive" | "error" | "warning" | "success" | "info" | undefined;
|
|
5
5
|
class?: string;
|
|
6
6
|
children: Snippet;
|
|
7
7
|
}, {}, "">;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
2
3
|
import { Checkbox as CheckboxPrimitive } from 'bits-ui';
|
|
3
4
|
import { cn } from '../utils.js';
|
|
4
5
|
|
|
@@ -10,7 +11,9 @@
|
|
|
10
11
|
value?: string;
|
|
11
12
|
id?: string;
|
|
12
13
|
class?: string;
|
|
14
|
+
error?: string;
|
|
13
15
|
onCheckedChange?: (checked: boolean) => void;
|
|
16
|
+
children?: Snippet;
|
|
14
17
|
[key: string]: unknown;
|
|
15
18
|
}
|
|
16
19
|
|
|
@@ -22,44 +25,80 @@
|
|
|
22
25
|
value,
|
|
23
26
|
id,
|
|
24
27
|
class: className,
|
|
28
|
+
error,
|
|
25
29
|
onCheckedChange,
|
|
30
|
+
children,
|
|
26
31
|
...restProps
|
|
27
32
|
}: Props = $props();
|
|
28
33
|
|
|
34
|
+
// Generate a stable unique ID if none provided (for accessibility)
|
|
35
|
+
const generatedId = `checkbox-${Math.random().toString(36).substring(2, 11)}`;
|
|
36
|
+
const effectiveId = $derived(id ?? generatedId);
|
|
37
|
+
|
|
29
38
|
function handleCheckedChange(newChecked: boolean | 'indeterminate') {
|
|
30
39
|
if (typeof newChecked === 'boolean') {
|
|
31
40
|
checked = newChecked;
|
|
32
41
|
onCheckedChange?.(newChecked);
|
|
33
42
|
}
|
|
34
43
|
}
|
|
44
|
+
|
|
45
|
+
const hasError = $derived(!!error);
|
|
46
|
+
const errorId = $derived(`${effectiveId}-error`);
|
|
35
47
|
</script>
|
|
36
48
|
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
class="h-5 w-5"
|
|
56
|
-
fill="none"
|
|
57
|
-
stroke="currentColor"
|
|
58
|
-
stroke-width="2"
|
|
59
|
-
viewBox="0 0 24 24"
|
|
60
|
-
aria-hidden="true"
|
|
49
|
+
<div class="space-y-1">
|
|
50
|
+
<div class="flex items-start gap-3">
|
|
51
|
+
<CheckboxPrimitive.Root
|
|
52
|
+
bind:checked
|
|
53
|
+
{disabled}
|
|
54
|
+
{required}
|
|
55
|
+
{name}
|
|
56
|
+
{value}
|
|
57
|
+
id={effectiveId}
|
|
58
|
+
onCheckedChange={handleCheckedChange}
|
|
59
|
+
aria-invalid={hasError ? 'true' : undefined}
|
|
60
|
+
aria-describedby={hasError ? errorId : undefined}
|
|
61
|
+
class={cn(
|
|
62
|
+
'peer relative h-5 w-5 shrink-0 rounded-sm border ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground flex items-center justify-center mt-0.5',
|
|
63
|
+
hasError ? 'border-destructive' : 'border-primary',
|
|
64
|
+
className
|
|
65
|
+
)}
|
|
66
|
+
{...restProps}
|
|
61
67
|
>
|
|
62
|
-
|
|
63
|
-
|
|
68
|
+
<!-- Touch target expansion (invisible, extends clickable area to 44x44px) -->
|
|
69
|
+
<span class="absolute inset-0 -m-3" aria-hidden="true"></span>
|
|
70
|
+
{#if checked}
|
|
71
|
+
<svg
|
|
72
|
+
class="h-5 w-5"
|
|
73
|
+
fill="none"
|
|
74
|
+
stroke="currentColor"
|
|
75
|
+
stroke-width="2"
|
|
76
|
+
viewBox="0 0 24 24"
|
|
77
|
+
aria-hidden="true"
|
|
78
|
+
>
|
|
79
|
+
<polyline points="20 6 9 17 4 12" />
|
|
80
|
+
</svg>
|
|
81
|
+
{/if}
|
|
82
|
+
</CheckboxPrimitive.Root>
|
|
83
|
+
{#if children}
|
|
84
|
+
<label
|
|
85
|
+
for={effectiveId}
|
|
86
|
+
class={cn(
|
|
87
|
+
'text-sm leading-relaxed cursor-pointer select-none',
|
|
88
|
+
disabled && 'cursor-not-allowed opacity-50'
|
|
89
|
+
)}
|
|
90
|
+
>
|
|
91
|
+
{@render children()}
|
|
92
|
+
{#if required}
|
|
93
|
+
<span class="text-destructive ml-1" aria-hidden="true">*</span>
|
|
94
|
+
<span class="sr-only">(required)</span>
|
|
95
|
+
{/if}
|
|
96
|
+
</label>
|
|
97
|
+
{/if}
|
|
98
|
+
</div>
|
|
99
|
+
{#if error}
|
|
100
|
+
<p id={errorId} class="text-sm text-destructive ml-8" role="alert" aria-live="polite">
|
|
101
|
+
{error}
|
|
102
|
+
</p>
|
|
64
103
|
{/if}
|
|
65
|
-
</
|
|
104
|
+
</div>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
1
2
|
interface Props {
|
|
2
3
|
checked?: boolean;
|
|
3
4
|
disabled?: boolean;
|
|
@@ -6,7 +7,9 @@ interface Props {
|
|
|
6
7
|
value?: string;
|
|
7
8
|
id?: string;
|
|
8
9
|
class?: string;
|
|
10
|
+
error?: string;
|
|
9
11
|
onCheckedChange?: (checked: boolean) => void;
|
|
12
|
+
children?: Snippet;
|
|
10
13
|
[key: string]: unknown;
|
|
11
14
|
}
|
|
12
15
|
declare const Checkbox: import("svelte").Component<Props, {}, "checked">;
|
|
@@ -29,12 +29,14 @@
|
|
|
29
29
|
error?: string;
|
|
30
30
|
/** Additional class for the trigger */
|
|
31
31
|
class?: string;
|
|
32
|
+
/** Show only date picker without time selection */
|
|
33
|
+
dateOnly?: boolean;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
let {
|
|
35
37
|
value = $bindable(null),
|
|
36
38
|
onValueChange,
|
|
37
|
-
placeholder
|
|
39
|
+
placeholder,
|
|
38
40
|
disabled = false,
|
|
39
41
|
required = false,
|
|
40
42
|
min,
|
|
@@ -45,8 +47,13 @@
|
|
|
45
47
|
id,
|
|
46
48
|
error,
|
|
47
49
|
class: className,
|
|
50
|
+
dateOnly = false,
|
|
48
51
|
}: Props = $props();
|
|
49
52
|
|
|
53
|
+
// Set default placeholder based on dateOnly mode
|
|
54
|
+
const defaultPlaceholder = $derived(dateOnly ? 'Select date...' : 'Select date and time...');
|
|
55
|
+
const effectivePlaceholder = $derived(placeholder ?? defaultPlaceholder);
|
|
56
|
+
|
|
50
57
|
let open = $state(false);
|
|
51
58
|
|
|
52
59
|
// Calendar state - initialize with current date
|
|
@@ -77,6 +84,9 @@
|
|
|
77
84
|
day: 'numeric',
|
|
78
85
|
year: 'numeric',
|
|
79
86
|
});
|
|
87
|
+
if (dateOnly) {
|
|
88
|
+
return dateStr;
|
|
89
|
+
}
|
|
80
90
|
const timeStr = value.toLocaleTimeString('en-US', {
|
|
81
91
|
hour: 'numeric',
|
|
82
92
|
minute: '2-digit',
|
|
@@ -139,12 +149,17 @@
|
|
|
139
149
|
|
|
140
150
|
const newDate = new Date(viewYear, viewMonth, day);
|
|
141
151
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
152
|
+
if (dateOnly) {
|
|
153
|
+
// Set to midnight for date-only mode
|
|
154
|
+
newDate.setHours(0, 0, 0, 0);
|
|
155
|
+
} else {
|
|
156
|
+
// Preserve or set time
|
|
157
|
+
let h = hour;
|
|
158
|
+
if (timeFormat === '12h') {
|
|
159
|
+
h = period === 'PM' ? (hour === 12 ? 12 : hour + 12) : hour === 12 ? 0 : hour;
|
|
160
|
+
}
|
|
161
|
+
newDate.setHours(h, minute, 0, 0);
|
|
146
162
|
}
|
|
147
|
-
newDate.setHours(h, minute, 0, 0);
|
|
148
163
|
|
|
149
164
|
value = newDate;
|
|
150
165
|
onValueChange?.(newDate);
|
|
@@ -227,7 +242,7 @@
|
|
|
227
242
|
aria-invalid={error ? 'true' : undefined}
|
|
228
243
|
>
|
|
229
244
|
<span class="truncate">
|
|
230
|
-
{displayValue ||
|
|
245
|
+
{displayValue || effectivePlaceholder}
|
|
231
246
|
</span>
|
|
232
247
|
<svg
|
|
233
248
|
xmlns="http://www.w3.org/2000/svg"
|
|
@@ -344,43 +359,45 @@
|
|
|
344
359
|
{/each}
|
|
345
360
|
</div>
|
|
346
361
|
|
|
347
|
-
<!-- Time selector -->
|
|
348
|
-
|
|
349
|
-
<div class="
|
|
350
|
-
<
|
|
351
|
-
|
|
352
|
-
bind:value={hour}
|
|
353
|
-
onchange={updateTime}
|
|
354
|
-
class="h-8 rounded-md border border-input bg-background px-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
355
|
-
>
|
|
356
|
-
{#each hourOptions as h}
|
|
357
|
-
<option value={timeFormat === '12h' ? h : h}>
|
|
358
|
-
{String(h).padStart(2, '0')}
|
|
359
|
-
</option>
|
|
360
|
-
{/each}
|
|
361
|
-
</select>
|
|
362
|
-
<span class="text-muted-foreground">:</span>
|
|
363
|
-
<select
|
|
364
|
-
bind:value={minute}
|
|
365
|
-
onchange={updateTime}
|
|
366
|
-
class="h-8 rounded-md border border-input bg-background px-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
367
|
-
>
|
|
368
|
-
{#each minuteOptions as m}
|
|
369
|
-
<option value={m}>{String(m).padStart(2, '0')}</option>
|
|
370
|
-
{/each}
|
|
371
|
-
</select>
|
|
372
|
-
{#if timeFormat === '12h'}
|
|
362
|
+
<!-- Time selector (hidden in dateOnly mode) -->
|
|
363
|
+
{#if !dateOnly}
|
|
364
|
+
<div class="border-t mt-3 pt-3">
|
|
365
|
+
<div class="flex items-center gap-2">
|
|
366
|
+
<span class="text-sm text-muted-foreground">Time:</span>
|
|
373
367
|
<select
|
|
374
|
-
bind:value={
|
|
368
|
+
bind:value={hour}
|
|
375
369
|
onchange={updateTime}
|
|
376
370
|
class="h-8 rounded-md border border-input bg-background px-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
377
371
|
>
|
|
378
|
-
|
|
379
|
-
|
|
372
|
+
{#each hourOptions as h}
|
|
373
|
+
<option value={timeFormat === '12h' ? h : h}>
|
|
374
|
+
{String(h).padStart(2, '0')}
|
|
375
|
+
</option>
|
|
376
|
+
{/each}
|
|
380
377
|
</select>
|
|
381
|
-
|
|
378
|
+
<span class="text-muted-foreground">:</span>
|
|
379
|
+
<select
|
|
380
|
+
bind:value={minute}
|
|
381
|
+
onchange={updateTime}
|
|
382
|
+
class="h-8 rounded-md border border-input bg-background px-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
383
|
+
>
|
|
384
|
+
{#each minuteOptions as m}
|
|
385
|
+
<option value={m}>{String(m).padStart(2, '0')}</option>
|
|
386
|
+
{/each}
|
|
387
|
+
</select>
|
|
388
|
+
{#if timeFormat === '12h'}
|
|
389
|
+
<select
|
|
390
|
+
bind:value={period}
|
|
391
|
+
onchange={updateTime}
|
|
392
|
+
class="h-8 rounded-md border border-input bg-background px-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
393
|
+
>
|
|
394
|
+
<option value="AM">AM</option>
|
|
395
|
+
<option value="PM">PM</option>
|
|
396
|
+
</select>
|
|
397
|
+
{/if}
|
|
398
|
+
</div>
|
|
382
399
|
</div>
|
|
383
|
-
|
|
400
|
+
{/if}
|
|
384
401
|
|
|
385
402
|
<!-- Actions -->
|
|
386
403
|
<div class="border-t mt-3 pt-3 flex justify-between">
|
|
@@ -25,6 +25,8 @@ interface Props {
|
|
|
25
25
|
error?: string;
|
|
26
26
|
/** Additional class for the trigger */
|
|
27
27
|
class?: string;
|
|
28
|
+
/** Show only date picker without time selection */
|
|
29
|
+
dateOnly?: boolean;
|
|
28
30
|
}
|
|
29
31
|
declare const DateTimePicker: import("svelte").Component<Props, {}, "value">;
|
|
30
32
|
type DateTimePicker = ReturnType<typeof DateTimePicker>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
declare const Toast: import("svelte").Component<{
|
|
3
3
|
[key: string]: unknown;
|
|
4
|
-
type?: "
|
|
4
|
+
type?: "error" | "warning" | "success" | "info" | undefined;
|
|
5
5
|
title?: string;
|
|
6
6
|
message: string;
|
|
7
7
|
class?: string;
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
<div class={cn('gap-8', hasSidebar ? 'grid lg:grid-cols-[1fr,320px]' : 'mx-auto max-w-2xl')}>
|
|
68
68
|
<!-- Sidebar (shows first on mobile when present) -->
|
|
69
69
|
{#if hasSidebar}
|
|
70
|
-
<aside class="order-first lg:order-last">
|
|
70
|
+
<aside class="order-first lg:order-last space-y-6">
|
|
71
71
|
{@render sidebar!()}
|
|
72
72
|
</aside>
|
|
73
73
|
{/if}
|