@lyeve-labs/ui-kit 0.8.2
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/LICENSE +21 -0
- package/README.md +199 -0
- package/dist/components/Accordion.svelte +48 -0
- package/dist/components/Accordion.svelte.d.ts +10 -0
- package/dist/components/AccordionItem.svelte +55 -0
- package/dist/components/AccordionItem.svelte.d.ts +10 -0
- package/dist/components/Alert.svelte +60 -0
- package/dist/components/Alert.svelte.d.ts +13 -0
- package/dist/components/Autocomplete.svelte +198 -0
- package/dist/components/Autocomplete.svelte.d.ts +23 -0
- package/dist/components/Avatar.svelte +65 -0
- package/dist/components/Avatar.svelte.d.ts +9 -0
- package/dist/components/AvatarGroup.svelte +45 -0
- package/dist/components/AvatarGroup.svelte.d.ts +14 -0
- package/dist/components/Badge.svelte +48 -0
- package/dist/components/Badge.svelte.d.ts +13 -0
- package/dist/components/Banner.svelte +73 -0
- package/dist/components/Banner.svelte.d.ts +13 -0
- package/dist/components/Breadcrumb.svelte +47 -0
- package/dist/components/Breadcrumb.svelte.d.ts +10 -0
- package/dist/components/Button.svelte +71 -0
- package/dist/components/Button.svelte.d.ts +18 -0
- package/dist/components/ButtonGroup.svelte +33 -0
- package/dist/components/ButtonGroup.svelte.d.ts +9 -0
- package/dist/components/Card.svelte +72 -0
- package/dist/components/Card.svelte.d.ts +17 -0
- package/dist/components/Checkbox.svelte +80 -0
- package/dist/components/Checkbox.svelte.d.ts +14 -0
- package/dist/components/DatePicker.svelte +274 -0
- package/dist/components/DatePicker.svelte.d.ts +17 -0
- package/dist/components/Divider.svelte +23 -0
- package/dist/components/Divider.svelte.d.ts +8 -0
- package/dist/components/Drawer.svelte +120 -0
- package/dist/components/Drawer.svelte.d.ts +16 -0
- package/dist/components/Dropdown.svelte +88 -0
- package/dist/components/Dropdown.svelte.d.ts +23 -0
- package/dist/components/EmptyState.svelte +51 -0
- package/dist/components/EmptyState.svelte.d.ts +13 -0
- package/dist/components/FileInput.svelte +102 -0
- package/dist/components/FileInput.svelte.d.ts +15 -0
- package/dist/components/Indicator.svelte +38 -0
- package/dist/components/Indicator.svelte.d.ts +11 -0
- package/dist/components/Input.svelte +60 -0
- package/dist/components/Input.svelte.d.ts +17 -0
- package/dist/components/Kbd.svelte +18 -0
- package/dist/components/Kbd.svelte.d.ts +8 -0
- package/dist/components/Label.svelte +30 -0
- package/dist/components/Label.svelte.d.ts +11 -0
- package/dist/components/Modal.svelte +105 -0
- package/dist/components/Modal.svelte.d.ts +14 -0
- package/dist/components/MultiSelect.svelte +212 -0
- package/dist/components/MultiSelect.svelte.d.ts +22 -0
- package/dist/components/NumberInput.svelte +98 -0
- package/dist/components/NumberInput.svelte.d.ts +15 -0
- package/dist/components/PageHeader.svelte +22 -0
- package/dist/components/PageHeader.svelte.d.ts +10 -0
- package/dist/components/Pagination.svelte +112 -0
- package/dist/components/Pagination.svelte.d.ts +10 -0
- package/dist/components/Progress.svelte +70 -0
- package/dist/components/Progress.svelte.d.ts +15 -0
- package/dist/components/Radio.svelte +65 -0
- package/dist/components/Radio.svelte.d.ts +14 -0
- package/dist/components/RadioGroup.svelte +96 -0
- package/dist/components/RadioGroup.svelte.d.ts +22 -0
- package/dist/components/SearchInput.svelte +81 -0
- package/dist/components/SearchInput.svelte.d.ts +10 -0
- package/dist/components/Select.svelte +69 -0
- package/dist/components/Select.svelte.d.ts +18 -0
- package/dist/components/Skeleton.svelte +20 -0
- package/dist/components/Skeleton.svelte.d.ts +9 -0
- package/dist/components/Spinner.svelte +19 -0
- package/dist/components/Spinner.svelte.d.ts +7 -0
- package/dist/components/Stat.svelte +45 -0
- package/dist/components/Stat.svelte.d.ts +14 -0
- package/dist/components/StepIndicator.svelte +142 -0
- package/dist/components/StepIndicator.svelte.d.ts +13 -0
- package/dist/components/Table.svelte +36 -0
- package/dist/components/Table.svelte.d.ts +11 -0
- package/dist/components/Tabs.svelte +39 -0
- package/dist/components/Tabs.svelte.d.ts +13 -0
- package/dist/components/Tag.svelte +56 -0
- package/dist/components/Tag.svelte.d.ts +11 -0
- package/dist/components/Textarea.svelte +75 -0
- package/dist/components/Textarea.svelte.d.ts +26 -0
- package/dist/components/ThemeToggle.svelte +33 -0
- package/dist/components/ThemeToggle.svelte.d.ts +6 -0
- package/dist/components/Toaster.svelte +54 -0
- package/dist/components/Toaster.svelte.d.ts +18 -0
- package/dist/components/Toggle.svelte +74 -0
- package/dist/components/Toggle.svelte.d.ts +11 -0
- package/dist/components/Tooltip.svelte +44 -0
- package/dist/components/Tooltip.svelte.d.ts +11 -0
- package/dist/components/dialog/ConfirmDialog.svelte +95 -0
- package/dist/components/dialog/ConfirmDialog.svelte.d.ts +8 -0
- package/dist/components/dialog/Dialog.svelte +225 -0
- package/dist/components/dialog/Dialog.svelte.d.ts +9 -0
- package/dist/components/dialog/DialogContainer.svelte +17 -0
- package/dist/components/dialog/DialogContainer.svelte.d.ts +18 -0
- package/dist/components/dialog/dialog-manager.svelte.d.ts +54 -0
- package/dist/components/dialog/dialog-manager.svelte.js +164 -0
- package/dist/components/dialog/types.d.ts +33 -0
- package/dist/components/dialog/types.js +10 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +72 -0
- package/dist/stores/toast.svelte.d.ts +18 -0
- package/dist/stores/toast.svelte.js +28 -0
- package/dist/styles/theme.css +148 -0
- package/dist/utils/cn.d.ts +13 -0
- package/dist/utils/cn.js +24 -0
- package/dist/utils/theme.d.ts +26 -0
- package/dist/utils/theme.js +55 -0
- package/package.json +81 -0
- package/src/lib/styles/theme.css +148 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
type SE = Event & { currentTarget: HTMLSelectElement };
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
value,
|
|
8
|
+
id,
|
|
9
|
+
name,
|
|
10
|
+
required = false,
|
|
11
|
+
disabled = false,
|
|
12
|
+
error,
|
|
13
|
+
class: cls = '',
|
|
14
|
+
onchange,
|
|
15
|
+
children,
|
|
16
|
+
}: {
|
|
17
|
+
value?: string | null;
|
|
18
|
+
id?: string;
|
|
19
|
+
name?: string;
|
|
20
|
+
required?: boolean;
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
error?: string;
|
|
23
|
+
class?: string;
|
|
24
|
+
onchange?: (e: SE) => void;
|
|
25
|
+
children?: Snippet;
|
|
26
|
+
} = $props();
|
|
27
|
+
|
|
28
|
+
const base =
|
|
29
|
+
'w-full bg-surface-2 rounded-lg px-3 py-2 text-sm text-fg ' +
|
|
30
|
+
'focus:outline-none transition-colors appearance-none cursor-pointer ' +
|
|
31
|
+
'disabled:opacity-50 disabled:cursor-not-allowed';
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<div class="flex flex-col gap-1 {cls}">
|
|
35
|
+
<div class="relative">
|
|
36
|
+
<select
|
|
37
|
+
{id}
|
|
38
|
+
{name}
|
|
39
|
+
{required}
|
|
40
|
+
{disabled}
|
|
41
|
+
value={value ?? ''}
|
|
42
|
+
{onchange}
|
|
43
|
+
class="{base}
|
|
44
|
+
{error
|
|
45
|
+
? 'border border-danger focus:border-danger/70'
|
|
46
|
+
: 'border border-line focus:border-brand/50'}
|
|
47
|
+
pr-8"
|
|
48
|
+
>
|
|
49
|
+
{@render children?.()}
|
|
50
|
+
</select>
|
|
51
|
+
<span
|
|
52
|
+
class="pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 text-faint"
|
|
53
|
+
aria-hidden="true"
|
|
54
|
+
>
|
|
55
|
+
<svg width="12" height="12" viewBox="0 0 12 12" fill="none">
|
|
56
|
+
<path
|
|
57
|
+
d="M2 4l4 4 4-4"
|
|
58
|
+
stroke="currentColor"
|
|
59
|
+
stroke-width="1.5"
|
|
60
|
+
stroke-linecap="round"
|
|
61
|
+
stroke-linejoin="round"
|
|
62
|
+
/>
|
|
63
|
+
</svg>
|
|
64
|
+
</span>
|
|
65
|
+
</div>
|
|
66
|
+
{#if error}
|
|
67
|
+
<p class="text-xs text-danger">{error}</p>
|
|
68
|
+
{/if}
|
|
69
|
+
</div>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type SE = Event & {
|
|
3
|
+
currentTarget: HTMLSelectElement;
|
|
4
|
+
};
|
|
5
|
+
type $$ComponentProps = {
|
|
6
|
+
value?: string | null;
|
|
7
|
+
id?: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
required?: boolean;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
error?: string;
|
|
12
|
+
class?: string;
|
|
13
|
+
onchange?: (e: SE) => void;
|
|
14
|
+
children?: Snippet;
|
|
15
|
+
};
|
|
16
|
+
declare const Select: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
17
|
+
type Select = ReturnType<typeof Select>;
|
|
18
|
+
export default Select;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
width?: string;
|
|
4
|
+
height?: string;
|
|
5
|
+
rounded?: string;
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
let {
|
|
9
|
+
width = '100%',
|
|
10
|
+
height = '1rem',
|
|
11
|
+
rounded = 'rounded-md',
|
|
12
|
+
class: klass = '',
|
|
13
|
+
}: Props = $props();
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<div
|
|
17
|
+
class="animate-pulse bg-surface-2 {rounded} {klass}"
|
|
18
|
+
style="width: {width}; height: {height};"
|
|
19
|
+
aria-hidden="true"
|
|
20
|
+
></div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
size?: number;
|
|
4
|
+
class?: string;
|
|
5
|
+
}
|
|
6
|
+
let { size = 16, class: klass = '' }: Props = $props();
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<svg
|
|
10
|
+
class="animate-spin shrink-0 {klass}"
|
|
11
|
+
width={size}
|
|
12
|
+
height={size}
|
|
13
|
+
viewBox="0 0 24 24"
|
|
14
|
+
fill="none"
|
|
15
|
+
aria-hidden="true"
|
|
16
|
+
>
|
|
17
|
+
<circle cx="12" cy="12" r="9" stroke="currentColor" stroke-width="3" opacity="0.25" />
|
|
18
|
+
<path d="M21 12a9 9 0 0 0-9-9" stroke="currentColor" stroke-width="3" stroke-linecap="round" />
|
|
19
|
+
</svg>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
type Trend = 'up' | 'down' | 'flat';
|
|
3
|
+
type Accent = 'brand' | 'violet' | 'success' | 'neutral';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
label: string;
|
|
7
|
+
value: string | number;
|
|
8
|
+
sub?: string;
|
|
9
|
+
trend?: Trend;
|
|
10
|
+
change?: string;
|
|
11
|
+
accent?: Accent;
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
label,
|
|
17
|
+
value,
|
|
18
|
+
sub = undefined,
|
|
19
|
+
trend = undefined,
|
|
20
|
+
change = undefined,
|
|
21
|
+
accent = 'brand',
|
|
22
|
+
class: klass = '',
|
|
23
|
+
}: Props = $props();
|
|
24
|
+
|
|
25
|
+
const accents = {
|
|
26
|
+
brand: 'text-brand',
|
|
27
|
+
violet: 'text-violet',
|
|
28
|
+
success: 'text-success',
|
|
29
|
+
neutral: 'text-fg',
|
|
30
|
+
} as const;
|
|
31
|
+
|
|
32
|
+
const trendColor = { up: 'text-success', down: 'text-danger', flat: 'text-muted' } as const;
|
|
33
|
+
const trendMark = { up: '↑', down: '↓', flat: '→' } as const;
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<div class="rounded-xl border border-line bg-surface p-5 {klass}">
|
|
37
|
+
<p class="text-xs font-medium uppercase tracking-wide text-faint">{label}</p>
|
|
38
|
+
<p class="mt-2 text-2xl font-bold {accents[accent]}">{value}</p>
|
|
39
|
+
<div class="mt-1 flex items-center gap-2">
|
|
40
|
+
{#if trend && change}
|
|
41
|
+
<span class="text-xs font-medium {trendColor[trend]}">{trendMark[trend]} {change}</span>
|
|
42
|
+
{/if}
|
|
43
|
+
{#if sub}<span class="text-xs text-muted">{sub}</span>{/if}
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type Trend = 'up' | 'down' | 'flat';
|
|
2
|
+
type Accent = 'brand' | 'violet' | 'success' | 'neutral';
|
|
3
|
+
interface Props {
|
|
4
|
+
label: string;
|
|
5
|
+
value: string | number;
|
|
6
|
+
sub?: string;
|
|
7
|
+
trend?: Trend;
|
|
8
|
+
change?: string;
|
|
9
|
+
accent?: Accent;
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
declare const Stat: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type Stat = ReturnType<typeof Stat>;
|
|
14
|
+
export default Stat;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Step {
|
|
3
|
+
label: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
steps: Step[];
|
|
9
|
+
current: number; // 1-based
|
|
10
|
+
orientation?: 'horizontal' | 'vertical';
|
|
11
|
+
class?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let { steps, current, orientation = 'horizontal', class: cls = '' }: Props = $props();
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
{#if orientation === 'horizontal'}
|
|
18
|
+
<nav aria-label="Progress" class="w-full {cls}">
|
|
19
|
+
<ol class="flex items-start">
|
|
20
|
+
{#each steps as step, i}
|
|
21
|
+
{@const idx = i + 1}
|
|
22
|
+
{@const done = idx < current}
|
|
23
|
+
{@const active = idx === current}
|
|
24
|
+
<li class="flex flex-1 flex-col items-center">
|
|
25
|
+
<div class="flex w-full items-center">
|
|
26
|
+
<!-- Left connector -->
|
|
27
|
+
<div
|
|
28
|
+
class="flex-1 {i === 0
|
|
29
|
+
? ''
|
|
30
|
+
: done || active
|
|
31
|
+
? 'bg-brand'
|
|
32
|
+
: 'bg-line'} h-px transition-colors"
|
|
33
|
+
></div>
|
|
34
|
+
|
|
35
|
+
<!-- Step circle -->
|
|
36
|
+
<div
|
|
37
|
+
class="flex h-8 w-8 shrink-0 items-center justify-center rounded-full border-2
|
|
38
|
+
font-bold text-sm transition-all
|
|
39
|
+
{done
|
|
40
|
+
? 'border-brand bg-brand text-ink'
|
|
41
|
+
: active
|
|
42
|
+
? 'border-brand bg-surface text-brand'
|
|
43
|
+
: 'border-line bg-surface text-faint'}"
|
|
44
|
+
>
|
|
45
|
+
{#if done}
|
|
46
|
+
<svg
|
|
47
|
+
width="13"
|
|
48
|
+
height="13"
|
|
49
|
+
viewBox="0 0 24 24"
|
|
50
|
+
fill="none"
|
|
51
|
+
stroke="currentColor"
|
|
52
|
+
stroke-width="2.5"
|
|
53
|
+
stroke-linecap="round"
|
|
54
|
+
aria-hidden="true"><path d="M20 6L9 17l-5-5" /></svg
|
|
55
|
+
>
|
|
56
|
+
{:else}{idx}{/if}
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<!-- Right connector -->
|
|
60
|
+
<div
|
|
61
|
+
class="flex-1 {i === steps.length - 1
|
|
62
|
+
? ''
|
|
63
|
+
: done
|
|
64
|
+
? 'bg-brand'
|
|
65
|
+
: 'bg-line'} h-px transition-colors"
|
|
66
|
+
></div>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div class="mt-2 px-1 text-center">
|
|
70
|
+
<p
|
|
71
|
+
class="text-xs font-medium {active
|
|
72
|
+
? 'text-fg'
|
|
73
|
+
: done
|
|
74
|
+
? 'text-muted'
|
|
75
|
+
: 'text-faint'} transition-colors"
|
|
76
|
+
>
|
|
77
|
+
{step.label}
|
|
78
|
+
</p>
|
|
79
|
+
{#if step.description}
|
|
80
|
+
<p class="mt-0.5 text-[10px] text-faint">{step.description}</p>
|
|
81
|
+
{/if}
|
|
82
|
+
</div>
|
|
83
|
+
</li>
|
|
84
|
+
{/each}
|
|
85
|
+
</ol>
|
|
86
|
+
</nav>
|
|
87
|
+
{:else}
|
|
88
|
+
<nav aria-label="Progress" class={cls}>
|
|
89
|
+
<ol class="flex flex-col">
|
|
90
|
+
{#each steps as step, i}
|
|
91
|
+
{@const idx = i + 1}
|
|
92
|
+
{@const done = idx < current}
|
|
93
|
+
{@const active = idx === current}
|
|
94
|
+
<li class="flex gap-4">
|
|
95
|
+
<div class="flex flex-col items-center">
|
|
96
|
+
<div
|
|
97
|
+
class="flex h-8 w-8 shrink-0 items-center justify-center rounded-full border-2
|
|
98
|
+
font-bold text-sm transition-all
|
|
99
|
+
{done
|
|
100
|
+
? 'border-brand bg-brand text-ink'
|
|
101
|
+
: active
|
|
102
|
+
? 'border-brand bg-surface text-brand'
|
|
103
|
+
: 'border-line bg-surface text-faint'}"
|
|
104
|
+
>
|
|
105
|
+
{#if done}
|
|
106
|
+
<svg
|
|
107
|
+
width="13"
|
|
108
|
+
height="13"
|
|
109
|
+
viewBox="0 0 24 24"
|
|
110
|
+
fill="none"
|
|
111
|
+
stroke="currentColor"
|
|
112
|
+
stroke-width="2.5"
|
|
113
|
+
stroke-linecap="round"
|
|
114
|
+
aria-hidden="true"><path d="M20 6L9 17l-5-5" /></svg
|
|
115
|
+
>
|
|
116
|
+
{:else}{idx}{/if}
|
|
117
|
+
</div>
|
|
118
|
+
{#if i < steps.length - 1}
|
|
119
|
+
<div
|
|
120
|
+
class="my-1 w-px flex-1 {done ? 'bg-brand/40' : 'bg-line'} transition-colors"
|
|
121
|
+
></div>
|
|
122
|
+
{/if}
|
|
123
|
+
</div>
|
|
124
|
+
<div class="pb-6 pt-1">
|
|
125
|
+
<p
|
|
126
|
+
class="text-sm font-medium {active
|
|
127
|
+
? 'text-fg'
|
|
128
|
+
: done
|
|
129
|
+
? 'text-muted'
|
|
130
|
+
: 'text-faint'} transition-colors"
|
|
131
|
+
>
|
|
132
|
+
{step.label}
|
|
133
|
+
</p>
|
|
134
|
+
{#if step.description}
|
|
135
|
+
<p class="mt-0.5 text-xs text-faint">{step.description}</p>
|
|
136
|
+
{/if}
|
|
137
|
+
</div>
|
|
138
|
+
</li>
|
|
139
|
+
{/each}
|
|
140
|
+
</ol>
|
|
141
|
+
</nav>
|
|
142
|
+
{/if}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface Step {
|
|
2
|
+
label: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
}
|
|
5
|
+
interface Props {
|
|
6
|
+
steps: Step[];
|
|
7
|
+
current: number;
|
|
8
|
+
orientation?: 'horizontal' | 'vertical';
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
declare const StepIndicator: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type StepIndicator = ReturnType<typeof StepIndicator>;
|
|
13
|
+
export default StepIndicator;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
striped?: boolean;
|
|
6
|
+
hoverable?: boolean;
|
|
7
|
+
fixed?: boolean;
|
|
8
|
+
class?: string;
|
|
9
|
+
children: Snippet;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
striped = false,
|
|
14
|
+
hoverable = true,
|
|
15
|
+
fixed = false,
|
|
16
|
+
class: cls = '',
|
|
17
|
+
children,
|
|
18
|
+
}: Props = $props();
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<div class="w-full overflow-x-auto rounded-xl border border-line {cls}">
|
|
22
|
+
<table
|
|
23
|
+
class="w-full text-sm text-left
|
|
24
|
+
{fixed ? 'table-fixed' : ''}
|
|
25
|
+
[&_thead_tr]:border-b [&_thead_tr]:border-line
|
|
26
|
+
[&_thead_th]:px-4 [&_thead_th]:py-3 [&_thead_th]:text-xs
|
|
27
|
+
[&_thead_th]:font-medium [&_thead_th]:text-faint
|
|
28
|
+
[&_thead_th]:uppercase [&_thead_th]:tracking-wider [&_thead_th]:whitespace-nowrap
|
|
29
|
+
[&_tbody_td]:px-4 [&_tbody_td]:py-3 [&_tbody_td]:text-fg [&_tbody_td]:align-middle
|
|
30
|
+
[&_tbody_tr]:border-b [&_tbody_tr]:border-line [&_tbody_tr:last-child]:border-0
|
|
31
|
+
{striped ? '[&_tbody_tr:nth-child(even)]:bg-surface-2/40' : ''}
|
|
32
|
+
{hoverable ? '[&_tbody_tr:hover]:bg-surface-2/60 [&_tbody_tr]:transition-colors' : ''}"
|
|
33
|
+
>
|
|
34
|
+
{@render children()}
|
|
35
|
+
</table>
|
|
36
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
striped?: boolean;
|
|
4
|
+
hoverable?: boolean;
|
|
5
|
+
fixed?: boolean;
|
|
6
|
+
class?: string;
|
|
7
|
+
children: Snippet;
|
|
8
|
+
}
|
|
9
|
+
declare const Table: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type Table = ReturnType<typeof Table>;
|
|
11
|
+
export default Table;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
items,
|
|
4
|
+
active,
|
|
5
|
+
class: cls = '',
|
|
6
|
+
onchange,
|
|
7
|
+
}: {
|
|
8
|
+
items: { id: string; label: string; count?: number }[];
|
|
9
|
+
active: string;
|
|
10
|
+
class?: string;
|
|
11
|
+
onchange: (id: string) => void;
|
|
12
|
+
} = $props();
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<div role="tablist" class="flex gap-0.5 border-b border-line {cls}">
|
|
16
|
+
{#each items as tab (tab.id)}
|
|
17
|
+
<button
|
|
18
|
+
type="button"
|
|
19
|
+
role="tab"
|
|
20
|
+
aria-selected={active === tab.id}
|
|
21
|
+
onclick={() => onchange(tab.id)}
|
|
22
|
+
class="relative px-4 py-2.5 text-sm font-medium transition-colors whitespace-nowrap
|
|
23
|
+
{active === tab.id
|
|
24
|
+
? 'text-fg after:absolute after:bottom-0 after:inset-x-0 after:h-0.5 after:bg-brand after:rounded-t'
|
|
25
|
+
: 'text-faint hover:text-fg'}"
|
|
26
|
+
>
|
|
27
|
+
{tab.label}
|
|
28
|
+
{#if tab.count !== undefined}
|
|
29
|
+
<span
|
|
30
|
+
class="ml-1.5 inline-flex items-center justify-center min-w-4.5 h-4.5 px-1
|
|
31
|
+
rounded-full text-[10px] font-semibold
|
|
32
|
+
{active === tab.id ? 'bg-brand/40 text-brand' : 'bg-surface-2 text-faint'}"
|
|
33
|
+
>
|
|
34
|
+
{tab.count}
|
|
35
|
+
</span>
|
|
36
|
+
{/if}
|
|
37
|
+
</button>
|
|
38
|
+
{/each}
|
|
39
|
+
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
items: {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
count?: number;
|
|
6
|
+
}[];
|
|
7
|
+
active: string;
|
|
8
|
+
class?: string;
|
|
9
|
+
onchange: (id: string) => void;
|
|
10
|
+
};
|
|
11
|
+
declare const Tabs: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
12
|
+
type Tabs = ReturnType<typeof Tabs>;
|
|
13
|
+
export default Tabs;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
type Tone = 'neutral' | 'brand' | 'violet' | 'success' | 'warn' | 'danger';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
label: string;
|
|
6
|
+
tone?: Tone;
|
|
7
|
+
removable?: boolean;
|
|
8
|
+
onremove?: () => void;
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
label,
|
|
14
|
+
tone = 'neutral',
|
|
15
|
+
removable = false,
|
|
16
|
+
onremove = undefined,
|
|
17
|
+
class: cls = '',
|
|
18
|
+
}: Props = $props();
|
|
19
|
+
|
|
20
|
+
const tones: Record<Tone, string> = {
|
|
21
|
+
neutral: 'bg-surface-2 text-muted border-line',
|
|
22
|
+
brand: 'bg-brand/10 text-brand border-brand/20',
|
|
23
|
+
violet: 'bg-violet/10 text-violet border-violet/20',
|
|
24
|
+
success: 'bg-success/10 text-success border-success/20',
|
|
25
|
+
warn: 'bg-warn/10 text-warn border-warn/20',
|
|
26
|
+
danger: 'bg-danger/10 text-danger border-danger/20',
|
|
27
|
+
};
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<span
|
|
31
|
+
class="inline-flex items-center gap-1 rounded-md border px-2 py-0.5
|
|
32
|
+
text-xs font-medium {tones[tone]} {cls}"
|
|
33
|
+
>
|
|
34
|
+
{label}
|
|
35
|
+
{#if removable}
|
|
36
|
+
<button
|
|
37
|
+
type="button"
|
|
38
|
+
onclick={onremove}
|
|
39
|
+
aria-label="Remove {label}"
|
|
40
|
+
class="ml-0.5 rounded transition-opacity hover:opacity-60 leading-none"
|
|
41
|
+
>
|
|
42
|
+
<svg
|
|
43
|
+
width="10"
|
|
44
|
+
height="10"
|
|
45
|
+
viewBox="0 0 24 24"
|
|
46
|
+
fill="none"
|
|
47
|
+
stroke="currentColor"
|
|
48
|
+
stroke-width="2.5"
|
|
49
|
+
stroke-linecap="round"
|
|
50
|
+
aria-hidden="true"
|
|
51
|
+
>
|
|
52
|
+
<path d="M18 6L6 18M6 6l12 12" />
|
|
53
|
+
</svg>
|
|
54
|
+
</button>
|
|
55
|
+
{/if}
|
|
56
|
+
</span>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type Tone = 'neutral' | 'brand' | 'violet' | 'success' | 'warn' | 'danger';
|
|
2
|
+
interface Props {
|
|
3
|
+
label: string;
|
|
4
|
+
tone?: Tone;
|
|
5
|
+
removable?: boolean;
|
|
6
|
+
onremove?: () => void;
|
|
7
|
+
class?: string;
|
|
8
|
+
}
|
|
9
|
+
declare const Tag: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type Tag = ReturnType<typeof Tag>;
|
|
11
|
+
export default Tag;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
type TA = HTMLTextAreaElement;
|
|
3
|
+
type TAE = Event & { currentTarget: TA };
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
value = $bindable(''),
|
|
7
|
+
id,
|
|
8
|
+
name,
|
|
9
|
+
placeholder,
|
|
10
|
+
rows = 4,
|
|
11
|
+
maxlength,
|
|
12
|
+
required = false,
|
|
13
|
+
disabled = false,
|
|
14
|
+
readonly = false,
|
|
15
|
+
resize = true,
|
|
16
|
+
error,
|
|
17
|
+
hint,
|
|
18
|
+
class: cls = '',
|
|
19
|
+
oninput,
|
|
20
|
+
onblur,
|
|
21
|
+
}: {
|
|
22
|
+
value?: string;
|
|
23
|
+
id?: string;
|
|
24
|
+
name?: string;
|
|
25
|
+
placeholder?: string;
|
|
26
|
+
rows?: number;
|
|
27
|
+
maxlength?: number;
|
|
28
|
+
required?: boolean;
|
|
29
|
+
disabled?: boolean;
|
|
30
|
+
readonly?: boolean;
|
|
31
|
+
resize?: boolean;
|
|
32
|
+
error?: string;
|
|
33
|
+
hint?: string;
|
|
34
|
+
class?: string;
|
|
35
|
+
oninput?: (e: TAE) => void;
|
|
36
|
+
onblur?: (e: FocusEvent & { currentTarget: TA }) => void;
|
|
37
|
+
} = $props();
|
|
38
|
+
|
|
39
|
+
const base =
|
|
40
|
+
'w-full bg-surface-2 rounded-lg px-3 py-2 text-sm text-fg ' +
|
|
41
|
+
'focus:outline-none transition-colors ' +
|
|
42
|
+
'placeholder:text-faint ' +
|
|
43
|
+
'disabled:opacity-50 disabled:cursor-not-allowed';
|
|
44
|
+
|
|
45
|
+
function handleInput(e: TAE) {
|
|
46
|
+
value = e.currentTarget.value;
|
|
47
|
+
oninput?.(e);
|
|
48
|
+
}
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<div class="flex flex-col gap-1 {cls}">
|
|
52
|
+
<textarea
|
|
53
|
+
{id}
|
|
54
|
+
{name}
|
|
55
|
+
{rows}
|
|
56
|
+
{maxlength}
|
|
57
|
+
{required}
|
|
58
|
+
{disabled}
|
|
59
|
+
{readonly}
|
|
60
|
+
{placeholder}
|
|
61
|
+
class="{base}
|
|
62
|
+
{error
|
|
63
|
+
? 'border border-danger focus:border-danger/70'
|
|
64
|
+
: 'border border-line focus:border-brand/50'}
|
|
65
|
+
{resize ? 'resize-y' : 'resize-none'}"
|
|
66
|
+
oninput={handleInput}
|
|
67
|
+
{onblur}>{value}</textarea
|
|
68
|
+
>
|
|
69
|
+
{#if error}
|
|
70
|
+
<p class="text-xs text-danger">{error}</p>
|
|
71
|
+
{/if}
|
|
72
|
+
{#if hint}
|
|
73
|
+
<p class="text-xs text-faint">{hint}</p>
|
|
74
|
+
{/if}
|
|
75
|
+
</div>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
type TA = HTMLTextAreaElement;
|
|
2
|
+
type TAE = Event & {
|
|
3
|
+
currentTarget: TA;
|
|
4
|
+
};
|
|
5
|
+
type $$ComponentProps = {
|
|
6
|
+
value?: string;
|
|
7
|
+
id?: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
placeholder?: string;
|
|
10
|
+
rows?: number;
|
|
11
|
+
maxlength?: number;
|
|
12
|
+
required?: boolean;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
readonly?: boolean;
|
|
15
|
+
resize?: boolean;
|
|
16
|
+
error?: string;
|
|
17
|
+
hint?: string;
|
|
18
|
+
class?: string;
|
|
19
|
+
oninput?: (e: TAE) => void;
|
|
20
|
+
onblur?: (e: FocusEvent & {
|
|
21
|
+
currentTarget: TA;
|
|
22
|
+
}) => void;
|
|
23
|
+
};
|
|
24
|
+
declare const Textarea: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
25
|
+
type Textarea = ReturnType<typeof Textarea>;
|
|
26
|
+
export default Textarea;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Moon, Sun } from '@lucide/svelte';
|
|
3
|
+
import { getTheme, toggleTheme, type Theme } from '../utils/theme.js';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let { class: klass = '' }: Props = $props();
|
|
10
|
+
|
|
11
|
+
let theme = $state<Theme>('dark');
|
|
12
|
+
|
|
13
|
+
$effect(() => {
|
|
14
|
+
theme = getTheme();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
function flip() {
|
|
18
|
+
theme = toggleTheme();
|
|
19
|
+
}
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<button
|
|
23
|
+
type="button"
|
|
24
|
+
onclick={flip}
|
|
25
|
+
aria-label={theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}
|
|
26
|
+
class="rounded-lg p-2 text-faint transition-colors hover:bg-surface-2 hover:text-fg {klass}"
|
|
27
|
+
>
|
|
28
|
+
{#if theme === 'dark'}
|
|
29
|
+
<Sun size={16} aria-hidden="true" />
|
|
30
|
+
{:else}
|
|
31
|
+
<Moon size={16} aria-hidden="true" />
|
|
32
|
+
{/if}
|
|
33
|
+
</button>
|