@classic-homes/theme-svelte 0.1.11 → 0.1.13
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/Badge.svelte.d.ts +1 -1
- package/dist/lib/components/Combobox.svelte +4 -2
- package/dist/lib/components/DateTimePicker.svelte +27 -8
- package/dist/lib/components/DropdownMenu.svelte +60 -56
- package/dist/lib/components/Icon.svelte +370 -0
- package/dist/lib/components/Icon.svelte.d.ts +22 -0
- package/dist/lib/components/MultiSelect.svelte +4 -2
- package/dist/lib/components/Select.svelte +5 -3
- package/dist/lib/components/Toast.svelte.d.ts +1 -1
- package/dist/lib/components/layout/DashboardLayout.svelte +8 -1
- package/dist/lib/components/layout/DashboardLayout.svelte.d.ts +2 -0
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.js +11 -0
- package/dist/lib/notices/components/NoticeBanner.svelte +73 -0
- package/dist/lib/notices/components/NoticeBanner.svelte.d.ts +6 -0
- package/dist/lib/notices/components/NoticeCard.svelte +73 -0
- package/dist/lib/notices/components/NoticeCard.svelte.d.ts +6 -0
- package/dist/lib/notices/index.d.ts +47 -0
- package/dist/lib/notices/index.js +50 -0
- package/dist/lib/notices/sanitize.d.ts +32 -0
- package/dist/lib/notices/sanitize.js +77 -0
- package/dist/lib/notices/store.d.ts +148 -0
- package/dist/lib/notices/store.js +247 -0
- package/dist/lib/notices/types.d.ts +142 -0
- package/dist/lib/notices/types.js +73 -0
- package/package.json +8 -3
|
@@ -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" | "error" | "
|
|
4
|
+
variant?: "default" | "destructive" | "error" | "info" | "warning" | "success" | undefined;
|
|
5
5
|
class?: string;
|
|
6
6
|
children: Snippet;
|
|
7
7
|
}, {}, "">;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
declare const Badge: import("svelte").Component<{
|
|
3
3
|
[key: string]: unknown;
|
|
4
|
-
variant?: "default" | "secondary" | "destructive" | "outline" | "
|
|
4
|
+
variant?: "default" | "secondary" | "destructive" | "outline" | "info" | "warning" | "success" | undefined;
|
|
5
5
|
size?: "default" | "sm" | "dot" | undefined;
|
|
6
6
|
clickable?: boolean;
|
|
7
7
|
class?: string;
|
|
@@ -133,8 +133,10 @@
|
|
|
133
133
|
</div>
|
|
134
134
|
|
|
135
135
|
<ComboboxPrimitive.Content
|
|
136
|
-
class="relative z-50
|
|
137
|
-
sideOffset={
|
|
136
|
+
class="relative z-50 w-[var(--bits-floating-anchor-width)] min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:border-t-0 data-[side=bottom]:rounded-t-none data-[side=top]:border-b-0 data-[side=top]:rounded-b-none 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2"
|
|
137
|
+
sideOffset={-1}
|
|
138
|
+
collisionPadding={8}
|
|
139
|
+
avoidCollisions={true}
|
|
138
140
|
>
|
|
139
141
|
<div class="p-1 max-h-[300px] overflow-y-auto">
|
|
140
142
|
{#if loading}
|
|
@@ -65,12 +65,21 @@
|
|
|
65
65
|
let minute = $state(0);
|
|
66
66
|
let period = $state<'AM' | 'PM'>('PM');
|
|
67
67
|
|
|
68
|
-
//
|
|
68
|
+
// Track if we're currently selecting to prevent effect from interfering
|
|
69
|
+
let isSelecting = $state(false);
|
|
70
|
+
|
|
71
|
+
// Sync state when value changes externally (not during our own selection)
|
|
69
72
|
$effect(() => {
|
|
70
|
-
if (value) {
|
|
71
|
-
|
|
73
|
+
if (value && !isSelecting) {
|
|
74
|
+
const hours = value.getHours();
|
|
72
75
|
minute = value.getMinutes();
|
|
73
|
-
period =
|
|
76
|
+
period = hours >= 12 ? 'PM' : 'AM';
|
|
77
|
+
// Convert 24h to 12h format for the hour state
|
|
78
|
+
if (timeFormat === '12h') {
|
|
79
|
+
hour = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
|
|
80
|
+
} else {
|
|
81
|
+
hour = hours;
|
|
82
|
+
}
|
|
74
83
|
viewMonth = value.getMonth();
|
|
75
84
|
viewYear = value.getFullYear();
|
|
76
85
|
}
|
|
@@ -147,7 +156,11 @@
|
|
|
147
156
|
function selectDate(day: number) {
|
|
148
157
|
if (isDateDisabled(day)) return;
|
|
149
158
|
|
|
150
|
-
|
|
159
|
+
// Capture current view state before any reactive updates
|
|
160
|
+
const currentMonth = viewMonth;
|
|
161
|
+
const currentYear = viewYear;
|
|
162
|
+
|
|
163
|
+
const newDate = new Date(currentYear, currentMonth, day);
|
|
151
164
|
|
|
152
165
|
if (dateOnly) {
|
|
153
166
|
// Set to midnight for date-only mode
|
|
@@ -161,11 +174,16 @@
|
|
|
161
174
|
newDate.setHours(h, minute, 0, 0);
|
|
162
175
|
}
|
|
163
176
|
|
|
177
|
+
// Prevent the effect from running during our selection
|
|
178
|
+
isSelecting = true;
|
|
164
179
|
value = newDate;
|
|
165
180
|
onValueChange?.(newDate);
|
|
181
|
+
// Allow effect to run again for external changes
|
|
182
|
+
isSelecting = false;
|
|
166
183
|
}
|
|
167
184
|
|
|
168
185
|
function updateTime() {
|
|
186
|
+
isSelecting = true;
|
|
169
187
|
if (!value) {
|
|
170
188
|
// If no date selected, use today
|
|
171
189
|
const today = new Date();
|
|
@@ -186,6 +204,7 @@
|
|
|
186
204
|
value = newDate;
|
|
187
205
|
onValueChange?.(newDate);
|
|
188
206
|
}
|
|
207
|
+
isSelecting = false;
|
|
189
208
|
}
|
|
190
209
|
|
|
191
210
|
function prevMonth() {
|
|
@@ -280,7 +299,7 @@
|
|
|
280
299
|
<button
|
|
281
300
|
type="button"
|
|
282
301
|
onclick={prevMonth}
|
|
283
|
-
class="h-7 w-7 inline-flex items-center justify-center rounded-md hover:bg-accent hover:text-accent-foreground"
|
|
302
|
+
class="h-7 w-7 inline-flex items-center justify-center rounded-md bg-transparent transition-colors duration-150 hover:bg-accent hover:text-accent-foreground"
|
|
284
303
|
aria-label="Previous month"
|
|
285
304
|
>
|
|
286
305
|
<svg
|
|
@@ -304,7 +323,7 @@
|
|
|
304
323
|
<button
|
|
305
324
|
type="button"
|
|
306
325
|
onclick={nextMonth}
|
|
307
|
-
class="h-7 w-7 inline-flex items-center justify-center rounded-md hover:bg-accent hover:text-accent-foreground"
|
|
326
|
+
class="h-7 w-7 inline-flex items-center justify-center rounded-md bg-transparent transition-colors duration-150 hover:bg-accent hover:text-accent-foreground"
|
|
308
327
|
aria-label="Next month"
|
|
309
328
|
>
|
|
310
329
|
<svg
|
|
@@ -345,7 +364,7 @@
|
|
|
345
364
|
onclick={() => selectDate(day)}
|
|
346
365
|
disabled={isDateDisabled(day)}
|
|
347
366
|
class={cn(
|
|
348
|
-
'h-8 w-8 inline-flex items-center justify-center rounded-md text-sm transition-colors',
|
|
367
|
+
'h-8 w-8 inline-flex items-center justify-center rounded-md text-sm bg-transparent transition-colors duration-150',
|
|
349
368
|
'hover:bg-accent hover:text-accent-foreground',
|
|
350
369
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
|
351
370
|
'disabled:pointer-events-none disabled:opacity-50',
|
|
@@ -67,69 +67,73 @@
|
|
|
67
67
|
<DropdownMenuPrimitive.Content
|
|
68
68
|
{side}
|
|
69
69
|
{align}
|
|
70
|
-
sideOffset={
|
|
70
|
+
sideOffset={-1}
|
|
71
|
+
collisionPadding={8}
|
|
72
|
+
avoidCollisions={true}
|
|
71
73
|
class={cn(
|
|
72
74
|
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
|
73
75
|
className
|
|
74
76
|
)}
|
|
75
77
|
>
|
|
76
|
-
|
|
77
|
-
{#
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
{item.label}
|
|
82
|
-
</div>
|
|
83
|
-
{/if}
|
|
84
|
-
{#each item.items as subItem}
|
|
85
|
-
{#if subItem.type === 'separator'}
|
|
86
|
-
<DropdownMenuPrimitive.Separator class="-mx-1 my-1 h-px bg-muted" />
|
|
87
|
-
{:else if subItem.type === 'label'}
|
|
78
|
+
<div class="max-h-[300px] overflow-y-auto">
|
|
79
|
+
{#each items as item}
|
|
80
|
+
{#if isGroup(item)}
|
|
81
|
+
<DropdownMenuPrimitive.Group>
|
|
82
|
+
{#if item.label}
|
|
88
83
|
<div class="px-2 py-1.5 text-sm font-semibold">
|
|
89
|
-
{
|
|
84
|
+
{item.label}
|
|
90
85
|
</div>
|
|
91
|
-
{:else}
|
|
92
|
-
<DropdownMenuPrimitive.Item
|
|
93
|
-
disabled={subItem.disabled}
|
|
94
|
-
onSelect={() => subItem.onSelect?.()}
|
|
95
|
-
class={cn(
|
|
96
|
-
'relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[disabled]:cursor-default',
|
|
97
|
-
subItem.destructive && 'text-destructive data-[highlighted]:text-destructive'
|
|
98
|
-
)}
|
|
99
|
-
>
|
|
100
|
-
{subItem.label}
|
|
101
|
-
{#if subItem.shortcut}
|
|
102
|
-
<span class="ml-auto text-xs tracking-widest opacity-60">
|
|
103
|
-
{subItem.shortcut}
|
|
104
|
-
</span>
|
|
105
|
-
{/if}
|
|
106
|
-
</DropdownMenuPrimitive.Item>
|
|
107
86
|
{/if}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
87
|
+
{#each item.items as subItem}
|
|
88
|
+
{#if subItem.type === 'separator'}
|
|
89
|
+
<DropdownMenuPrimitive.Separator class="-mx-1 my-1 h-px bg-muted" />
|
|
90
|
+
{:else if subItem.type === 'label'}
|
|
91
|
+
<div class="px-2 py-1.5 text-sm font-semibold">
|
|
92
|
+
{subItem.label}
|
|
93
|
+
</div>
|
|
94
|
+
{:else}
|
|
95
|
+
<DropdownMenuPrimitive.Item
|
|
96
|
+
disabled={subItem.disabled}
|
|
97
|
+
onSelect={() => subItem.onSelect?.()}
|
|
98
|
+
class={cn(
|
|
99
|
+
'relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[disabled]:cursor-default',
|
|
100
|
+
subItem.destructive && 'text-destructive data-[highlighted]:text-destructive'
|
|
101
|
+
)}
|
|
102
|
+
>
|
|
103
|
+
{subItem.label}
|
|
104
|
+
{#if subItem.shortcut}
|
|
105
|
+
<span class="ml-auto text-xs tracking-widest opacity-60">
|
|
106
|
+
{subItem.shortcut}
|
|
107
|
+
</span>
|
|
108
|
+
{/if}
|
|
109
|
+
</DropdownMenuPrimitive.Item>
|
|
110
|
+
{/if}
|
|
111
|
+
{/each}
|
|
112
|
+
</DropdownMenuPrimitive.Group>
|
|
113
|
+
{:else if item.type === 'separator'}
|
|
114
|
+
<DropdownMenuPrimitive.Separator class="-mx-1 my-1 h-px bg-muted" />
|
|
115
|
+
{:else if item.type === 'label'}
|
|
116
|
+
<div class="px-2 py-1.5 text-sm font-semibold">
|
|
117
|
+
{item.label}
|
|
118
|
+
</div>
|
|
119
|
+
{:else}
|
|
120
|
+
<DropdownMenuPrimitive.Item
|
|
121
|
+
disabled={item.disabled}
|
|
122
|
+
onSelect={() => item.onSelect?.()}
|
|
123
|
+
class={cn(
|
|
124
|
+
'relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[disabled]:cursor-default',
|
|
125
|
+
item.destructive && 'text-destructive data-[highlighted]:text-destructive'
|
|
126
|
+
)}
|
|
127
|
+
>
|
|
128
|
+
{item.label}
|
|
129
|
+
{#if item.shortcut}
|
|
130
|
+
<span class="ml-auto text-xs tracking-widest opacity-60">
|
|
131
|
+
{item.shortcut}
|
|
132
|
+
</span>
|
|
133
|
+
{/if}
|
|
134
|
+
</DropdownMenuPrimitive.Item>
|
|
135
|
+
{/if}
|
|
136
|
+
{/each}
|
|
137
|
+
</div>
|
|
134
138
|
</DropdownMenuPrimitive.Content>
|
|
135
139
|
</DropdownMenuPrimitive.Root>
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from '../utils.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Available icon names for the Icon component.
|
|
6
|
+
* These are common icons useful across applications.
|
|
7
|
+
*/
|
|
8
|
+
export type IconName =
|
|
9
|
+
| 'close'
|
|
10
|
+
| 'check'
|
|
11
|
+
| 'plus'
|
|
12
|
+
| 'minus'
|
|
13
|
+
| 'edit'
|
|
14
|
+
| 'trash'
|
|
15
|
+
| 'info'
|
|
16
|
+
| 'alert-circle'
|
|
17
|
+
| 'alert-triangle'
|
|
18
|
+
| 'chevron-down'
|
|
19
|
+
| 'chevron-up'
|
|
20
|
+
| 'chevron-left'
|
|
21
|
+
| 'chevron-right'
|
|
22
|
+
| 'search'
|
|
23
|
+
| 'settings'
|
|
24
|
+
| 'user'
|
|
25
|
+
| 'home'
|
|
26
|
+
| 'external-link'
|
|
27
|
+
| 'loading'
|
|
28
|
+
| 'send'
|
|
29
|
+
| 'more'
|
|
30
|
+
| 'more-horizontal'
|
|
31
|
+
| 'grip'
|
|
32
|
+
| 'history'
|
|
33
|
+
| 'message'
|
|
34
|
+
| 'chat'
|
|
35
|
+
| 'eye'
|
|
36
|
+
| 'eye-off'
|
|
37
|
+
| 'copy'
|
|
38
|
+
| 'download'
|
|
39
|
+
| 'upload'
|
|
40
|
+
| 'refresh'
|
|
41
|
+
| 'arrow-left'
|
|
42
|
+
| 'arrow-right'
|
|
43
|
+
| 'menu'
|
|
44
|
+
| 'filter'
|
|
45
|
+
| 'sort'
|
|
46
|
+
| 'calendar'
|
|
47
|
+
| 'clock'
|
|
48
|
+
| 'mail'
|
|
49
|
+
| 'phone'
|
|
50
|
+
| 'link'
|
|
51
|
+
| 'image'
|
|
52
|
+
| 'file'
|
|
53
|
+
| 'folder'
|
|
54
|
+
| 'star'
|
|
55
|
+
| 'heart'
|
|
56
|
+
| 'bookmark'
|
|
57
|
+
| 'lock'
|
|
58
|
+
| 'unlock';
|
|
59
|
+
|
|
60
|
+
interface Props {
|
|
61
|
+
/** The name of the icon to display */
|
|
62
|
+
name: IconName;
|
|
63
|
+
/** Size of the icon in pixels (default: 24) */
|
|
64
|
+
size?: number;
|
|
65
|
+
/** Additional CSS classes */
|
|
66
|
+
class?: string;
|
|
67
|
+
/** Stroke width for stroke-based icons (default: 2) */
|
|
68
|
+
strokeWidth?: number;
|
|
69
|
+
/** Accessible label for the icon (renders as visually hidden text) */
|
|
70
|
+
label?: string;
|
|
71
|
+
/** Additional attributes to spread onto the SVG element */
|
|
72
|
+
[key: string]: unknown;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let { name, size = 24, class: className, strokeWidth = 2, label, ...restProps }: Props = $props();
|
|
76
|
+
|
|
77
|
+
// SVG path definitions for each icon
|
|
78
|
+
// All icons use a 24x24 viewBox
|
|
79
|
+
const icons: Record<IconName, { path: string; fill?: boolean }> = {
|
|
80
|
+
// Close (X)
|
|
81
|
+
close: {
|
|
82
|
+
path: 'M18 6L6 18M6 6l12 12',
|
|
83
|
+
},
|
|
84
|
+
// Check mark
|
|
85
|
+
check: {
|
|
86
|
+
path: 'M20 6L9 17l-5-5',
|
|
87
|
+
},
|
|
88
|
+
// Plus
|
|
89
|
+
plus: {
|
|
90
|
+
path: 'M12 5v14M5 12h14',
|
|
91
|
+
},
|
|
92
|
+
// Minus
|
|
93
|
+
minus: {
|
|
94
|
+
path: 'M5 12h14',
|
|
95
|
+
},
|
|
96
|
+
// Edit (pencil)
|
|
97
|
+
edit: {
|
|
98
|
+
path: 'M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5ZM15 5l4 4',
|
|
99
|
+
},
|
|
100
|
+
// Trash
|
|
101
|
+
trash: {
|
|
102
|
+
path: 'M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M10 11v6M14 11v6',
|
|
103
|
+
},
|
|
104
|
+
// Info
|
|
105
|
+
info: {
|
|
106
|
+
path: 'M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10zM12 16v-4M12 8h.01',
|
|
107
|
+
},
|
|
108
|
+
// Alert circle
|
|
109
|
+
'alert-circle': {
|
|
110
|
+
path: 'M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10zM12 8v4M12 16h.01',
|
|
111
|
+
},
|
|
112
|
+
// Alert triangle
|
|
113
|
+
'alert-triangle': {
|
|
114
|
+
path: 'M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0zM12 9v4M12 17h.01',
|
|
115
|
+
},
|
|
116
|
+
// Chevrons
|
|
117
|
+
'chevron-down': {
|
|
118
|
+
path: 'M6 9l6 6 6-6',
|
|
119
|
+
},
|
|
120
|
+
'chevron-up': {
|
|
121
|
+
path: 'M18 15l-6-6-6 6',
|
|
122
|
+
},
|
|
123
|
+
'chevron-left': {
|
|
124
|
+
path: 'M15 18l-6-6 6-6',
|
|
125
|
+
},
|
|
126
|
+
'chevron-right': {
|
|
127
|
+
path: 'M9 18l6-6-6-6',
|
|
128
|
+
},
|
|
129
|
+
// Search
|
|
130
|
+
search: {
|
|
131
|
+
path: 'M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM21 21l-4.35-4.35',
|
|
132
|
+
},
|
|
133
|
+
// Settings (gear)
|
|
134
|
+
settings: {
|
|
135
|
+
path: 'M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2zM12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z',
|
|
136
|
+
},
|
|
137
|
+
// User
|
|
138
|
+
user: {
|
|
139
|
+
path: 'M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2M12 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z',
|
|
140
|
+
},
|
|
141
|
+
// Home
|
|
142
|
+
home: {
|
|
143
|
+
path: 'M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V9zM9 22V12h6v10',
|
|
144
|
+
},
|
|
145
|
+
// External link
|
|
146
|
+
'external-link': {
|
|
147
|
+
path: 'M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14L21 3',
|
|
148
|
+
},
|
|
149
|
+
// Loading spinner (animated)
|
|
150
|
+
loading: {
|
|
151
|
+
path: '', // Special case - handled separately
|
|
152
|
+
},
|
|
153
|
+
// Send
|
|
154
|
+
send: {
|
|
155
|
+
path: 'M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z',
|
|
156
|
+
},
|
|
157
|
+
// More (vertical dots)
|
|
158
|
+
more: {
|
|
159
|
+
path: '', // Special case - uses filled circles
|
|
160
|
+
fill: true,
|
|
161
|
+
},
|
|
162
|
+
// More horizontal (horizontal dots)
|
|
163
|
+
'more-horizontal': {
|
|
164
|
+
path: '', // Special case - uses filled circles
|
|
165
|
+
fill: true,
|
|
166
|
+
},
|
|
167
|
+
// Grip (drag handle)
|
|
168
|
+
grip: {
|
|
169
|
+
path: '', // Special case - uses filled circles
|
|
170
|
+
fill: true,
|
|
171
|
+
},
|
|
172
|
+
// History (clock)
|
|
173
|
+
history: {
|
|
174
|
+
path: 'M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10zM12 6v6l4 2',
|
|
175
|
+
},
|
|
176
|
+
// Message bubble
|
|
177
|
+
message: {
|
|
178
|
+
path: 'M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z',
|
|
179
|
+
},
|
|
180
|
+
// Chat bubble (simpler)
|
|
181
|
+
chat: {
|
|
182
|
+
path: 'M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z',
|
|
183
|
+
},
|
|
184
|
+
// Eye
|
|
185
|
+
eye: {
|
|
186
|
+
path: 'M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8zM12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z',
|
|
187
|
+
},
|
|
188
|
+
// Eye off
|
|
189
|
+
'eye-off': {
|
|
190
|
+
path: 'M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24M1 1l22 22',
|
|
191
|
+
},
|
|
192
|
+
// Copy
|
|
193
|
+
copy: {
|
|
194
|
+
path: 'M20 9h-9a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2-2v-9a2 2 0 0 0-2-2zM5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1',
|
|
195
|
+
},
|
|
196
|
+
// Download
|
|
197
|
+
download: {
|
|
198
|
+
path: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3',
|
|
199
|
+
},
|
|
200
|
+
// Upload
|
|
201
|
+
upload: {
|
|
202
|
+
path: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12',
|
|
203
|
+
},
|
|
204
|
+
// Refresh
|
|
205
|
+
refresh: {
|
|
206
|
+
path: 'M23 4v6h-6M1 20v-6h6M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15',
|
|
207
|
+
},
|
|
208
|
+
// Arrow left
|
|
209
|
+
'arrow-left': {
|
|
210
|
+
path: 'M19 12H5M12 19l-7-7 7-7',
|
|
211
|
+
},
|
|
212
|
+
// Arrow right
|
|
213
|
+
'arrow-right': {
|
|
214
|
+
path: 'M5 12h14M12 5l7 7-7 7',
|
|
215
|
+
},
|
|
216
|
+
// Menu (hamburger)
|
|
217
|
+
menu: {
|
|
218
|
+
path: 'M3 12h18M3 6h18M3 18h18',
|
|
219
|
+
},
|
|
220
|
+
// Filter
|
|
221
|
+
filter: {
|
|
222
|
+
path: 'M22 3H2l8 9.46V19l4 2v-8.54L22 3z',
|
|
223
|
+
},
|
|
224
|
+
// Sort
|
|
225
|
+
sort: {
|
|
226
|
+
path: 'M11 5h10M11 9h7M11 13h4M3 17l3 3 3-3M6 18V4',
|
|
227
|
+
},
|
|
228
|
+
// Calendar
|
|
229
|
+
calendar: {
|
|
230
|
+
path: 'M19 4H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zM16 2v4M8 2v4M3 10h18',
|
|
231
|
+
},
|
|
232
|
+
// Clock
|
|
233
|
+
clock: {
|
|
234
|
+
path: 'M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10zM12 6v6l4 2',
|
|
235
|
+
},
|
|
236
|
+
// Mail
|
|
237
|
+
mail: {
|
|
238
|
+
path: 'M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2zM22 6l-10 7L2 6',
|
|
239
|
+
},
|
|
240
|
+
// Phone
|
|
241
|
+
phone: {
|
|
242
|
+
path: 'M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z',
|
|
243
|
+
},
|
|
244
|
+
// Link
|
|
245
|
+
link: {
|
|
246
|
+
path: 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71',
|
|
247
|
+
},
|
|
248
|
+
// Image
|
|
249
|
+
image: {
|
|
250
|
+
path: 'M19 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zM8.5 10a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zM21 15l-5-5L5 21',
|
|
251
|
+
},
|
|
252
|
+
// File
|
|
253
|
+
file: {
|
|
254
|
+
path: 'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8zM14 2v6h6M16 13H8M16 17H8M10 9H8',
|
|
255
|
+
},
|
|
256
|
+
// Folder
|
|
257
|
+
folder: {
|
|
258
|
+
path: 'M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z',
|
|
259
|
+
},
|
|
260
|
+
// Star
|
|
261
|
+
star: {
|
|
262
|
+
path: 'M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z',
|
|
263
|
+
},
|
|
264
|
+
// Heart
|
|
265
|
+
heart: {
|
|
266
|
+
path: 'M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z',
|
|
267
|
+
},
|
|
268
|
+
// Bookmark
|
|
269
|
+
bookmark: {
|
|
270
|
+
path: 'M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z',
|
|
271
|
+
},
|
|
272
|
+
// Lock
|
|
273
|
+
lock: {
|
|
274
|
+
path: 'M19 11H5a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7a2 2 0 0 0-2-2zM7 11V7a5 5 0 0 1 10 0v4',
|
|
275
|
+
},
|
|
276
|
+
// Unlock
|
|
277
|
+
unlock: {
|
|
278
|
+
path: 'M19 11H5a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7a2 2 0 0 0-2-2zM7 11V7a5 5 0 0 1 9.9-1',
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
const iconDef = $derived(icons[name]);
|
|
283
|
+
</script>
|
|
284
|
+
|
|
285
|
+
{#if name === 'loading'}
|
|
286
|
+
<!-- Loading spinner with animation -->
|
|
287
|
+
<svg
|
|
288
|
+
aria-hidden={!label}
|
|
289
|
+
width={size}
|
|
290
|
+
height={size}
|
|
291
|
+
viewBox="0 0 24 24"
|
|
292
|
+
fill="none"
|
|
293
|
+
stroke="currentColor"
|
|
294
|
+
stroke-width={strokeWidth}
|
|
295
|
+
class={cn('animate-spin', className)}
|
|
296
|
+
{...restProps}
|
|
297
|
+
>
|
|
298
|
+
<circle cx="12" cy="12" r="10" stroke-opacity="0.25"></circle>
|
|
299
|
+
<path d="M12 2a10 10 0 0 1 10 10" stroke-linecap="round"></path>
|
|
300
|
+
</svg>
|
|
301
|
+
{:else if name === 'more'}
|
|
302
|
+
<!-- Vertical dots -->
|
|
303
|
+
<svg
|
|
304
|
+
aria-hidden={!label}
|
|
305
|
+
width={size}
|
|
306
|
+
height={size}
|
|
307
|
+
viewBox="0 0 24 24"
|
|
308
|
+
fill="currentColor"
|
|
309
|
+
class={className}
|
|
310
|
+
{...restProps}
|
|
311
|
+
>
|
|
312
|
+
<circle cx="12" cy="5" r="1.5"></circle>
|
|
313
|
+
<circle cx="12" cy="12" r="1.5"></circle>
|
|
314
|
+
<circle cx="12" cy="19" r="1.5"></circle>
|
|
315
|
+
</svg>
|
|
316
|
+
{:else if name === 'more-horizontal'}
|
|
317
|
+
<!-- Horizontal dots -->
|
|
318
|
+
<svg
|
|
319
|
+
aria-hidden={!label}
|
|
320
|
+
width={size}
|
|
321
|
+
height={size}
|
|
322
|
+
viewBox="0 0 24 24"
|
|
323
|
+
fill="currentColor"
|
|
324
|
+
class={className}
|
|
325
|
+
{...restProps}
|
|
326
|
+
>
|
|
327
|
+
<circle cx="5" cy="12" r="1.5"></circle>
|
|
328
|
+
<circle cx="12" cy="12" r="1.5"></circle>
|
|
329
|
+
<circle cx="19" cy="12" r="1.5"></circle>
|
|
330
|
+
</svg>
|
|
331
|
+
{:else if name === 'grip'}
|
|
332
|
+
<!-- Grip handle (6 dots for drag) -->
|
|
333
|
+
<svg
|
|
334
|
+
aria-hidden={!label}
|
|
335
|
+
width={size}
|
|
336
|
+
height={size}
|
|
337
|
+
viewBox="0 0 24 24"
|
|
338
|
+
fill="currentColor"
|
|
339
|
+
class={className}
|
|
340
|
+
{...restProps}
|
|
341
|
+
>
|
|
342
|
+
<circle cx="8" cy="6" r="2"></circle>
|
|
343
|
+
<circle cx="16" cy="6" r="2"></circle>
|
|
344
|
+
<circle cx="8" cy="12" r="2"></circle>
|
|
345
|
+
<circle cx="16" cy="12" r="2"></circle>
|
|
346
|
+
<circle cx="8" cy="18" r="2"></circle>
|
|
347
|
+
<circle cx="16" cy="18" r="2"></circle>
|
|
348
|
+
</svg>
|
|
349
|
+
{:else if iconDef}
|
|
350
|
+
<!-- Standard path-based icons -->
|
|
351
|
+
<svg
|
|
352
|
+
aria-hidden={!label}
|
|
353
|
+
width={size}
|
|
354
|
+
height={size}
|
|
355
|
+
viewBox="0 0 24 24"
|
|
356
|
+
fill={iconDef.fill ? 'currentColor' : 'none'}
|
|
357
|
+
stroke={iconDef.fill ? 'none' : 'currentColor'}
|
|
358
|
+
stroke-width={iconDef.fill ? 0 : strokeWidth}
|
|
359
|
+
stroke-linecap="round"
|
|
360
|
+
stroke-linejoin="round"
|
|
361
|
+
class={className}
|
|
362
|
+
{...restProps}
|
|
363
|
+
>
|
|
364
|
+
<path d={iconDef.path}></path>
|
|
365
|
+
</svg>
|
|
366
|
+
{/if}
|
|
367
|
+
|
|
368
|
+
{#if label}
|
|
369
|
+
<span class="sr-only">{label}</span>
|
|
370
|
+
{/if}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Available icon names for the Icon component.
|
|
3
|
+
* These are common icons useful across applications.
|
|
4
|
+
*/
|
|
5
|
+
export type IconName = 'close' | 'check' | 'plus' | 'minus' | 'edit' | 'trash' | 'info' | 'alert-circle' | 'alert-triangle' | 'chevron-down' | 'chevron-up' | 'chevron-left' | 'chevron-right' | 'search' | 'settings' | 'user' | 'home' | 'external-link' | 'loading' | 'send' | 'more' | 'more-horizontal' | 'grip' | 'history' | 'message' | 'chat' | 'eye' | 'eye-off' | 'copy' | 'download' | 'upload' | 'refresh' | 'arrow-left' | 'arrow-right' | 'menu' | 'filter' | 'sort' | 'calendar' | 'clock' | 'mail' | 'phone' | 'link' | 'image' | 'file' | 'folder' | 'star' | 'heart' | 'bookmark' | 'lock' | 'unlock';
|
|
6
|
+
interface Props {
|
|
7
|
+
/** The name of the icon to display */
|
|
8
|
+
name: IconName;
|
|
9
|
+
/** Size of the icon in pixels (default: 24) */
|
|
10
|
+
size?: number;
|
|
11
|
+
/** Additional CSS classes */
|
|
12
|
+
class?: string;
|
|
13
|
+
/** Stroke width for stroke-based icons (default: 2) */
|
|
14
|
+
strokeWidth?: number;
|
|
15
|
+
/** Accessible label for the icon (renders as visually hidden text) */
|
|
16
|
+
label?: string;
|
|
17
|
+
/** Additional attributes to spread onto the SVG element */
|
|
18
|
+
[key: string]: unknown;
|
|
19
|
+
}
|
|
20
|
+
declare const Icon: import("svelte").Component<Props, {}, "">;
|
|
21
|
+
type Icon = ReturnType<typeof Icon>;
|
|
22
|
+
export default Icon;
|
|
@@ -185,8 +185,10 @@
|
|
|
185
185
|
</div>
|
|
186
186
|
|
|
187
187
|
<ComboboxPrimitive.Content
|
|
188
|
-
class="relative z-50
|
|
189
|
-
sideOffset={
|
|
188
|
+
class="relative z-50 w-[var(--bits-floating-anchor-width)] min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:border-t-0 data-[side=bottom]:rounded-t-none data-[side=top]:border-b-0 data-[side=top]:rounded-b-none 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2"
|
|
189
|
+
sideOffset={-1}
|
|
190
|
+
collisionPadding={8}
|
|
191
|
+
avoidCollisions={true}
|
|
190
192
|
>
|
|
191
193
|
<div class="p-1 max-h-[300px] overflow-y-auto">
|
|
192
194
|
{#if loading}
|