@classic-homes/theme-svelte 0.1.53 → 0.1.55
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/__tests__/FileUpload.test.js +45 -0
- package/dist/lib/components/AlertDialog.svelte +1 -1
- package/dist/lib/components/AlertDialog.svelte.d.ts +2 -2
- package/dist/lib/components/Badge.svelte +28 -11
- package/dist/lib/components/Badge.svelte.d.ts +2 -0
- package/dist/lib/components/Button.svelte +12 -1
- package/dist/lib/components/Combobox.svelte +2 -2
- package/dist/lib/components/DataPanelHeader.svelte +4 -4
- package/dist/lib/components/DataPanelTab.svelte +1 -1
- package/dist/lib/components/DataTable.svelte +7 -7
- package/dist/lib/components/DataTable.svelte.d.ts +5 -5
- package/dist/lib/components/DateTimePicker.svelte +3 -3
- package/dist/lib/components/Dialog.svelte +5 -5
- package/dist/lib/components/DropdownMenu.svelte +2 -2
- package/dist/lib/components/FileUpload.svelte +35 -1
- package/dist/lib/components/FileUpload.svelte.d.ts +6 -0
- package/dist/lib/components/HeaderSearch.svelte +9 -9
- package/dist/lib/components/Input.svelte +13 -0
- package/dist/lib/components/MultiSelect.svelte +2 -2
- package/dist/lib/components/NumberInput.svelte +4 -4
- package/dist/lib/components/PasswordInput.svelte +12 -0
- package/dist/lib/components/PasswordInput.svelte.d.ts +4 -0
- package/dist/lib/components/RadioGroup.svelte +5 -5
- package/dist/lib/components/Select.svelte +13 -1
- package/dist/lib/components/Signature.svelte +5 -5
- package/dist/lib/components/Tabs.svelte +1 -1
- package/dist/lib/components/Toast.svelte +1 -1
- package/dist/lib/components/layout/AuthLayout.svelte +1 -1
- package/dist/lib/components/layout/DashboardLayout.svelte +1 -1
- package/dist/lib/components/layout/Footer.svelte +1 -1
- package/dist/lib/components/layout/PublicLayout.svelte +2 -2
- package/dist/lib/components/layout/QuickLinks.svelte +3 -3
- package/dist/lib/components/layout/Sidebar.svelte +1 -1
- package/dist/lib/components/layout/sidebar/SidebarFlyout.svelte +1 -2
- package/dist/lib/components/layout/sidebar/SidebarNavItem.svelte +20 -21
- package/dist/lib/components/layout/sidebar/SidebarSection.svelte +2 -2
- package/dist/lib/components/notifications/NotificationFilters.svelte +1 -1
- package/dist/lib/components/notifications/NotificationItem.svelte +1 -1
- package/dist/lib/utils.d.ts +4 -2
- package/dist/lib/utils.js +5 -11
- package/package.json +3 -3
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { render, screen } from '@testing-library/svelte';
|
|
3
|
+
import FileUpload from '../lib/components/FileUpload.svelte';
|
|
4
|
+
describe('FileUpload accepted-types label', () => {
|
|
5
|
+
it('derives a friendly label from MIME types instead of showing raw values', () => {
|
|
6
|
+
render(FileUpload, {
|
|
7
|
+
props: {
|
|
8
|
+
label: 'Upload files',
|
|
9
|
+
acceptedTypes: [
|
|
10
|
+
'image/jpeg',
|
|
11
|
+
'image/png',
|
|
12
|
+
'application/pdf',
|
|
13
|
+
'video/mp4',
|
|
14
|
+
'video/quicktime',
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
expect(screen.getByText(/Images, PDF, Videos \(max/)).toBeInTheDocument();
|
|
19
|
+
// Raw MIME types should not be shown
|
|
20
|
+
expect(screen.queryByText(/image\/jpeg/)).not.toBeInTheDocument();
|
|
21
|
+
});
|
|
22
|
+
it('uses the default accepted types (Images, PDF) when none are provided', () => {
|
|
23
|
+
render(FileUpload, { props: { label: 'Upload files' } });
|
|
24
|
+
expect(screen.getByText(/^Images, PDF \(max/)).toBeInTheDocument();
|
|
25
|
+
});
|
|
26
|
+
it('respects an explicit typesLabel override', () => {
|
|
27
|
+
render(FileUpload, {
|
|
28
|
+
props: {
|
|
29
|
+
label: 'Upload files',
|
|
30
|
+
acceptedTypes: ['image/png', 'video/mp4'],
|
|
31
|
+
typesLabel: 'Photos and clips',
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
expect(screen.getByText(/^Photos and clips \(max/)).toBeInTheDocument();
|
|
35
|
+
});
|
|
36
|
+
it('falls back to the uppercased subtype for unmapped types', () => {
|
|
37
|
+
render(FileUpload, {
|
|
38
|
+
props: {
|
|
39
|
+
label: 'Upload files',
|
|
40
|
+
acceptedTypes: ['application/zip'],
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
expect(screen.getByText(/^ZIP \(max/)).toBeInTheDocument();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { tv, type VariantProps } from 'tailwind-variants';
|
|
6
6
|
|
|
7
7
|
const buttonVariants = tv({
|
|
8
|
-
base: 'inline-flex items-center justify-center rounded-md text-sm font-medium
|
|
8
|
+
base: 'inline-flex items-center justify-center rounded-md text-sm font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
|
9
9
|
variants: {
|
|
10
10
|
variant: {
|
|
11
11
|
default: 'bg-primary text-primary-foreground hover:bg-primary-hover',
|
|
@@ -21,7 +21,7 @@ declare const AlertDialog: import("svelte").Component<{
|
|
|
21
21
|
outline: string;
|
|
22
22
|
ghost: string;
|
|
23
23
|
};
|
|
24
|
-
}, undefined, "inline-flex items-center justify-center rounded-md text-sm font-medium
|
|
24
|
+
}, undefined, "inline-flex items-center justify-center rounded-md text-sm font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", import("tailwind-variants/dist/config.js").TVConfig<{
|
|
25
25
|
variant: {
|
|
26
26
|
default: string;
|
|
27
27
|
destructive: string;
|
|
@@ -49,7 +49,7 @@ declare const AlertDialog: import("svelte").Component<{
|
|
|
49
49
|
outline: string;
|
|
50
50
|
ghost: string;
|
|
51
51
|
};
|
|
52
|
-
}, undefined, "inline-flex items-center justify-center rounded-md text-sm font-medium
|
|
52
|
+
}, undefined, "inline-flex items-center justify-center rounded-md text-sm font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", import("tailwind-variants/dist/config.js").TVConfig<{
|
|
53
53
|
variant: {
|
|
54
54
|
default: string;
|
|
55
55
|
destructive: string;
|
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
import { tv, type VariantProps } from 'tailwind-variants';
|
|
5
5
|
|
|
6
6
|
const badgeVariants = tv({
|
|
7
|
-
base: 'inline-flex items-center justify-center rounded-md border font-medium w-fit whitespace-nowrap shrink-0 gap-1
|
|
7
|
+
base: 'inline-flex items-center justify-center rounded-md border font-medium w-fit whitespace-nowrap shrink-0 gap-1',
|
|
8
8
|
variants: {
|
|
9
9
|
variant: {
|
|
10
10
|
default: 'border-transparent bg-primary text-primary-foreground',
|
|
11
11
|
secondary: 'border-transparent bg-secondary text-secondary-foreground',
|
|
12
|
-
destructive: 'border-transparent bg-destructive text-
|
|
12
|
+
destructive: 'border-transparent bg-destructive text-destructive-foreground',
|
|
13
13
|
outline: 'text-foreground',
|
|
14
|
-
success: 'border-transparent bg-
|
|
15
|
-
warning: 'border-transparent bg-
|
|
16
|
-
info: 'border-transparent bg-
|
|
14
|
+
success: 'border-transparent bg-success text-success-foreground',
|
|
15
|
+
warning: 'border-transparent bg-warning text-warning-foreground',
|
|
16
|
+
info: 'border-transparent bg-info text-info-foreground',
|
|
17
17
|
},
|
|
18
18
|
size: {
|
|
19
19
|
default: 'px-2 py-0.5 text-xs',
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
dot: 'h-5 w-5 p-0 text-xs',
|
|
22
22
|
},
|
|
23
23
|
clickable: {
|
|
24
|
-
true: 'cursor-pointer',
|
|
24
|
+
true: 'cursor-pointer focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
|
25
25
|
false: '',
|
|
26
26
|
},
|
|
27
27
|
},
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
clickable: true,
|
|
35
35
|
class: 'hover:bg-accent-hover hover:text-accent-foreground',
|
|
36
36
|
},
|
|
37
|
-
{ variant: 'success', clickable: true, class: 'hover:bg-
|
|
38
|
-
{ variant: 'warning', clickable: true, class: 'hover:bg-
|
|
39
|
-
{ variant: 'info', clickable: true, class: 'hover:bg-
|
|
37
|
+
{ variant: 'success', clickable: true, class: 'hover:bg-success/80' },
|
|
38
|
+
{ variant: 'warning', clickable: true, class: 'hover:bg-warning/80' },
|
|
39
|
+
{ variant: 'info', clickable: true, class: 'hover:bg-info/80' },
|
|
40
40
|
],
|
|
41
41
|
defaultVariants: {
|
|
42
42
|
variant: 'default',
|
|
@@ -51,6 +51,8 @@
|
|
|
51
51
|
variant?: BadgeVariants['variant'];
|
|
52
52
|
size?: BadgeVariants['size'];
|
|
53
53
|
clickable?: boolean;
|
|
54
|
+
/** When true, adds role="status" and aria-live="polite" for dynamic content announcements */
|
|
55
|
+
live?: boolean;
|
|
54
56
|
class?: string;
|
|
55
57
|
children: Snippet;
|
|
56
58
|
onclick?: (e: MouseEvent) => void;
|
|
@@ -67,6 +69,7 @@
|
|
|
67
69
|
variant = 'default',
|
|
68
70
|
size = 'default',
|
|
69
71
|
clickable = false,
|
|
72
|
+
live = false,
|
|
70
73
|
class: className,
|
|
71
74
|
children,
|
|
72
75
|
onclick,
|
|
@@ -81,12 +84,26 @@
|
|
|
81
84
|
</script>
|
|
82
85
|
|
|
83
86
|
{#if isInteractive}
|
|
84
|
-
<button
|
|
87
|
+
<button
|
|
88
|
+
type="button"
|
|
89
|
+
class={classes}
|
|
90
|
+
{onclick}
|
|
91
|
+
role={live ? 'status' : undefined}
|
|
92
|
+
aria-live={live ? 'polite' : undefined}
|
|
93
|
+
aria-atomic={live ? 'true' : undefined}
|
|
94
|
+
{...restProps}
|
|
95
|
+
>
|
|
85
96
|
{@render children()}
|
|
86
97
|
{#if label}<span class="sr-only">{label}</span>{/if}
|
|
87
98
|
</button>
|
|
88
99
|
{:else}
|
|
89
|
-
<span
|
|
100
|
+
<span
|
|
101
|
+
class={classes}
|
|
102
|
+
role={live ? 'status' : undefined}
|
|
103
|
+
aria-live={live ? 'polite' : undefined}
|
|
104
|
+
aria-atomic={live ? 'true' : undefined}
|
|
105
|
+
{...restProps}
|
|
106
|
+
>
|
|
90
107
|
{@render children()}
|
|
91
108
|
{#if label}<span class="sr-only">{label}</span>{/if}
|
|
92
109
|
</span>
|
|
@@ -4,6 +4,8 @@ declare const Badge: import("svelte").Component<{
|
|
|
4
4
|
variant?: "default" | "secondary" | "destructive" | "outline" | "info" | "warning" | "success" | undefined;
|
|
5
5
|
size?: "default" | "sm" | "dot" | undefined;
|
|
6
6
|
clickable?: boolean;
|
|
7
|
+
/** When true, adds role="status" and aria-live="polite" for dynamic content announcements */
|
|
8
|
+
live?: boolean;
|
|
7
9
|
class?: string;
|
|
8
10
|
children: Snippet;
|
|
9
11
|
onclick?: (e: MouseEvent) => void;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { tv, type VariantProps } from 'tailwind-variants';
|
|
5
5
|
|
|
6
6
|
const buttonVariants = tv({
|
|
7
|
-
base: 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm font-medium ring-offset-background
|
|
7
|
+
base: 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm font-medium ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
|
8
8
|
variants: {
|
|
9
9
|
variant: {
|
|
10
10
|
default: 'bg-primary text-primary-foreground shadow hover:bg-primary-hover',
|
|
@@ -63,6 +63,17 @@
|
|
|
63
63
|
}: Props = $props();
|
|
64
64
|
|
|
65
65
|
const classes = $derived(cn(buttonVariants({ variant, size }), className));
|
|
66
|
+
|
|
67
|
+
// Dev-mode a11y warning for icon buttons without accessible labels
|
|
68
|
+
if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
|
|
69
|
+
$effect(() => {
|
|
70
|
+
if (size === 'icon' && !restProps['aria-label'] && !restProps['aria-labelledby']) {
|
|
71
|
+
console.warn(
|
|
72
|
+
'Button: Icon-only buttons (size="icon") should have an aria-label or aria-labelledby for accessibility.'
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
66
77
|
</script>
|
|
67
78
|
|
|
68
79
|
{#snippet loadingSpinner()}
|
|
@@ -199,7 +199,7 @@
|
|
|
199
199
|
value={option.value}
|
|
200
200
|
label={option.label}
|
|
201
201
|
disabled={option.disabled}
|
|
202
|
-
class="relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none
|
|
202
|
+
class="relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[disabled]:cursor-default"
|
|
203
203
|
>
|
|
204
204
|
{#if option.value === value}
|
|
205
205
|
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
@@ -291,7 +291,7 @@
|
|
|
291
291
|
value={option.value}
|
|
292
292
|
label={option.label}
|
|
293
293
|
disabled={option.disabled}
|
|
294
|
-
class="relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none
|
|
294
|
+
class="relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[disabled]:cursor-default"
|
|
295
295
|
>
|
|
296
296
|
{#if option.value === value}
|
|
297
297
|
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
{#snippet trigger()}
|
|
90
90
|
<button
|
|
91
91
|
type="button"
|
|
92
|
-
class="flex h-8 w-8 items-center justify-center rounded-md bg-transparent text-muted-foreground
|
|
92
|
+
class="flex h-8 w-8 items-center justify-center rounded-md bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
93
93
|
aria-label="Panel options"
|
|
94
94
|
>
|
|
95
95
|
<svg
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
<!-- Collapse/Expand button -->
|
|
142
142
|
<button
|
|
143
143
|
type="button"
|
|
144
|
-
class="flex h-8 w-8 items-center justify-center rounded-md bg-transparent text-muted-foreground
|
|
144
|
+
class="flex h-8 w-8 items-center justify-center rounded-md bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
145
145
|
onclick={() => onExpandedChange?.(!isExpanded)}
|
|
146
146
|
aria-label={isExpanded ? 'Collapse panel' : 'Expand panel'}
|
|
147
147
|
aria-expanded={isExpanded}
|
|
@@ -168,7 +168,7 @@
|
|
|
168
168
|
{#snippet trigger()}
|
|
169
169
|
<button
|
|
170
170
|
type="button"
|
|
171
|
-
class="flex h-8 w-8 items-center justify-center rounded-md bg-transparent text-muted-foreground
|
|
171
|
+
class="flex h-8 w-8 items-center justify-center rounded-md bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
172
172
|
aria-label="Panel options"
|
|
173
173
|
>
|
|
174
174
|
<svg
|
|
@@ -195,7 +195,7 @@
|
|
|
195
195
|
{#if !disableClose}
|
|
196
196
|
<button
|
|
197
197
|
type="button"
|
|
198
|
-
class="flex h-8 w-8 items-center justify-center rounded-md bg-transparent text-muted-foreground
|
|
198
|
+
class="flex h-8 w-8 items-center justify-center rounded-md bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
199
199
|
onclick={() => onClose?.()}
|
|
200
200
|
aria-label="Close panel"
|
|
201
201
|
>
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
<button
|
|
33
33
|
type="button"
|
|
34
34
|
class={cn(
|
|
35
|
-
'fixed z-40 flex items-center justify-center gap-2 border border-border bg-background px-3 py-2 text-sm font-medium shadow-md
|
|
35
|
+
'fixed z-40 flex items-center justify-center gap-2 border border-border bg-background px-3 py-2 text-sm font-medium shadow-md hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
|
36
36
|
positionClasses,
|
|
37
37
|
isVertical && 'writing-mode-vertical',
|
|
38
38
|
className
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
* ## Basic Usage
|
|
23
23
|
* ```svelte
|
|
24
24
|
* <DataTable
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
25
|
+
* data={users}
|
|
26
|
+
* columns={[
|
|
27
|
+
* { id: 'name', header: 'Name', accessor: 'name', sortable: true },
|
|
28
|
+
* { id: 'email', header: 'Email', accessor: 'email' },
|
|
29
|
+
* ]}
|
|
30
30
|
* />
|
|
31
31
|
* ```
|
|
32
32
|
*/
|
|
@@ -218,7 +218,7 @@
|
|
|
218
218
|
}
|
|
219
219
|
if (duplicates.length > 0) {
|
|
220
220
|
throw new Error(
|
|
221
|
-
`[DataTable] Duplicate column IDs found: ${duplicates.join(',
|
|
221
|
+
`[DataTable] Duplicate column IDs found: ${duplicates.join(',')}. ` +
|
|
222
222
|
'Each column must have a unique ID.'
|
|
223
223
|
);
|
|
224
224
|
}
|
|
@@ -430,7 +430,7 @@
|
|
|
430
430
|
{:else}
|
|
431
431
|
<tr
|
|
432
432
|
class={cn(
|
|
433
|
-
'bg-card
|
|
433
|
+
'bg-card',
|
|
434
434
|
onRowClick && 'cursor-pointer hover:bg-muted-hover',
|
|
435
435
|
row.isSelected && 'bg-primary/5'
|
|
436
436
|
)}
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
* ## Basic Usage
|
|
22
22
|
* ```svelte
|
|
23
23
|
* <DataTable
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
24
|
+
* data={users}
|
|
25
|
+
* columns={[
|
|
26
|
+
* { id: 'name', header: 'Name', accessor: 'name', sortable: true },
|
|
27
|
+
* { id: 'email', header: 'Email', accessor: 'email' },
|
|
28
|
+
* ]}
|
|
29
29
|
* />
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
@@ -482,7 +482,7 @@
|
|
|
482
482
|
<button
|
|
483
483
|
type="button"
|
|
484
484
|
onclick={prevMonth}
|
|
485
|
-
class="h-7 w-7 inline-flex items-center justify-center rounded-md bg-transparent
|
|
485
|
+
class="h-7 w-7 inline-flex items-center justify-center rounded-md bg-transparent hover:bg-accent hover:text-accent-foreground"
|
|
486
486
|
aria-label="Previous month"
|
|
487
487
|
>
|
|
488
488
|
<svg
|
|
@@ -506,7 +506,7 @@
|
|
|
506
506
|
<button
|
|
507
507
|
type="button"
|
|
508
508
|
onclick={nextMonth}
|
|
509
|
-
class="h-7 w-7 inline-flex items-center justify-center rounded-md bg-transparent
|
|
509
|
+
class="h-7 w-7 inline-flex items-center justify-center rounded-md bg-transparent hover:bg-accent hover:text-accent-foreground"
|
|
510
510
|
aria-label="Next month"
|
|
511
511
|
>
|
|
512
512
|
<svg
|
|
@@ -547,7 +547,7 @@
|
|
|
547
547
|
onclick={() => selectDate(day)}
|
|
548
548
|
disabled={isDateDisabled(day)}
|
|
549
549
|
class={cn(
|
|
550
|
-
'h-8 w-8 inline-flex items-center justify-center rounded-md text-sm bg-transparent
|
|
550
|
+
'h-8 w-8 inline-flex items-center justify-center rounded-md text-sm bg-transparent',
|
|
551
551
|
'hover:bg-accent hover:text-accent-foreground',
|
|
552
552
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
|
553
553
|
'disabled:pointer-events-none disabled:opacity-50',
|
|
@@ -78,11 +78,11 @@
|
|
|
78
78
|
|
|
79
79
|
<DialogPrimitive.Portal>
|
|
80
80
|
<DialogPrimitive.Overlay
|
|
81
|
-
class="fixed inset-0 z-50 bg-
|
|
81
|
+
class="fixed inset-0 z-50 bg-foreground/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
|
82
82
|
/>
|
|
83
83
|
<DialogPrimitive.Content
|
|
84
84
|
class={cn(
|
|
85
|
-
'fixed left-[50%] top-[50%] z-50 grid w-full translate-x-[-50%] translate-y-[-50%] gap-4 border border-
|
|
85
|
+
'fixed left-[50%] top-[50%] z-50 grid w-full translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-background shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg overflow-hidden',
|
|
86
86
|
sizeClasses[size],
|
|
87
87
|
className
|
|
88
88
|
)}
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
<!-- Header section with title and actions -->
|
|
91
91
|
{#if header || title || description || headerActions || showClose}
|
|
92
92
|
<div
|
|
93
|
-
class="flex items-start justify-between gap-4 border-b border-
|
|
93
|
+
class="flex items-start justify-between gap-4 border-b border-border bg-muted/30 px-6 py-4"
|
|
94
94
|
>
|
|
95
95
|
<!-- Title section -->
|
|
96
96
|
<div class="flex flex-col space-y-1.5 min-w-0 flex-1 overflow-hidden">
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
{/if}
|
|
118
118
|
{#if showClose}
|
|
119
119
|
<DialogPrimitive.Close
|
|
120
|
-
class="flex h-9 w-9 items-center justify-center rounded-md bg-transparent text-muted-foreground
|
|
120
|
+
class="flex h-9 w-9 items-center justify-center rounded-md bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none"
|
|
121
121
|
aria-label="Close dialog"
|
|
122
122
|
>
|
|
123
123
|
<svg
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
<!-- Footer section -->
|
|
151
151
|
{#if footer}
|
|
152
152
|
<div
|
|
153
|
-
class="flex flex-col-reverse border-t border-
|
|
153
|
+
class="flex flex-col-reverse border-t border-border bg-muted/30 px-6 py-4 sm:flex-row sm:justify-end sm:space-x-2"
|
|
154
154
|
>
|
|
155
155
|
{@render footer()}
|
|
156
156
|
</div>
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
disabled={subItem.disabled}
|
|
97
97
|
onSelect={() => subItem.onSelect?.()}
|
|
98
98
|
class={cn(
|
|
99
|
-
'relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none
|
|
99
|
+
'relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none bg-popover data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[disabled]:cursor-default',
|
|
100
100
|
subItem.destructive && 'text-destructive data-[highlighted]:text-destructive'
|
|
101
101
|
)}
|
|
102
102
|
aria-keyshortcuts={subItem.shortcut || undefined}
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
disabled={item.disabled}
|
|
124
124
|
onSelect={() => item.onSelect?.()}
|
|
125
125
|
class={cn(
|
|
126
|
-
'relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none
|
|
126
|
+
'relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none bg-popover data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[disabled]:cursor-default',
|
|
127
127
|
item.destructive && 'text-destructive data-[highlighted]:text-destructive'
|
|
128
128
|
)}
|
|
129
129
|
aria-keyshortcuts={item.shortcut || undefined}
|
|
@@ -38,6 +38,12 @@
|
|
|
38
38
|
label?: string;
|
|
39
39
|
/** Hint text shown below the drop zone */
|
|
40
40
|
hint?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Human-friendly label for the accepted file types shown in the drop zone.
|
|
43
|
+
* When omitted, a readable label is derived from `acceptedTypes`
|
|
44
|
+
* (e.g. "Images, PDF, Videos") instead of showing raw MIME types.
|
|
45
|
+
*/
|
|
46
|
+
typesLabel?: string;
|
|
41
47
|
/** Additional classes */
|
|
42
48
|
class?: string;
|
|
43
49
|
}
|
|
@@ -56,6 +62,7 @@
|
|
|
56
62
|
onUpload,
|
|
57
63
|
label,
|
|
58
64
|
hint,
|
|
65
|
+
typesLabel,
|
|
59
66
|
class: className,
|
|
60
67
|
}: Props = $props();
|
|
61
68
|
|
|
@@ -68,6 +75,33 @@
|
|
|
68
75
|
|
|
69
76
|
const displayError = $derived(externalError || internalError);
|
|
70
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Readable label for the accepted types shown in the drop zone.
|
|
80
|
+
* Uses the explicit `typesLabel` when provided, otherwise groups the
|
|
81
|
+
* accepted MIME types into friendly names (Images, PDF, Videos, ...).
|
|
82
|
+
*/
|
|
83
|
+
const acceptedTypesLabel = $derived.by(() => {
|
|
84
|
+
if (typesLabel) return typesLabel;
|
|
85
|
+
|
|
86
|
+
const groups: string[] = [];
|
|
87
|
+
const add = (name: string) => {
|
|
88
|
+
if (!groups.includes(name)) groups.push(name);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
for (const type of acceptedTypes) {
|
|
92
|
+
if (type.startsWith('image/')) add('Images');
|
|
93
|
+
else if (type === 'application/pdf') add('PDF');
|
|
94
|
+
else if (type.startsWith('video/')) add('Videos');
|
|
95
|
+
else if (type.startsWith('audio/')) add('Audio');
|
|
96
|
+
else {
|
|
97
|
+
const subtype = type.split('/')[1];
|
|
98
|
+
add(subtype ? subtype.toUpperCase() : type);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return groups.join(', ');
|
|
103
|
+
});
|
|
104
|
+
|
|
71
105
|
// Helper to generate unique ID
|
|
72
106
|
function generateId(): string {
|
|
73
107
|
return `file-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
@@ -337,7 +371,7 @@
|
|
|
337
371
|
<span class="font-medium text-foreground">Click to upload</span> or drag and drop
|
|
338
372
|
</p>
|
|
339
373
|
<p class="mt-1 text-xs text-muted-foreground">
|
|
340
|
-
{
|
|
374
|
+
{acceptedTypesLabel} (max {formatSize(maxSizeBytes)})
|
|
341
375
|
</p>
|
|
342
376
|
|
|
343
377
|
<!-- Hidden file input -->
|
|
@@ -34,6 +34,12 @@ interface Props {
|
|
|
34
34
|
label?: string;
|
|
35
35
|
/** Hint text shown below the drop zone */
|
|
36
36
|
hint?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Human-friendly label for the accepted file types shown in the drop zone.
|
|
39
|
+
* When omitted, a readable label is derived from `acceptedTypes`
|
|
40
|
+
* (e.g. "Images, PDF, Videos") instead of showing raw MIME types.
|
|
41
|
+
*/
|
|
42
|
+
typesLabel?: string;
|
|
37
43
|
/** Additional classes */
|
|
38
44
|
class?: string;
|
|
39
45
|
}
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
* @example Basic usage:
|
|
16
16
|
* ```svelte
|
|
17
17
|
* <HeaderSearch
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
18
|
+
* enabled={true}
|
|
19
|
+
* placeholder="Search..."
|
|
20
|
+
* onSearch={(query) => handleSearch(query)}
|
|
21
|
+
* onSelect={(item) => goto(item.href)}
|
|
22
|
+
* results={searchResults}
|
|
23
|
+
* loading={isSearching}
|
|
24
24
|
* />
|
|
25
25
|
* ```
|
|
26
26
|
*/
|
|
@@ -175,7 +175,7 @@
|
|
|
175
175
|
type="button"
|
|
176
176
|
onclick={() => (open = true)}
|
|
177
177
|
class={cn(
|
|
178
|
-
'inline-flex items-center justify-center rounded-md border border-input bg-background text-muted-foreground
|
|
178
|
+
'inline-flex items-center justify-center rounded-md border border-input bg-background text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
|
179
179
|
size === 'sm' ? 'h-8 w-8' : 'h-9 w-9',
|
|
180
180
|
className
|
|
181
181
|
)}
|
|
@@ -197,7 +197,7 @@
|
|
|
197
197
|
type="button"
|
|
198
198
|
onclick={() => (open = true)}
|
|
199
199
|
class={cn(
|
|
200
|
-
'flex items-center gap-2 rounded-md border border-input bg-background px-3 text-sm text-muted-foreground
|
|
200
|
+
'flex items-center gap-2 rounded-md border border-input bg-background px-3 text-sm text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
|
201
201
|
size === 'sm' ? 'h-8' : 'h-9',
|
|
202
202
|
className
|
|
203
203
|
)}
|
|
@@ -287,7 +287,7 @@
|
|
|
287
287
|
type="button"
|
|
288
288
|
onclick={() => handleSelect(item)}
|
|
289
289
|
class={cn(
|
|
290
|
-
'flex w-full cursor-pointer items-start gap-3 rounded-md px-3 py-2 text-left text-sm
|
|
290
|
+
'flex w-full cursor-pointer items-start gap-3 rounded-md px-3 py-2 text-left text-sm',
|
|
291
291
|
index === selectedIndex
|
|
292
292
|
? 'bg-accent text-accent-foreground'
|
|
293
293
|
: 'hover:bg-accent-hover'
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { cn } from '../utils.js';
|
|
3
|
+
import { DEV } from 'esm-env';
|
|
3
4
|
|
|
4
5
|
interface Props {
|
|
5
6
|
type?: string;
|
|
@@ -46,6 +47,17 @@
|
|
|
46
47
|
...restProps
|
|
47
48
|
}: Props = $props();
|
|
48
49
|
|
|
50
|
+
// Accessibility: warn if no label is associated in dev mode
|
|
51
|
+
if (DEV) {
|
|
52
|
+
$effect(() => {
|
|
53
|
+
if (!ariaLabel && !ariaLabelledby && !id) {
|
|
54
|
+
console.warn(
|
|
55
|
+
'[Input] Missing accessible label. Provide aria-label, aria-labelledby, or an id with a matching <label for="...">.'
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
49
61
|
function handleInput(e: Event) {
|
|
50
62
|
const target = e.target as HTMLInputElement;
|
|
51
63
|
value = target.value;
|
|
@@ -68,6 +80,7 @@
|
|
|
68
80
|
aria-labelledby={ariaLabelledby}
|
|
69
81
|
aria-describedby={ariaDescribedby}
|
|
70
82
|
aria-invalid={ariaInvalid}
|
|
83
|
+
aria-required={required}
|
|
71
84
|
class={cn(
|
|
72
85
|
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
|
73
86
|
ariaInvalid === true || ariaInvalid === 'true'
|
|
@@ -259,7 +259,7 @@
|
|
|
259
259
|
value={option.value}
|
|
260
260
|
label={option.label}
|
|
261
261
|
disabled={option.disabled || isDisabledByMax}
|
|
262
|
-
class="relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none
|
|
262
|
+
class="relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[disabled]:cursor-default"
|
|
263
263
|
>
|
|
264
264
|
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
265
265
|
{#if isSelected}
|
|
@@ -392,7 +392,7 @@
|
|
|
392
392
|
value={option.value}
|
|
393
393
|
label={option.label}
|
|
394
394
|
disabled={option.disabled || isDisabledByMax}
|
|
395
|
-
class="relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none
|
|
395
|
+
class="relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[disabled]:cursor-default"
|
|
396
396
|
>
|
|
397
397
|
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
398
398
|
{#if isSelected}
|
|
@@ -159,7 +159,7 @@
|
|
|
159
159
|
onclick={decrement}
|
|
160
160
|
disabled={disabled || !canDecrement}
|
|
161
161
|
class={cn(
|
|
162
|
-
'flex h-10 w-10 items-center justify-center rounded-l-md border border-r-0 border-input bg-background text-sm font-medium
|
|
162
|
+
'flex h-10 w-10 items-center justify-center rounded-l-md border border-r-0 border-input bg-background text-sm font-medium hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
|
163
163
|
error && 'border-destructive'
|
|
164
164
|
)}
|
|
165
165
|
aria-label="Decrease value"
|
|
@@ -208,7 +208,7 @@
|
|
|
208
208
|
onclick={increment}
|
|
209
209
|
disabled={disabled || !canIncrement}
|
|
210
210
|
class={cn(
|
|
211
|
-
'flex h-10 w-10 items-center justify-center rounded-r-md border border-l-0 border-input bg-background text-sm font-medium
|
|
211
|
+
'flex h-10 w-10 items-center justify-center rounded-r-md border border-l-0 border-input bg-background text-sm font-medium hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
|
212
212
|
error && 'border-destructive'
|
|
213
213
|
)}
|
|
214
214
|
aria-label="Increase value"
|
|
@@ -251,7 +251,7 @@
|
|
|
251
251
|
onclick={decrement}
|
|
252
252
|
disabled={disabled || !canDecrement}
|
|
253
253
|
class={cn(
|
|
254
|
-
'flex h-10 w-10 items-center justify-center rounded-l-md border border-r-0 border-input bg-background text-sm font-medium
|
|
254
|
+
'flex h-10 w-10 items-center justify-center rounded-l-md border border-r-0 border-input bg-background text-sm font-medium hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
|
255
255
|
error && 'border-destructive'
|
|
256
256
|
)}
|
|
257
257
|
aria-label="Decrease value"
|
|
@@ -300,7 +300,7 @@
|
|
|
300
300
|
onclick={increment}
|
|
301
301
|
disabled={disabled || !canIncrement}
|
|
302
302
|
class={cn(
|
|
303
|
-
'flex h-10 w-10 items-center justify-center rounded-r-md border border-l-0 border-input bg-background text-sm font-medium
|
|
303
|
+
'flex h-10 w-10 items-center justify-center rounded-r-md border border-l-0 border-input bg-background text-sm font-medium hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
|
304
304
|
error && 'border-destructive'
|
|
305
305
|
)}
|
|
306
306
|
aria-label="Increase value"
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
showLabel?: string;
|
|
18
18
|
/** Label for the toggle button when password is visible */
|
|
19
19
|
hideLabel?: string;
|
|
20
|
+
/** Screen reader announcement when password becomes visible */
|
|
21
|
+
visibleAnnouncement?: string;
|
|
22
|
+
/** Screen reader announcement when password becomes hidden */
|
|
23
|
+
hiddenAnnouncement?: string;
|
|
20
24
|
[key: string]: unknown;
|
|
21
25
|
}
|
|
22
26
|
|
|
@@ -33,10 +37,13 @@
|
|
|
33
37
|
onValueChange,
|
|
34
38
|
showLabel = 'Show password',
|
|
35
39
|
hideLabel = 'Hide password',
|
|
40
|
+
visibleAnnouncement = 'Password is now visible',
|
|
41
|
+
hiddenAnnouncement = 'Password is now hidden',
|
|
36
42
|
...restProps
|
|
37
43
|
}: Props = $props();
|
|
38
44
|
|
|
39
45
|
let showPassword = $state(false);
|
|
46
|
+
let announcement = $state('');
|
|
40
47
|
|
|
41
48
|
function handleInput(e: Event) {
|
|
42
49
|
const target = e.target as HTMLInputElement;
|
|
@@ -46,6 +53,7 @@
|
|
|
46
53
|
|
|
47
54
|
function toggleVisibility() {
|
|
48
55
|
showPassword = !showPassword;
|
|
56
|
+
announcement = showPassword ? visibleAnnouncement : hiddenAnnouncement;
|
|
49
57
|
}
|
|
50
58
|
</script>
|
|
51
59
|
|
|
@@ -77,4 +85,8 @@
|
|
|
77
85
|
>
|
|
78
86
|
<Icon name={showPassword ? 'eye-off' : 'eye'} size={18} />
|
|
79
87
|
</button>
|
|
88
|
+
<!-- Screen reader live region for announcing visibility changes -->
|
|
89
|
+
<div role="status" aria-live="polite" aria-atomic="true" class="sr-only">
|
|
90
|
+
{announcement}
|
|
91
|
+
</div>
|
|
80
92
|
</div>
|
|
@@ -13,6 +13,10 @@ interface Props {
|
|
|
13
13
|
showLabel?: string;
|
|
14
14
|
/** Label for the toggle button when password is visible */
|
|
15
15
|
hideLabel?: string;
|
|
16
|
+
/** Screen reader announcement when password becomes visible */
|
|
17
|
+
visibleAnnouncement?: string;
|
|
18
|
+
/** Screen reader announcement when password becomes hidden */
|
|
19
|
+
hiddenAnnouncement?: string;
|
|
16
20
|
[key: string]: unknown;
|
|
17
21
|
}
|
|
18
22
|
declare const PasswordInput: import("svelte").Component<Props, {}, "value">;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { RadioGroup as RadioGroupPrimitive } from 'bits-ui';
|
|
3
|
-
import { cn } from '../utils.js';
|
|
4
|
-
import {
|
|
3
|
+
import { cn, useId } from '../utils.js';
|
|
4
|
+
import { computeDescribedBy } from '../utils/form.js';
|
|
5
5
|
import { validateNonEmptyArray } from '../validation.js';
|
|
6
6
|
import Label from './Label.svelte';
|
|
7
7
|
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
class: className,
|
|
52
52
|
}: Props = $props();
|
|
53
53
|
|
|
54
|
-
// Generate unique IDs for accessibility
|
|
55
|
-
const
|
|
56
|
-
const componentName = $derived(name ||
|
|
54
|
+
// Generate unique IDs for accessibility (sequential counter, SSR-safe)
|
|
55
|
+
const generatedId = useId('radio-group');
|
|
56
|
+
const componentName = $derived(name || generatedId);
|
|
57
57
|
const labelId = $derived(`${componentName}-label`);
|
|
58
58
|
const hintId = $derived(`${componentName}-hint`);
|
|
59
59
|
const errorId = $derived(`${componentName}-error`);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Select as SelectPrimitive } from 'bits-ui';
|
|
3
3
|
import { cn, useId } from '../utils.js';
|
|
4
4
|
import { validateNonEmptyArray } from '../validation.js';
|
|
5
|
+
import { DEV } from 'esm-env';
|
|
5
6
|
import Label from './Label.svelte';
|
|
6
7
|
|
|
7
8
|
export interface SelectOption {
|
|
@@ -76,6 +77,17 @@
|
|
|
76
77
|
validateNonEmptyArray(options, 'options', 'Select');
|
|
77
78
|
});
|
|
78
79
|
|
|
80
|
+
// Accessibility: warn if no label is associated in dev mode
|
|
81
|
+
if (DEV) {
|
|
82
|
+
$effect(() => {
|
|
83
|
+
if (!label && !id) {
|
|
84
|
+
console.warn(
|
|
85
|
+
'[Select] Missing accessible label. Provide a label prop or an id with a matching <label for="...">.'
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
79
91
|
function isGroup(item: SelectOption | SelectGroup): item is SelectGroup {
|
|
80
92
|
return 'options' in item;
|
|
81
93
|
}
|
|
@@ -131,7 +143,7 @@
|
|
|
131
143
|
|
|
132
144
|
// Common item classes
|
|
133
145
|
const itemClasses =
|
|
134
|
-
'relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none
|
|
146
|
+
'relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[disabled]:cursor-default';
|
|
135
147
|
</script>
|
|
136
148
|
|
|
137
149
|
{#snippet chevronIcon()}
|
|
@@ -28,14 +28,14 @@
|
|
|
28
28
|
labelText: 'text-sm font-medium leading-none',
|
|
29
29
|
modeToggle: 'inline-flex items-center rounded-lg bg-muted p-1',
|
|
30
30
|
modeButton:
|
|
31
|
-
'inline-flex items-center justify-center rounded-md px-3 py-1.5 text-sm font-medium
|
|
31
|
+
'inline-flex items-center justify-center rounded-md px-3 py-1.5 text-sm font-medium',
|
|
32
32
|
modeButtonActive: 'bg-background text-foreground shadow-sm',
|
|
33
33
|
modeButtonInactive: 'text-muted-foreground hover:text-foreground',
|
|
34
34
|
canvasContainer: 'relative w-full overflow-hidden rounded-lg border bg-background',
|
|
35
35
|
canvas: 'block w-full cursor-crosshair touch-none',
|
|
36
36
|
toolbar: 'flex flex-wrap items-center gap-2',
|
|
37
37
|
toolButton:
|
|
38
|
-
'inline-flex h-9 w-9 items-center justify-center rounded-md border border-input bg-background text-sm font-medium ring-offset-background
|
|
38
|
+
'inline-flex h-9 w-9 items-center justify-center rounded-md border border-input bg-background text-sm font-medium ring-offset-background hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
|
39
39
|
toolGroup: 'flex items-center gap-1.5',
|
|
40
40
|
toolLabel: 'text-xs text-muted-foreground',
|
|
41
41
|
colorPicker:
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
'h-2 w-20 cursor-pointer appearance-none rounded-full bg-muted accent-primary',
|
|
45
45
|
typeInput: 'w-full',
|
|
46
46
|
fontPicker: 'flex flex-wrap gap-2',
|
|
47
|
-
fontButton: 'rounded-lg border-2 px-3 py-2 text-lg
|
|
47
|
+
fontButton: 'rounded-lg border-2 px-3 py-2 text-lg hover:border-primary',
|
|
48
48
|
fontButtonSelected: 'border-primary bg-primary/5',
|
|
49
49
|
fontButtonUnselected: 'border-input',
|
|
50
50
|
consent: 'flex items-start gap-2',
|
|
@@ -384,8 +384,8 @@
|
|
|
384
384
|
|
|
385
385
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
386
386
|
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
|
|
387
|
-
|
|
388
|
-
|
|
387
|
+
${backgroundColor === 'white' ? `<rect width="${width}" height="${height}" fill="white"/>` : ''}
|
|
388
|
+
${paths}
|
|
389
389
|
</svg>`;
|
|
390
390
|
}
|
|
391
391
|
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
tabindex={value === tab.id ? 0 : -1}
|
|
115
115
|
disabled={tab.disabled}
|
|
116
116
|
class={cn(
|
|
117
|
-
'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background
|
|
117
|
+
'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
|
118
118
|
value === tab.id
|
|
119
119
|
? 'bg-background text-foreground shadow-sm'
|
|
120
120
|
: 'hover:bg-background-hover hover:text-foreground',
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
{#each footerLinks as link}
|
|
120
120
|
<a
|
|
121
121
|
href={link.href}
|
|
122
|
-
class="text-sm text-muted-foreground
|
|
122
|
+
class="text-sm text-muted-foreground hover:text-foreground"
|
|
123
123
|
target={link.external ? '_blank' : undefined}
|
|
124
124
|
rel={link.external ? 'noopener noreferrer' : undefined}
|
|
125
125
|
>
|
|
@@ -255,7 +255,7 @@
|
|
|
255
255
|
|
|
256
256
|
<!-- Main Content Area -->
|
|
257
257
|
<div
|
|
258
|
-
class="flex flex-1 flex-col transition-
|
|
258
|
+
class="flex flex-1 flex-col transition-[margin] duration-300 motion-reduce:transition-none"
|
|
259
259
|
style="margin-left: {sidebarWidth}px;"
|
|
260
260
|
>
|
|
261
261
|
{#if showHeader}
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
<a
|
|
98
98
|
href={item.href}
|
|
99
99
|
class={cn(
|
|
100
|
-
'text-sm font-bold uppercase
|
|
100
|
+
'text-sm font-bold uppercase hover:text-primary',
|
|
101
101
|
item.active ? 'text-primary' : 'text-muted-foreground'
|
|
102
102
|
)}
|
|
103
103
|
target={item.external ? '_blank' : undefined}
|
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
<a
|
|
123
123
|
href={item.href}
|
|
124
124
|
class={cn(
|
|
125
|
-
'block rounded-md px-3 py-2 text-sm font-bold uppercase
|
|
125
|
+
'block rounded-md px-3 py-2 text-sm font-bold uppercase',
|
|
126
126
|
item.active
|
|
127
127
|
? 'bg-primary/10 text-primary'
|
|
128
128
|
: 'text-muted-foreground hover:bg-accent hover:text-accent-foreground'
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
<a
|
|
51
51
|
href={link.href}
|
|
52
52
|
class={cn(
|
|
53
|
-
'flex items-center
|
|
53
|
+
'flex items-center overflow-hidden',
|
|
54
54
|
// List mode styles
|
|
55
55
|
display === 'list' && 'gap-3 rounded-md px-3 py-2 text-sm',
|
|
56
56
|
display === 'list' &&
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
|
|
94
94
|
<span
|
|
95
95
|
class={cn(
|
|
96
|
-
'truncate whitespace-nowrap transition-
|
|
96
|
+
'truncate whitespace-nowrap transition-[width,opacity] duration-300 motion-reduce:transition-none',
|
|
97
97
|
display === 'icons' ? 'w-0 opacity-0 flex-none' : 'flex-1 opacity-100'
|
|
98
98
|
)}
|
|
99
99
|
>
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
{#if link.external}
|
|
112
112
|
<svg
|
|
113
113
|
class={cn(
|
|
114
|
-
'h-4 w-4 shrink-0 transition-
|
|
114
|
+
'h-4 w-4 shrink-0 transition-opacity duration-300',
|
|
115
115
|
display === 'icons' ? 'hidden' : 'opacity-50'
|
|
116
116
|
)}
|
|
117
117
|
fill="none"
|
|
@@ -437,7 +437,7 @@
|
|
|
437
437
|
<!-- Sidebar: z-50 positions above overlay and content, below modals (z-100+) -->
|
|
438
438
|
<aside
|
|
439
439
|
class={cn(
|
|
440
|
-
'fixed left-0 top-0 z-[var(--z-sidebar,50)] flex h-full flex-col transition-
|
|
440
|
+
'fixed left-0 top-0 z-[var(--z-sidebar,50)] flex h-full flex-col transition-[width,transform] duration-300 motion-reduce:transition-none touch-pan-y',
|
|
441
441
|
// Variant-based background and text colors
|
|
442
442
|
isLight ? 'bg-background text-foreground' : 'bg-sidebar text-sidebar-foreground',
|
|
443
443
|
strongBorder ? 'border-r-2 border-black' : 'border-r border-black',
|
|
@@ -110,7 +110,6 @@
|
|
|
110
110
|
bind:this={flyoutElement}
|
|
111
111
|
class={cn(
|
|
112
112
|
'fixed min-w-48 rounded-md border shadow-lg z-[calc(var(--z-sidebar,50)+10)]',
|
|
113
|
-
'motion-reduce:transition-none',
|
|
114
113
|
isLight ? 'bg-background border-border' : 'bg-sidebar border-sidebar-foreground/20'
|
|
115
114
|
)}
|
|
116
115
|
style="top: {top}px; left: {left}px;"
|
|
@@ -174,7 +173,7 @@
|
|
|
174
173
|
<a
|
|
175
174
|
href={child.disabled ? undefined : child.href}
|
|
176
175
|
class={cn(
|
|
177
|
-
'flex items-center gap-2 rounded-md px-3 py-2 text-sm
|
|
176
|
+
'flex items-center gap-2 rounded-md px-3 py-2 text-sm ml-2',
|
|
178
177
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1',
|
|
179
178
|
isLight
|
|
180
179
|
? 'text-foreground hover:bg-accent hover:text-accent-foreground'
|
|
@@ -84,11 +84,13 @@
|
|
|
84
84
|
|
|
85
85
|
function getItemStateClasses(active?: boolean, disabled?: boolean): string {
|
|
86
86
|
return cn(
|
|
87
|
-
isLight &&
|
|
87
|
+
isLight &&
|
|
88
|
+
!active &&
|
|
89
|
+
'text-foreground bg-background hover:bg-accent hover:text-accent-foreground',
|
|
88
90
|
isLight && active && 'bg-primary/10 text-primary',
|
|
89
91
|
!isLight &&
|
|
90
92
|
!active &&
|
|
91
|
-
'text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
|
|
93
|
+
'text-sidebar-foreground bg-sidebar hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
|
|
92
94
|
!isLight && active && 'bg-sidebar-primary text-sidebar-primary-foreground',
|
|
93
95
|
disabled && 'opacity-50 pointer-events-none cursor-not-allowed'
|
|
94
96
|
);
|
|
@@ -172,13 +174,13 @@
|
|
|
172
174
|
highlight: { before: string; match: string; after: string } | null
|
|
173
175
|
)}
|
|
174
176
|
{#if highlight}
|
|
175
|
-
<span class="flex-1 truncate">
|
|
177
|
+
<span class="flex-1 min-w-0 truncate">
|
|
176
178
|
{highlight.before}<mark class="bg-yellow-200 dark:bg-yellow-900 text-inherit rounded px-0.5"
|
|
177
179
|
>{highlight.match}</mark
|
|
178
180
|
>{highlight.after}
|
|
179
181
|
</span>
|
|
180
182
|
{:else}
|
|
181
|
-
<span class="flex-1 truncate">{name}</span>
|
|
183
|
+
<span class="flex-1 min-w-0 truncate">{name}</span>
|
|
182
184
|
{/if}
|
|
183
185
|
{/snippet}
|
|
184
186
|
|
|
@@ -186,7 +188,7 @@
|
|
|
186
188
|
<button
|
|
187
189
|
type="button"
|
|
188
190
|
class={cn(
|
|
189
|
-
'flex items-center justify-center p-1 rounded
|
|
191
|
+
'flex items-center justify-center p-1 rounded shrink-0',
|
|
190
192
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1',
|
|
191
193
|
// Show on hover for regular items, always show in pinned section
|
|
192
194
|
isUnpin ? 'opacity-100' : 'opacity-0 group-hover:opacity-100',
|
|
@@ -247,10 +249,10 @@
|
|
|
247
249
|
<div>
|
|
248
250
|
<div
|
|
249
251
|
class={cn(
|
|
250
|
-
'group flex w-full items-center rounded-md
|
|
251
|
-
isLight && !item.active && 'hover:bg-accent',
|
|
252
|
+
'group flex w-full items-center rounded-md',
|
|
253
|
+
isLight && !item.active && 'bg-background hover:bg-accent',
|
|
252
254
|
isLight && item.active && 'bg-primary/10',
|
|
253
|
-
!isLight && !item.active && 'hover:bg-sidebar-accent',
|
|
255
|
+
!isLight && !item.active && 'bg-sidebar hover:bg-sidebar-accent',
|
|
254
256
|
!isLight && item.active && 'bg-sidebar-primary'
|
|
255
257
|
)}
|
|
256
258
|
>
|
|
@@ -259,7 +261,7 @@
|
|
|
259
261
|
<a
|
|
260
262
|
href={item.disabled ? undefined : item.href}
|
|
261
263
|
class={cn(
|
|
262
|
-
'flex flex-1 items-center gap-3 px-3 py-2 text-sm font-medium
|
|
264
|
+
'flex flex-1 items-center gap-3 px-3 py-2 text-sm font-medium',
|
|
263
265
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1',
|
|
264
266
|
'group-hover:underline underline-offset-2',
|
|
265
267
|
isLight && !item.active && 'text-foreground group-hover:text-accent-foreground',
|
|
@@ -287,7 +289,7 @@
|
|
|
287
289
|
<button
|
|
288
290
|
type="button"
|
|
289
291
|
class={cn(
|
|
290
|
-
'flex flex-1 items-center gap-3 px-3 py-2 text-sm font-medium
|
|
292
|
+
'flex flex-1 items-center gap-3 px-3 py-2 text-sm font-medium text-left',
|
|
291
293
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1',
|
|
292
294
|
isLight && !item.active && 'text-foreground group-hover:text-accent-foreground',
|
|
293
295
|
isLight && item.active && 'text-primary',
|
|
@@ -312,7 +314,7 @@
|
|
|
312
314
|
<button
|
|
313
315
|
type="button"
|
|
314
316
|
class={cn(
|
|
315
|
-
'flex items-center justify-center p-2
|
|
317
|
+
'flex items-center justify-center p-2 rounded-md',
|
|
316
318
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1',
|
|
317
319
|
isLight
|
|
318
320
|
? 'hover:bg-accent-hover group-hover:text-accent-foreground'
|
|
@@ -353,16 +355,13 @@
|
|
|
353
355
|
</div>
|
|
354
356
|
|
|
355
357
|
{#if isExpanded}
|
|
356
|
-
<ul
|
|
357
|
-
class="mt-1 space-y-1 pl-4 motion-reduce:transition-none overflow-hidden"
|
|
358
|
-
transition:slide={{ duration: 200 }}
|
|
359
|
-
>
|
|
358
|
+
<ul class="mt-1 space-y-1 pl-4 overflow-hidden" transition:slide={{ duration: 200 }}>
|
|
360
359
|
{#each item.children as child}
|
|
361
360
|
<li>
|
|
362
361
|
<a
|
|
363
362
|
href={child.disabled ? undefined : child.href}
|
|
364
363
|
class={cn(
|
|
365
|
-
'flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium
|
|
364
|
+
'flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium',
|
|
366
365
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1',
|
|
367
366
|
getItemStateClasses(child.active, child.disabled)
|
|
368
367
|
)}
|
|
@@ -394,10 +393,10 @@
|
|
|
394
393
|
<!-- Wrap in group div for pin button hover behavior -->
|
|
395
394
|
<div
|
|
396
395
|
class={cn(
|
|
397
|
-
'group flex w-full items-center rounded-md
|
|
398
|
-
isLight && !item.active && 'hover:bg-accent',
|
|
396
|
+
'group flex w-full items-center rounded-md',
|
|
397
|
+
isLight && !item.active && 'bg-background hover:bg-accent',
|
|
399
398
|
isLight && item.active && 'bg-primary/10',
|
|
400
|
-
!isLight && !item.active && 'hover:bg-sidebar-accent',
|
|
399
|
+
!isLight && !item.active && 'bg-sidebar hover:bg-sidebar-accent',
|
|
401
400
|
!isLight && item.active && 'bg-sidebar-primary'
|
|
402
401
|
)}
|
|
403
402
|
>
|
|
@@ -406,7 +405,7 @@
|
|
|
406
405
|
<a
|
|
407
406
|
href={item.disabled ? undefined : item.href}
|
|
408
407
|
class={cn(
|
|
409
|
-
'flex flex-1 items-center gap-3 px-3 py-2 text-sm font-medium
|
|
408
|
+
'flex flex-1 items-center gap-3 px-3 py-2 text-sm font-medium relative',
|
|
410
409
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1',
|
|
411
410
|
isLight && !item.active && 'text-foreground group-hover:text-accent-foreground',
|
|
412
411
|
isLight && item.active && 'text-primary',
|
|
@@ -453,7 +452,7 @@
|
|
|
453
452
|
<button
|
|
454
453
|
type="button"
|
|
455
454
|
class={cn(
|
|
456
|
-
'flex flex-1 items-center gap-3 px-3 py-2 text-sm font-medium
|
|
455
|
+
'flex flex-1 items-center gap-3 px-3 py-2 text-sm font-medium text-left relative',
|
|
457
456
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1',
|
|
458
457
|
isLight && !item.active && 'text-foreground group-hover:text-accent-foreground',
|
|
459
458
|
isLight && item.active && 'text-primary',
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
<button
|
|
68
68
|
onclick={toggleSection}
|
|
69
69
|
class={cn(
|
|
70
|
-
'flex w-full items-center justify-between mb-2 px-4 text-xs font-semibold uppercase tracking-wider
|
|
70
|
+
'flex w-full items-center justify-between mb-2 px-4 text-xs font-semibold uppercase tracking-wider rounded-md overflow-hidden',
|
|
71
71
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1',
|
|
72
72
|
isLight
|
|
73
73
|
? 'text-muted-foreground hover:text-foreground'
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
{:else if isExpanded}
|
|
133
133
|
<!-- Collapsible and expanded: animate -->
|
|
134
134
|
<ul
|
|
135
|
-
class="space-y-1 px-2
|
|
135
|
+
class="space-y-1 px-2 overflow-hidden"
|
|
136
136
|
id={`sidebar-section-${section.id}`}
|
|
137
137
|
transition:slide={{ duration: 200 }}
|
|
138
138
|
>
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
aria-selected={filter === filterItem.id}
|
|
82
82
|
tabindex={filter === filterItem.id ? 0 : -1}
|
|
83
83
|
class={cn(
|
|
84
|
-
'inline-flex items-center justify-center gap-1.5 whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background
|
|
84
|
+
'inline-flex items-center justify-center gap-1.5 whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
|
85
85
|
filter === filterItem.id
|
|
86
86
|
? 'bg-background text-foreground shadow-sm'
|
|
87
87
|
: 'hover:bg-background-hover hover:text-foreground'
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
70
70
|
<div
|
|
71
71
|
class={cn(
|
|
72
|
-
'group relative flex gap-3 rounded-lg border p-4
|
|
72
|
+
'group relative flex gap-3 rounded-lg border p-4',
|
|
73
73
|
!notification.read && 'bg-blue-50/50 border-blue-200',
|
|
74
74
|
notification.read && 'bg-card border-border',
|
|
75
75
|
onClick && 'cursor-pointer hover:bg-muted/50',
|
package/dist/lib/utils.d.ts
CHANGED
|
@@ -6,9 +6,11 @@ import { type ClassValue } from 'clsx';
|
|
|
6
6
|
export declare function cn(...inputs: ClassValue[]): string;
|
|
7
7
|
/**
|
|
8
8
|
* Generate a unique ID with an optional prefix.
|
|
9
|
-
*
|
|
9
|
+
* Uses crypto.randomUUID() to avoid SSR hydration mismatches caused by
|
|
10
|
+
* sequential counters (where server and client counts can diverge).
|
|
11
|
+
* The short suffix is derived from the UUID for compact, readable IDs.
|
|
10
12
|
*
|
|
11
13
|
* @param prefix - Optional prefix for the ID (e.g., 'checkbox', 'tabs')
|
|
12
|
-
* @returns A unique ID string like 'checkbox-
|
|
14
|
+
* @returns A unique ID string like 'checkbox-a1b2c3d4' or 'ct-a1b2c3d4'
|
|
13
15
|
*/
|
|
14
16
|
export declare function useId(prefix?: string): string;
|
package/dist/lib/utils.js
CHANGED
|
@@ -7,22 +7,16 @@ import { twMerge } from 'tailwind-merge';
|
|
|
7
7
|
export function cn(...inputs) {
|
|
8
8
|
return twMerge(clsx(inputs));
|
|
9
9
|
}
|
|
10
|
-
/**
|
|
11
|
-
* Counter for generating unique sequential IDs.
|
|
12
|
-
* Uses a sequential counter instead of Math.random() to ensure:
|
|
13
|
-
* - Deterministic IDs that match between SSR and hydration
|
|
14
|
-
* - Stable IDs across component re-renders
|
|
15
|
-
* - Accessibility-friendly ID associations
|
|
16
|
-
*/
|
|
17
|
-
let idCounter = 0;
|
|
18
10
|
/**
|
|
19
11
|
* Generate a unique ID with an optional prefix.
|
|
20
|
-
*
|
|
12
|
+
* Uses crypto.randomUUID() to avoid SSR hydration mismatches caused by
|
|
13
|
+
* sequential counters (where server and client counts can diverge).
|
|
14
|
+
* The short suffix is derived from the UUID for compact, readable IDs.
|
|
21
15
|
*
|
|
22
16
|
* @param prefix - Optional prefix for the ID (e.g., 'checkbox', 'tabs')
|
|
23
|
-
* @returns A unique ID string like 'checkbox-
|
|
17
|
+
* @returns A unique ID string like 'checkbox-a1b2c3d4' or 'ct-a1b2c3d4'
|
|
24
18
|
*/
|
|
25
19
|
export function useId(prefix) {
|
|
26
|
-
const id =
|
|
20
|
+
const id = crypto.randomUUID().slice(0, 8);
|
|
27
21
|
return prefix ? `${prefix}-${id}` : `ct-${id}`;
|
|
28
22
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classic-homes/theme-svelte",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.55",
|
|
4
4
|
"description": "Svelte components for the Classic theme system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"svelte": "./dist/lib/index.js",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "svelte-package -i src",
|
|
29
29
|
"dev": "svelte-package -i src --watch",
|
|
30
|
-
"clean": "rm
|
|
30
|
+
"clean": "node ../../scripts/rm.mjs dist",
|
|
31
31
|
"typecheck": "svelte-check --tsconfig ./tsconfig.json",
|
|
32
32
|
"test": "vitest run",
|
|
33
33
|
"test:watch": "vitest",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@sveltejs/package": "^2.3.0",
|
|
72
|
-
"@sveltejs/vite-plugin-svelte": "^
|
|
72
|
+
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
|
73
73
|
"@testing-library/jest-dom": "^6.6.0",
|
|
74
74
|
"@testing-library/svelte": "^5.0.0",
|
|
75
75
|
"@testing-library/user-event": "^14.5.0",
|