@nqmcreative/ui 0.4.0 → 0.4.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/README.md +17 -19
- package/dist/core/calendar.d.ts +1 -1
- package/dist/core/calendar.js +1 -1
- package/dist/core/date.d.ts +3 -3
- package/dist/core/date.js +1 -1
- package/dist/core/files.d.ts +2 -3
- package/dist/core/files.js +2 -3
- package/dist/core/index.d.ts +2 -4
- package/dist/core/index.js +2 -4
- package/dist/core/password.d.ts +1 -1
- package/dist/core/password.js +1 -1
- package/dist/core/tones.d.ts +10 -0
- package/dist/core/tones.js +19 -0
- package/dist/core/trigger.d.ts +8 -0
- package/dist/core/trigger.js +11 -0
- package/dist/styles/matte/Alert.svelte +2 -4
- package/dist/styles/matte/Avatar.svelte +16 -3
- package/dist/styles/matte/Breadcrumb.svelte +1 -4
- package/dist/styles/matte/Button.svelte +6 -2
- package/dist/styles/matte/Calendar.svelte +6 -9
- package/dist/styles/matte/Checkbox.svelte +49 -30
- package/dist/styles/matte/CheckboxGroup.svelte +15 -4
- package/dist/styles/matte/ColorInput.svelte +1 -4
- package/dist/styles/matte/Combobox.svelte +14 -8
- package/dist/styles/matte/CommandPalette.svelte +3 -6
- package/dist/styles/matte/ConfirmDialog.svelte +11 -6
- package/dist/styles/matte/ContextMenu.svelte +1 -4
- package/dist/styles/matte/CurrencyInput.svelte +1 -1
- package/dist/styles/matte/CurrencyInput.svelte.d.ts +1 -1
- package/dist/styles/matte/DatePicker.svelte +18 -11
- package/dist/styles/matte/DatePicker.svelte.d.ts +3 -0
- package/dist/styles/matte/Divider.svelte +1 -1
- package/dist/styles/matte/Drawer.svelte +11 -5
- package/dist/styles/matte/Dropdown.svelte +2 -5
- package/dist/styles/matte/Dropdown.svelte.d.ts +1 -1
- package/dist/styles/matte/Dropzone.svelte +9 -12
- package/dist/styles/matte/Dropzone.svelte.d.ts +1 -1
- package/dist/styles/matte/Field.svelte +59 -3
- package/dist/styles/matte/FileInput.svelte +11 -14
- package/dist/styles/matte/FileInput.svelte.d.ts +2 -2
- package/dist/styles/matte/Footer.svelte +15 -4
- package/dist/styles/matte/Footer.svelte.d.ts +1 -1
- package/dist/styles/matte/Input.svelte +9 -2
- package/dist/styles/matte/Label.svelte +3 -1
- package/dist/styles/matte/Link.svelte +13 -2
- package/dist/styles/matte/MenuItem.svelte +11 -4
- package/dist/styles/matte/Modal.svelte +11 -5
- package/dist/styles/matte/MultiSelect.svelte +5 -8
- package/dist/styles/matte/Navbar.svelte +5 -11
- package/dist/styles/matte/Pagination.svelte +3 -6
- package/dist/styles/matte/PinInput.svelte +1 -4
- package/dist/styles/matte/Radio.svelte +41 -22
- package/dist/styles/matte/RadioGroup.svelte +15 -4
- package/dist/styles/matte/SearchInput.svelte +1 -4
- package/dist/styles/matte/Select.svelte +4 -2
- package/dist/styles/matte/Sidebar.svelte +3 -6
- package/dist/styles/matte/Skeleton.svelte +2 -1
- package/dist/styles/matte/Spinner.svelte +2 -5
- package/dist/styles/matte/Spinner.svelte.d.ts +1 -1
- package/dist/styles/matte/Stat.svelte +27 -3
- package/dist/styles/matte/Switch.svelte +6 -1
- package/dist/styles/matte/Table.svelte +5 -8
- package/dist/styles/matte/Tabs.svelte +1 -1
- package/dist/styles/matte/TagsInput.svelte +5 -8
- package/dist/styles/matte/Textarea.svelte +4 -2
- package/dist/styles/matte/ThemeToggle.svelte +5 -8
- package/dist/styles/matte/TimeInput.svelte +2 -5
- package/dist/styles/matte/Toaster.svelte +23 -8
- package/dist/styles/matte/Tooltip.svelte +8 -1
- package/dist/styles/matte/index.d.ts +2 -3
- package/dist/styles/matte/index.js +1 -3
- package/dist/styles/matte/theme.css +21 -0
- package/dist/styles/paper/Alert.svelte +3 -5
- package/dist/styles/paper/Avatar.svelte +14 -3
- package/dist/styles/paper/Breadcrumb.svelte +1 -4
- package/dist/styles/paper/Button.svelte +5 -1
- package/dist/styles/paper/Calendar.svelte +6 -9
- package/dist/styles/paper/Card.svelte +1 -1
- package/dist/styles/paper/Checkbox.svelte +55 -36
- package/dist/styles/paper/CheckboxGroup.svelte +15 -4
- package/dist/styles/paper/ColorInput.svelte +1 -4
- package/dist/styles/paper/Combobox.svelte +14 -8
- package/dist/styles/paper/CommandPalette.svelte +3 -6
- package/dist/styles/paper/ConfirmDialog.svelte +9 -6
- package/dist/styles/paper/ContextMenu.svelte +1 -4
- package/dist/styles/paper/CurrencyInput.svelte +1 -1
- package/dist/styles/paper/CurrencyInput.svelte.d.ts +1 -1
- package/dist/styles/paper/DatePicker.svelte +18 -11
- package/dist/styles/paper/DatePicker.svelte.d.ts +3 -0
- package/dist/styles/paper/Divider.svelte +1 -1
- package/dist/styles/paper/Drawer.svelte +9 -5
- package/dist/styles/paper/Dropdown.svelte +2 -5
- package/dist/styles/paper/Dropdown.svelte.d.ts +1 -1
- package/dist/styles/paper/Dropzone.svelte +9 -12
- package/dist/styles/paper/Dropzone.svelte.d.ts +1 -1
- package/dist/styles/paper/Field.svelte +59 -3
- package/dist/styles/paper/FileInput.svelte +11 -14
- package/dist/styles/paper/FileInput.svelte.d.ts +2 -2
- package/dist/styles/paper/Footer.svelte +15 -4
- package/dist/styles/paper/Footer.svelte.d.ts +1 -1
- package/dist/styles/paper/Input.svelte +9 -2
- package/dist/styles/paper/Label.svelte +3 -1
- package/dist/styles/paper/Link.svelte +13 -2
- package/dist/styles/paper/MenuItem.svelte +11 -4
- package/dist/styles/paper/Modal.svelte +9 -5
- package/dist/styles/paper/MultiSelect.svelte +5 -8
- package/dist/styles/paper/Navbar.svelte +5 -11
- package/dist/styles/paper/Pagination.svelte +3 -6
- package/dist/styles/paper/PinInput.svelte +1 -4
- package/dist/styles/paper/Radio.svelte +41 -22
- package/dist/styles/paper/RadioGroup.svelte +15 -4
- package/dist/styles/paper/SearchInput.svelte +1 -4
- package/dist/styles/paper/SegmentedControl.svelte +2 -2
- package/dist/styles/paper/Select.svelte +4 -2
- package/dist/styles/paper/Sidebar.svelte +3 -6
- package/dist/styles/paper/Skeleton.svelte +2 -1
- package/dist/styles/paper/Spinner.svelte +2 -5
- package/dist/styles/paper/Spinner.svelte.d.ts +1 -1
- package/dist/styles/paper/Stat.svelte +24 -2
- package/dist/styles/paper/Switch.svelte +6 -1
- package/dist/styles/paper/Table.svelte +5 -8
- package/dist/styles/paper/Tabs.svelte +1 -1
- package/dist/styles/paper/TagsInput.svelte +5 -8
- package/dist/styles/paper/Textarea.svelte +4 -2
- package/dist/styles/paper/ThemeToggle.svelte +5 -8
- package/dist/styles/paper/TimeInput.svelte +2 -5
- package/dist/styles/paper/Toaster.svelte +22 -9
- package/dist/styles/paper/Tooltip.svelte +8 -1
- package/dist/styles/paper/index.d.ts +2 -3
- package/dist/styles/paper/index.js +1 -3
- package/dist/styles/paper/theme.css +21 -0
- package/dist/styles/sprout/Alert.svelte +4 -6
- package/dist/styles/sprout/Avatar.svelte +14 -3
- package/dist/styles/sprout/Breadcrumb.svelte +2 -5
- package/dist/styles/sprout/Button.svelte +8 -4
- package/dist/styles/sprout/Calendar.svelte +7 -10
- package/dist/styles/sprout/Card.svelte +2 -2
- package/dist/styles/sprout/Checkbox.svelte +55 -36
- package/dist/styles/sprout/CheckboxGroup.svelte +15 -4
- package/dist/styles/sprout/ColorInput.svelte +2 -5
- package/dist/styles/sprout/Combobox.svelte +15 -9
- package/dist/styles/sprout/CommandPalette.svelte +3 -6
- package/dist/styles/sprout/ConfirmDialog.svelte +9 -6
- package/dist/styles/sprout/ContextMenu.svelte +1 -4
- package/dist/styles/sprout/CurrencyInput.svelte +1 -1
- package/dist/styles/sprout/CurrencyInput.svelte.d.ts +1 -1
- package/dist/styles/sprout/DatePicker.svelte +20 -13
- package/dist/styles/sprout/DatePicker.svelte.d.ts +3 -0
- package/dist/styles/sprout/Divider.svelte +1 -1
- package/dist/styles/sprout/Drawer.svelte +9 -5
- package/dist/styles/sprout/Dropdown.svelte +2 -5
- package/dist/styles/sprout/Dropdown.svelte.d.ts +1 -1
- package/dist/styles/sprout/Dropzone.svelte +10 -13
- package/dist/styles/sprout/Dropzone.svelte.d.ts +1 -1
- package/dist/styles/sprout/Field.svelte +59 -3
- package/dist/styles/sprout/FileInput.svelte +14 -17
- package/dist/styles/sprout/FileInput.svelte.d.ts +2 -2
- package/dist/styles/sprout/Footer.svelte +15 -4
- package/dist/styles/sprout/Footer.svelte.d.ts +1 -1
- package/dist/styles/sprout/Input.svelte +9 -2
- package/dist/styles/sprout/Label.svelte +3 -1
- package/dist/styles/sprout/Link.svelte +13 -2
- package/dist/styles/sprout/LogoCloud.svelte +1 -1
- package/dist/styles/sprout/MenuItem.svelte +11 -4
- package/dist/styles/sprout/Modal.svelte +9 -5
- package/dist/styles/sprout/MultiSelect.svelte +8 -11
- package/dist/styles/sprout/Navbar.svelte +5 -11
- package/dist/styles/sprout/Pagination.svelte +3 -6
- package/dist/styles/sprout/PinInput.svelte +1 -4
- package/dist/styles/sprout/Radio.svelte +41 -22
- package/dist/styles/sprout/RadioGroup.svelte +15 -4
- package/dist/styles/sprout/SearchInput.svelte +2 -5
- package/dist/styles/sprout/SegmentedControl.svelte +2 -2
- package/dist/styles/sprout/Select.svelte +4 -2
- package/dist/styles/sprout/Sidebar.svelte +5 -7
- package/dist/styles/sprout/Skeleton.svelte +2 -1
- package/dist/styles/sprout/Spinner.svelte +2 -5
- package/dist/styles/sprout/Spinner.svelte.d.ts +1 -1
- package/dist/styles/sprout/Stat.svelte +24 -2
- package/dist/styles/sprout/Switch.svelte +6 -1
- package/dist/styles/sprout/Table.svelte +6 -9
- package/dist/styles/sprout/Tabs.svelte +1 -1
- package/dist/styles/sprout/TagsInput.svelte +6 -9
- package/dist/styles/sprout/Textarea.svelte +4 -2
- package/dist/styles/sprout/ThemeToggle.svelte +5 -8
- package/dist/styles/sprout/TimeInput.svelte +3 -6
- package/dist/styles/sprout/Toaster.svelte +25 -12
- package/dist/styles/sprout/Tooltip.svelte +8 -1
- package/dist/styles/sprout/index.d.ts +3 -4
- package/dist/styles/sprout/index.js +2 -4
- package/dist/styles/sprout/lift.d.ts +14 -0
- package/dist/styles/sprout/lift.js +23 -0
- package/dist/styles/sprout/theme.css +50 -24
- package/package.json +2 -19
- package/registry.json +7 -46
- package/dist/core/locale.svelte.d.ts +0 -101
- package/dist/core/locale.svelte.js +0 -167
- package/dist/styles/matte/LocaleProvider.svelte +0 -24
- package/dist/styles/matte/LocaleProvider.svelte.d.ts +0 -10
- package/dist/styles/paper/LocaleProvider.svelte +0 -24
- package/dist/styles/paper/LocaleProvider.svelte.d.ts +0 -10
- package/dist/styles/sprout/LocaleProvider.svelte +0 -24
- package/dist/styles/sprout/LocaleProvider.svelte.d.ts +0 -10
|
@@ -3,8 +3,14 @@
|
|
|
3
3
|
import { anchored } from '../../core/actions/anchor.js';
|
|
4
4
|
import { clickOutside, portal } from '../../core/actions/dismissable.js';
|
|
5
5
|
import { ListCursor, groupItems, matchQuery, revealIndex } from '../../core/list.svelte.js';
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import {
|
|
7
|
+
focusWithinRing,
|
|
8
|
+
toneFocusWithinBorder,
|
|
9
|
+
toneFocusWithinRing,
|
|
10
|
+
toneSoft,
|
|
11
|
+
toneText,
|
|
12
|
+
type Tone
|
|
13
|
+
} from '../../core/tones.js';
|
|
8
14
|
import { iconMd } from './icon.js';
|
|
9
15
|
|
|
10
16
|
export interface ComboboxOption {
|
|
@@ -51,8 +57,6 @@
|
|
|
51
57
|
class: className = ''
|
|
52
58
|
}: Props = $props();
|
|
53
59
|
|
|
54
|
-
const t = useLocale();
|
|
55
|
-
|
|
56
60
|
let open = $state(false);
|
|
57
61
|
let query = $state('');
|
|
58
62
|
let wrapper: HTMLElement | null = $state(null);
|
|
@@ -146,7 +150,9 @@
|
|
|
146
150
|
|
|
147
151
|
<div
|
|
148
152
|
bind:this={wrapper}
|
|
149
|
-
class="relative inline-flex w-full items-center gap-2 border bg-bg pr-2 pl-3.5 transition-colors duration-150 ease-brand-out
|
|
153
|
+
class="relative inline-flex w-full items-center gap-2 border bg-bg pr-2 pl-3.5 transition-colors duration-150 ease-brand-out {focusWithinRing} {toneFocusWithinRing[
|
|
154
|
+
tone
|
|
155
|
+
]}
|
|
150
156
|
{invalid ? 'border-danger' : `border-hairline ${toneFocusWithinBorder[tone]}`}
|
|
151
157
|
{disabled ? 'pointer-events-none opacity-50' : ''} {className}"
|
|
152
158
|
>
|
|
@@ -161,7 +167,7 @@
|
|
|
161
167
|
aria-controls="{id ?? 'combobox'}-listbox"
|
|
162
168
|
aria-activedescendant={open && flat[active] ? `${id ?? 'combobox'}-opt-${active}` : undefined}
|
|
163
169
|
aria-invalid={invalid ? 'true' : undefined}
|
|
164
|
-
placeholder={selected ? selected.label : (placeholder ??
|
|
170
|
+
placeholder={selected ? selected.label : (placeholder ?? 'Search…')}
|
|
165
171
|
value={open ? query : (selected?.label ?? '')}
|
|
166
172
|
oninput={(event) => {
|
|
167
173
|
query = event.currentTarget.value;
|
|
@@ -179,7 +185,7 @@
|
|
|
179
185
|
<button
|
|
180
186
|
type="button"
|
|
181
187
|
onclick={clear}
|
|
182
|
-
aria-label=
|
|
188
|
+
aria-label="Clear selection"
|
|
183
189
|
class="shrink-0 p-1.5 text-text-muted transition-colors duration-150 hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
|
|
184
190
|
>
|
|
185
191
|
<svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
@@ -212,7 +218,7 @@
|
|
|
212
218
|
>
|
|
213
219
|
{#if flat.length === 0}
|
|
214
220
|
<p class="px-3 py-6 text-center text-sm text-text-muted">
|
|
215
|
-
{emptyText ??
|
|
221
|
+
{emptyText ?? 'No matches'}
|
|
216
222
|
</p>
|
|
217
223
|
{:else}
|
|
218
224
|
{#each groups as [group, items] (group)}
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
import { focusTrap } from '../../core/actions/dismissable.js';
|
|
24
24
|
import { syncDialog } from '../../core/dialog.svelte.js';
|
|
25
25
|
import { ListCursor, groupItems, matchQuery, revealIndex } from '../../core/list.svelte.js';
|
|
26
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
27
26
|
import { toneSoft, type Tone } from '../../core/tones.js';
|
|
28
27
|
import { isTypingTarget } from '../../core/trigger.js';
|
|
29
28
|
import { iconMd } from './icon.js';
|
|
@@ -61,8 +60,6 @@
|
|
|
61
60
|
class: className = ''
|
|
62
61
|
}: Props = $props();
|
|
63
62
|
|
|
64
|
-
const t = useLocale();
|
|
65
|
-
|
|
66
63
|
let dialog: HTMLDialogElement | null = $state(null);
|
|
67
64
|
let inputEl: HTMLInputElement | null = $state(null);
|
|
68
65
|
let listEl: HTMLElement | null = $state(null);
|
|
@@ -182,8 +179,8 @@
|
|
|
182
179
|
oninput={() => cursor.reset()}
|
|
183
180
|
type="text"
|
|
184
181
|
autocomplete="off"
|
|
185
|
-
aria-label={placeholder ??
|
|
186
|
-
placeholder={placeholder ??
|
|
182
|
+
aria-label={placeholder ?? 'Type a command or search…'}
|
|
183
|
+
placeholder={placeholder ?? 'Type a command or search…'}
|
|
187
184
|
class="h-11 w-full min-w-0 bg-transparent font-sans text-[15px] text-text placeholder:text-text-muted focus:outline-none"
|
|
188
185
|
/>
|
|
189
186
|
<Kbd>esc</Kbd>
|
|
@@ -197,7 +194,7 @@
|
|
|
197
194
|
>
|
|
198
195
|
{#if flat.length === 0}
|
|
199
196
|
<p class="px-3.5 py-10 text-center font-sans text-sm text-text-muted">
|
|
200
|
-
{emptyText ??
|
|
197
|
+
{emptyText ?? 'No results'}
|
|
201
198
|
</p>
|
|
202
199
|
{:else}
|
|
203
200
|
{#each groups as [group, groupItems] (group)}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import Button from './Button.svelte';
|
|
4
4
|
import Modal from './Modal.svelte';
|
|
5
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
6
5
|
import { toneSoft, type Tone } from '../../core/tones.js';
|
|
7
6
|
|
|
8
7
|
interface Props {
|
|
@@ -37,7 +36,7 @@
|
|
|
37
36
|
children
|
|
38
37
|
}: Props = $props();
|
|
39
38
|
|
|
40
|
-
const
|
|
39
|
+
const uid = $props.id();
|
|
41
40
|
|
|
42
41
|
async function confirm() {
|
|
43
42
|
try {
|
|
@@ -58,6 +57,8 @@
|
|
|
58
57
|
|
|
59
58
|
<Modal
|
|
60
59
|
bind:open
|
|
60
|
+
aria-labelledby="{uid}-title"
|
|
61
|
+
aria-describedby={description ? `${uid}-description` : undefined}
|
|
61
62
|
size="sm"
|
|
62
63
|
dismissible={!loading}
|
|
63
64
|
showClose={false}
|
|
@@ -76,9 +77,13 @@
|
|
|
76
77
|
{/if}
|
|
77
78
|
</span>
|
|
78
79
|
<div class="flex min-w-0 flex-1 flex-col gap-2">
|
|
79
|
-
<h2 class="font-heading text-lg font-medium tracking-tight text-text">
|
|
80
|
+
<h2 id="{uid}-title" class="font-heading text-lg font-medium tracking-tight text-text">
|
|
81
|
+
{title}
|
|
82
|
+
</h2>
|
|
80
83
|
{#if description}
|
|
81
|
-
<p class="text-sm leading-relaxed text-text-secondary">
|
|
84
|
+
<p id="{uid}-description" class="text-sm leading-relaxed text-text-secondary">
|
|
85
|
+
{description}
|
|
86
|
+
</p>
|
|
82
87
|
{/if}
|
|
83
88
|
{#if children}
|
|
84
89
|
{@render children()}
|
|
@@ -88,10 +93,10 @@
|
|
|
88
93
|
|
|
89
94
|
{#snippet footer()}
|
|
90
95
|
<Button variant="ghost" tone="neutral" onclick={cancel} disabled={loading}>
|
|
91
|
-
{cancelLabel ??
|
|
96
|
+
{cancelLabel ?? 'Cancel'}
|
|
92
97
|
</Button>
|
|
93
98
|
<Button {tone} onclick={confirm} {loading}>
|
|
94
|
-
{confirmLabel ??
|
|
99
|
+
{confirmLabel ?? 'Confirm'}
|
|
95
100
|
</Button>
|
|
96
101
|
{/snippet}
|
|
97
102
|
</Modal>
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import { anchored } from '../../core/actions/anchor.js';
|
|
4
4
|
import { clickOutside, focusTrap, navigateList, portal } from '../../core/actions/dismissable.js';
|
|
5
5
|
import { menuItems } from '../../core/trigger.js';
|
|
6
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
7
6
|
|
|
8
7
|
interface Props {
|
|
9
8
|
/** The region that responds to a right-click. */
|
|
@@ -27,8 +26,6 @@
|
|
|
27
26
|
menuClass = ''
|
|
28
27
|
}: Props = $props();
|
|
29
28
|
|
|
30
|
-
const t = useLocale();
|
|
31
|
-
|
|
32
29
|
let menuEl: HTMLElement | null = $state(null);
|
|
33
30
|
/**
|
|
34
31
|
* A 0×0 element parked at the pointer, so the same `anchored` action that
|
|
@@ -79,7 +76,7 @@
|
|
|
79
76
|
if ((event.target as HTMLElement).closest('[role="menuitem"]:not([disabled])')) open = false;
|
|
80
77
|
}}
|
|
81
78
|
role="menu"
|
|
82
|
-
aria-label={label ??
|
|
79
|
+
aria-label={label ?? 'Menu'}
|
|
83
80
|
tabindex="-1"
|
|
84
81
|
class="z-50 flex max-h-[min(24rem,80vh)] min-w-44 flex-col overflow-y-auto border border-hairline bg-bg py-1 font-sans {menuClass}"
|
|
85
82
|
>
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
precision?: number;
|
|
21
21
|
/** Leading mark — `Rp`, `$`, `€`. */
|
|
22
22
|
currency?: string;
|
|
23
|
-
/** Trailing mark instead, for the
|
|
23
|
+
/** Trailing mark instead, for the regions that write it that way. */
|
|
24
24
|
unit?: string;
|
|
25
25
|
size?: InputSize;
|
|
26
26
|
tone?: Tone;
|
|
@@ -14,7 +14,7 @@ interface Props extends Omit<HTMLInputAttributes, 'size' | 'prefix' | 'suffix' |
|
|
|
14
14
|
precision?: number;
|
|
15
15
|
/** Leading mark — `Rp`, `$`, `€`. */
|
|
16
16
|
currency?: string;
|
|
17
|
-
/** Trailing mark instead, for the
|
|
17
|
+
/** Trailing mark instead, for the regions that write it that way. */
|
|
18
18
|
unit?: string;
|
|
19
19
|
size?: InputSize;
|
|
20
20
|
tone?: Tone;
|
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
import { anchored } from '../../core/actions/anchor.js';
|
|
3
3
|
import { clickOutside, portal } from '../../core/actions/dismissable.js';
|
|
4
4
|
import { dateHint } from '../../core/calendar.js';
|
|
5
|
-
import { formatISO, parseFormatted } from '../../core/date.js';
|
|
6
|
-
import {
|
|
7
|
-
|
|
5
|
+
import { formatISO, parseFormatted, type DateFormat } from '../../core/date.js';
|
|
6
|
+
import {
|
|
7
|
+
focusWithinRing,
|
|
8
|
+
toneFocusWithinBorder,
|
|
9
|
+
toneFocusWithinRing,
|
|
10
|
+
type Tone
|
|
11
|
+
} from '../../core/tones.js';
|
|
8
12
|
import Calendar from './Calendar.svelte';
|
|
9
13
|
|
|
10
14
|
interface Props {
|
|
@@ -15,6 +19,8 @@
|
|
|
15
19
|
max?: string;
|
|
16
20
|
isDisabled?: (date: Date) => boolean;
|
|
17
21
|
weekStart?: 0 | 1;
|
|
22
|
+
/** Order of the date parts the field accepts and prints. */
|
|
23
|
+
format?: DateFormat;
|
|
18
24
|
tone?: Tone;
|
|
19
25
|
invalid?: boolean;
|
|
20
26
|
disabled?: boolean;
|
|
@@ -33,6 +39,7 @@
|
|
|
33
39
|
max,
|
|
34
40
|
isDisabled,
|
|
35
41
|
weekStart = 1,
|
|
42
|
+
format = 'dmy',
|
|
36
43
|
tone = 'brand',
|
|
37
44
|
invalid = false,
|
|
38
45
|
disabled = false,
|
|
@@ -44,8 +51,6 @@
|
|
|
44
51
|
class: className = ''
|
|
45
52
|
}: Props = $props();
|
|
46
53
|
|
|
47
|
-
const t = useLocale();
|
|
48
|
-
|
|
49
54
|
let open = $state(false);
|
|
50
55
|
let text = $state('');
|
|
51
56
|
let editing = $state(false);
|
|
@@ -53,8 +58,8 @@
|
|
|
53
58
|
let inputEl: HTMLInputElement | null = $state(null);
|
|
54
59
|
|
|
55
60
|
/** While typing the field shows the raw text; otherwise the formatted value. */
|
|
56
|
-
const display = $derived(editing ? text : formatISO(value,
|
|
57
|
-
const hint = $derived(placeholder ?? dateHint(
|
|
61
|
+
const display = $derived(editing ? text : formatISO(value, format));
|
|
62
|
+
const hint = $derived(placeholder ?? dateHint(format));
|
|
58
63
|
|
|
59
64
|
function commit() {
|
|
60
65
|
editing = false;
|
|
@@ -65,7 +70,7 @@
|
|
|
65
70
|
}
|
|
66
71
|
return;
|
|
67
72
|
}
|
|
68
|
-
const parsed = parseFormatted(text,
|
|
73
|
+
const parsed = parseFormatted(text, format);
|
|
69
74
|
if (parsed && parsed !== value) {
|
|
70
75
|
value = parsed;
|
|
71
76
|
onchange?.(parsed);
|
|
@@ -94,7 +99,9 @@
|
|
|
94
99
|
|
|
95
100
|
<div
|
|
96
101
|
bind:this={wrapper}
|
|
97
|
-
class="inline-flex w-full items-center gap-1 border bg-bg pr-1 pl-3.5 transition-colors duration-150 ease-brand-out
|
|
102
|
+
class="inline-flex w-full items-center gap-1 border bg-bg pr-1 pl-3.5 transition-colors duration-150 ease-brand-out {focusWithinRing} {toneFocusWithinRing[
|
|
103
|
+
tone
|
|
104
|
+
]}
|
|
98
105
|
{invalid ? 'border-danger' : `border-hairline ${toneFocusWithinBorder[tone]}`}
|
|
99
106
|
{disabled ? 'pointer-events-none opacity-50' : ''} {className}"
|
|
100
107
|
>
|
|
@@ -132,7 +139,7 @@
|
|
|
132
139
|
onchange?.('');
|
|
133
140
|
inputEl?.focus();
|
|
134
141
|
}}
|
|
135
|
-
aria-label=
|
|
142
|
+
aria-label="Clear selection"
|
|
136
143
|
class="shrink-0 p-1.5 text-text-muted transition-colors duration-150 hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
|
|
137
144
|
>
|
|
138
145
|
<svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
@@ -145,7 +152,7 @@
|
|
|
145
152
|
type="button"
|
|
146
153
|
{disabled}
|
|
147
154
|
onclick={() => (open = !open)}
|
|
148
|
-
aria-label=
|
|
155
|
+
aria-label="Select a date"
|
|
149
156
|
aria-expanded={open}
|
|
150
157
|
class="shrink-0 p-2 text-text-muted transition-colors duration-150 hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
|
|
151
158
|
>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type DateFormat } from '../../core/date.js';
|
|
1
2
|
import { type Tone } from '../../core/tones.js';
|
|
2
3
|
interface Props {
|
|
3
4
|
/** Bindable `YYYY-MM-DD` — the same shape as `<input type="date">`. */
|
|
@@ -7,6 +8,8 @@ interface Props {
|
|
|
7
8
|
max?: string;
|
|
8
9
|
isDisabled?: (date: Date) => boolean;
|
|
9
10
|
weekStart?: 0 | 1;
|
|
11
|
+
/** Order of the date parts the field accepts and prints. */
|
|
12
|
+
format?: DateFormat;
|
|
10
13
|
tone?: Tone;
|
|
11
14
|
invalid?: boolean;
|
|
12
15
|
disabled?: boolean;
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
{...rest}
|
|
21
21
|
></div>
|
|
22
22
|
{:else if label}
|
|
23
|
-
<div class="flex items-center gap-4 {className}" {...rest}>
|
|
23
|
+
<div role="separator" class="flex items-center gap-4 {className}" {...rest}>
|
|
24
24
|
<span class="h-px flex-1 bg-hairline"></span>
|
|
25
25
|
<span class="font-mono text-xs tracking-wide text-text-muted uppercase">{label}</span>
|
|
26
26
|
<span class="h-px flex-1 bg-hairline"></span>
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import { iconLg } from './icon.js';
|
|
4
4
|
import type { HTMLDialogAttributes } from 'svelte/elements';
|
|
5
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
6
5
|
import { isBackdropClick, syncDialog } from '../../core/dialog.svelte.js';
|
|
7
6
|
|
|
8
7
|
export type DrawerSide = 'left' | 'right' | 'top' | 'bottom';
|
|
@@ -39,7 +38,8 @@
|
|
|
39
38
|
...rest
|
|
40
39
|
}: Props = $props();
|
|
41
40
|
|
|
42
|
-
const
|
|
41
|
+
const uid = $props.id();
|
|
42
|
+
|
|
43
43
|
let dialog: HTMLDialogElement | null = $state(null);
|
|
44
44
|
|
|
45
45
|
const horizontal = $derived(side === 'left' || side === 'right');
|
|
@@ -82,6 +82,8 @@
|
|
|
82
82
|
|
|
83
83
|
<dialog
|
|
84
84
|
bind:this={dialog}
|
|
85
|
+
aria-labelledby={title ? `${uid}-title` : undefined}
|
|
86
|
+
aria-describedby={description ? `${uid}-description` : undefined}
|
|
85
87
|
onclose={close}
|
|
86
88
|
onclick={(event) => {
|
|
87
89
|
if (dismissible && isBackdropClick(event, dialog)) close();
|
|
@@ -99,17 +101,21 @@
|
|
|
99
101
|
{#if icon}<span class={iconLg}>{@render icon()}</span>{/if}
|
|
100
102
|
<div class="flex min-w-0 flex-1 flex-col gap-1">
|
|
101
103
|
{#if title}
|
|
102
|
-
<h2 class="font-heading text-lg font-medium tracking-tight">
|
|
104
|
+
<h2 id="{uid}-title" class="font-heading text-lg font-medium tracking-tight">
|
|
105
|
+
{title}
|
|
106
|
+
</h2>
|
|
103
107
|
{/if}
|
|
104
108
|
{#if description}
|
|
105
|
-
<p class="font-sans text-sm text-text-secondary">
|
|
109
|
+
<p id="{uid}-description" class="font-sans text-sm text-text-secondary">
|
|
110
|
+
{description}
|
|
111
|
+
</p>
|
|
106
112
|
{/if}
|
|
107
113
|
</div>
|
|
108
114
|
{#if dismissible}
|
|
109
115
|
<button
|
|
110
116
|
type="button"
|
|
111
117
|
onclick={close}
|
|
112
|
-
aria-label=
|
|
118
|
+
aria-label="Close"
|
|
113
119
|
class="-m-1 shrink-0 p-1 text-text-muted transition-colors duration-150 hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand"
|
|
114
120
|
>
|
|
115
121
|
<svg class="size-4" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import { anchored, type Placement } from '../../core/actions/anchor.js';
|
|
4
4
|
import { clickOutside, focusTrap, navigateList, portal } from '../../core/actions/dismissable.js';
|
|
5
5
|
import { describeTrigger, menuItems, popupTrigger } from '../../core/trigger.js';
|
|
6
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
7
6
|
|
|
8
7
|
interface Props {
|
|
9
8
|
/** The control that opens the menu — put a `Button` in here. */
|
|
@@ -17,7 +16,7 @@
|
|
|
17
16
|
matchWidth?: boolean;
|
|
18
17
|
/** Close as soon as an item inside is activated. Default true. */
|
|
19
18
|
closeOnSelect?: boolean;
|
|
20
|
-
/** Accessible name for the menu. Defaults to
|
|
19
|
+
/** Accessible name for the menu. Defaults to `Menu`. */
|
|
21
20
|
label?: string;
|
|
22
21
|
class?: string;
|
|
23
22
|
/** Classes for the wrapper around the trigger. */
|
|
@@ -37,8 +36,6 @@
|
|
|
37
36
|
triggerClass = ''
|
|
38
37
|
}: Props = $props();
|
|
39
38
|
|
|
40
|
-
const t = useLocale();
|
|
41
|
-
|
|
42
39
|
let anchorEl: HTMLElement | null = $state(null);
|
|
43
40
|
let menuEl: HTMLElement | null = $state(null);
|
|
44
41
|
|
|
@@ -85,7 +82,7 @@
|
|
|
85
82
|
onkeydown={onMenuKeydown}
|
|
86
83
|
onclick={onMenuClick}
|
|
87
84
|
role="menu"
|
|
88
|
-
aria-label={label ??
|
|
85
|
+
aria-label={label ?? 'Menu'}
|
|
89
86
|
tabindex="-1"
|
|
90
87
|
class="z-50 flex max-h-[min(24rem,80vh)] min-w-44 flex-col overflow-y-auto border border-hairline bg-bg py-1 font-sans {className}"
|
|
91
88
|
>
|
|
@@ -12,7 +12,7 @@ interface Props {
|
|
|
12
12
|
matchWidth?: boolean;
|
|
13
13
|
/** Close as soon as an item inside is activated. Default true. */
|
|
14
14
|
closeOnSelect?: boolean;
|
|
15
|
-
/** Accessible name for the menu. Defaults to
|
|
15
|
+
/** Accessible name for the menu. Defaults to `Menu`. */
|
|
16
16
|
label?: string;
|
|
17
17
|
class?: string;
|
|
18
18
|
/** Classes for the wrapper around the trigger. */
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import { toneBorder, toneSoft, toneSurface, toneText, type Tone } from '../../core/tones.js';
|
|
4
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
5
4
|
import { formatSize as toReadableSize, sortFiles, type RejectedFile } from '../../core/files.js';
|
|
6
5
|
|
|
7
6
|
interface Props {
|
|
@@ -16,7 +15,7 @@
|
|
|
16
15
|
maxFiles?: number;
|
|
17
16
|
disabled?: boolean;
|
|
18
17
|
tone?: Tone;
|
|
19
|
-
/** Headline inside the zone. Defaults to
|
|
18
|
+
/** Headline inside the zone. Defaults to `Drop files here`. */
|
|
20
19
|
label?: string;
|
|
21
20
|
hint?: string;
|
|
22
21
|
/** Hide the built-in file list to render your own. */
|
|
@@ -45,8 +44,6 @@
|
|
|
45
44
|
class: className = ''
|
|
46
45
|
}: Props = $props();
|
|
47
46
|
|
|
48
|
-
const t = useLocale();
|
|
49
|
-
|
|
50
47
|
let input: HTMLInputElement | null = $state(null);
|
|
51
48
|
let dragging = $state(false);
|
|
52
49
|
let rejected = $state<RejectedFile[]>([]);
|
|
@@ -63,7 +60,7 @@
|
|
|
63
60
|
return parts.join(' · ');
|
|
64
61
|
});
|
|
65
62
|
|
|
66
|
-
const formatSize = (bytes: number) => toReadableSize(bytes
|
|
63
|
+
const formatSize = (bytes: number) => toReadableSize(bytes);
|
|
67
64
|
|
|
68
65
|
function add(incoming: FileList | File[] | null) {
|
|
69
66
|
if (!incoming || disabled) return;
|
|
@@ -96,9 +93,9 @@
|
|
|
96
93
|
}
|
|
97
94
|
|
|
98
95
|
const reasons: Record<RejectedFile['reason'], string> = $derived({
|
|
99
|
-
type:
|
|
100
|
-
size:
|
|
101
|
-
count:
|
|
96
|
+
type: 'wrong file type',
|
|
97
|
+
size: 'too large',
|
|
98
|
+
count: 'over the file limit'
|
|
102
99
|
});
|
|
103
100
|
</script>
|
|
104
101
|
|
|
@@ -135,10 +132,10 @@
|
|
|
135
132
|
{/if}
|
|
136
133
|
</span>
|
|
137
134
|
<span class="flex flex-col gap-1">
|
|
138
|
-
<span class="text-[15px] font-medium text-text">{label ??
|
|
135
|
+
<span class="text-[15px] font-medium text-text">{label ?? 'Drop files here'}</span>
|
|
139
136
|
<span class="text-sm text-text-secondary">
|
|
140
|
-
|
|
141
|
-
<span class="underline underline-offset-4">
|
|
137
|
+
or
|
|
138
|
+
<span class="underline underline-offset-4">browse</span>
|
|
142
139
|
</span>
|
|
143
140
|
{#if defaultHint}
|
|
144
141
|
<span class="font-mono text-xs text-text-muted">{defaultHint}</span>
|
|
@@ -180,7 +177,7 @@
|
|
|
180
177
|
<button
|
|
181
178
|
type="button"
|
|
182
179
|
onclick={() => remove(i)}
|
|
183
|
-
aria-label="
|
|
180
|
+
aria-label="Remove {file.name}"
|
|
184
181
|
class="-m-1 shrink-0 p-1 text-text-muted transition-colors duration-150 hover:text-danger focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-danger"
|
|
185
182
|
>
|
|
186
183
|
<svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
@@ -13,7 +13,7 @@ interface Props {
|
|
|
13
13
|
maxFiles?: number;
|
|
14
14
|
disabled?: boolean;
|
|
15
15
|
tone?: Tone;
|
|
16
|
-
/** Headline inside the zone. Defaults to
|
|
16
|
+
/** Headline inside the zone. Defaults to `Drop files here`. */
|
|
17
17
|
label?: string;
|
|
18
18
|
hint?: string;
|
|
19
19
|
/** Hide the built-in file list to render your own. */
|
|
@@ -24,9 +24,65 @@
|
|
|
24
24
|
children,
|
|
25
25
|
...rest
|
|
26
26
|
}: Props = $props();
|
|
27
|
+
|
|
28
|
+
const uid = $props.id();
|
|
29
|
+
const messageId = $derived(error ? `${uid}-error` : hint ? `${uid}-hint` : undefined);
|
|
30
|
+
|
|
31
|
+
interface Described {
|
|
32
|
+
for?: string;
|
|
33
|
+
messageId?: string;
|
|
34
|
+
required: boolean;
|
|
35
|
+
invalid: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A hint nobody can hear is not a hint. `aria-describedby` has to sit on the
|
|
40
|
+
* control, and the control arrives through `children` — out of reach of this
|
|
41
|
+
* template — so the field finds it once the DOM exists and keeps the three
|
|
42
|
+
* attributes in step itself. It touches only what it set: an `aria-invalid`
|
|
43
|
+
* the control already carries from its own `invalid` prop stays that
|
|
44
|
+
* control's business.
|
|
45
|
+
*/
|
|
46
|
+
function describes(node: HTMLElement, state: Described) {
|
|
47
|
+
let ourId: string | undefined;
|
|
48
|
+
let ownsInvalid = false;
|
|
49
|
+
|
|
50
|
+
const apply = (next: Described) => {
|
|
51
|
+
const control =
|
|
52
|
+
(next.for ? node.querySelector<HTMLElement>(`#${CSS.escape(next.for)}`) : null) ??
|
|
53
|
+
node.querySelector<HTMLElement>('input, select, textarea');
|
|
54
|
+
if (!control) return;
|
|
55
|
+
|
|
56
|
+
const ids = (control.getAttribute('aria-describedby') ?? '')
|
|
57
|
+
.split(/\s+/)
|
|
58
|
+
.filter((id) => id && id !== ourId);
|
|
59
|
+
if (next.messageId) ids.push(next.messageId);
|
|
60
|
+
if (ids.length) control.setAttribute('aria-describedby', ids.join(' '));
|
|
61
|
+
else control.removeAttribute('aria-describedby');
|
|
62
|
+
ourId = next.messageId;
|
|
63
|
+
|
|
64
|
+
if (next.required) control.setAttribute('aria-required', 'true');
|
|
65
|
+
else control.removeAttribute('aria-required');
|
|
66
|
+
|
|
67
|
+
if (next.invalid) {
|
|
68
|
+
ownsInvalid ||= !control.hasAttribute('aria-invalid');
|
|
69
|
+
control.setAttribute('aria-invalid', 'true');
|
|
70
|
+
} else if (ownsInvalid) {
|
|
71
|
+
control.removeAttribute('aria-invalid');
|
|
72
|
+
ownsInvalid = false;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
apply(state);
|
|
77
|
+
return { update: apply };
|
|
78
|
+
}
|
|
27
79
|
</script>
|
|
28
80
|
|
|
29
|
-
<div
|
|
81
|
+
<div
|
|
82
|
+
use:describes={{ for: htmlFor, messageId, required, invalid: Boolean(error) }}
|
|
83
|
+
class="flex w-full flex-col gap-2 {className}"
|
|
84
|
+
{...rest}
|
|
85
|
+
>
|
|
30
86
|
{#if label}
|
|
31
87
|
<label for={htmlFor} class="font-sans text-sm font-medium text-text">
|
|
32
88
|
{label}
|
|
@@ -35,8 +91,8 @@
|
|
|
35
91
|
{/if}
|
|
36
92
|
{@render children()}
|
|
37
93
|
{#if error}
|
|
38
|
-
<p class="font-sans text-sm text-danger">{error}</p>
|
|
94
|
+
<p id="{uid}-error" role="alert" class="font-sans text-sm text-danger">{error}</p>
|
|
39
95
|
{:else if hint}
|
|
40
|
-
<p class="font-sans text-sm text-text-muted">{hint}</p>
|
|
96
|
+
<p id="{uid}-hint" class="font-sans text-sm text-text-muted">{hint}</p>
|
|
41
97
|
{/if}
|
|
42
98
|
</div>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { formatSize as toReadableSize, sortFiles, type RejectedFile } from '../../core/files.js';
|
|
3
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
4
3
|
import { toneFocusWithinBorder, type Tone } from '../../core/tones.js';
|
|
5
4
|
import type { InputSize } from './Input.svelte';
|
|
6
5
|
|
|
@@ -18,9 +17,9 @@
|
|
|
18
17
|
size?: InputSize;
|
|
19
18
|
tone?: Tone;
|
|
20
19
|
invalid?: boolean;
|
|
21
|
-
/** Text on the button. Defaults to
|
|
20
|
+
/** Text on the button. Defaults to `Choose file`. */
|
|
22
21
|
label?: string;
|
|
23
|
-
/** Shown while nothing is chosen. Defaults to
|
|
22
|
+
/** Shown while nothing is chosen. Defaults to `No file chosen`. */
|
|
24
23
|
placeholder?: string;
|
|
25
24
|
/** Show the list of held files below the row. */
|
|
26
25
|
showList?: boolean;
|
|
@@ -50,18 +49,16 @@
|
|
|
50
49
|
class: className = ''
|
|
51
50
|
}: Props = $props();
|
|
52
51
|
|
|
53
|
-
const t = useLocale();
|
|
54
|
-
|
|
55
52
|
let input: HTMLInputElement | null = $state(null);
|
|
56
53
|
let rejected = $state<RejectedFile[]>([]);
|
|
57
54
|
|
|
58
|
-
const formatSize = (bytes: number) => toReadableSize(bytes
|
|
55
|
+
const formatSize = (bytes: number) => toReadableSize(bytes);
|
|
59
56
|
|
|
60
57
|
/** One file reads as its name and weight; several read as a count. */
|
|
61
58
|
const summary = $derived.by(() => {
|
|
62
|
-
if (files.length === 0) return placeholder ??
|
|
59
|
+
if (files.length === 0) return placeholder ?? 'No file chosen';
|
|
63
60
|
if (files.length === 1) return `${files[0].name} · ${formatSize(files[0].size)}`;
|
|
64
|
-
return `${files.length} $
|
|
61
|
+
return `${files.length} $selected`;
|
|
65
62
|
});
|
|
66
63
|
|
|
67
64
|
function add(incoming: FileList | File[] | null) {
|
|
@@ -90,9 +87,9 @@
|
|
|
90
87
|
}
|
|
91
88
|
|
|
92
89
|
const reasons: Record<RejectedFile['reason'], string> = $derived({
|
|
93
|
-
type:
|
|
94
|
-
size:
|
|
95
|
-
count:
|
|
90
|
+
type: 'wrong file type',
|
|
91
|
+
size: 'too large',
|
|
92
|
+
count: 'over the file limit'
|
|
96
93
|
});
|
|
97
94
|
|
|
98
95
|
const sizes: Record<InputSize, string> = {
|
|
@@ -124,7 +121,7 @@
|
|
|
124
121
|
class="shrink-0 border border-hairline bg-bg-alt font-mono tracking-wide text-text-secondary transition-colors duration-150 hover:bg-bg-inset hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current
|
|
125
122
|
{buttons[size]}"
|
|
126
123
|
>
|
|
127
|
-
{label ??
|
|
124
|
+
{label ?? 'Choose file'}
|
|
128
125
|
</button>
|
|
129
126
|
|
|
130
127
|
<span
|
|
@@ -137,7 +134,7 @@
|
|
|
137
134
|
<button
|
|
138
135
|
type="button"
|
|
139
136
|
onclick={clear}
|
|
140
|
-
aria-label=
|
|
137
|
+
aria-label="Remove"
|
|
141
138
|
class="mr-1 shrink-0 p-1 text-text-muted transition-colors duration-150 hover:text-danger focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-danger"
|
|
142
139
|
>
|
|
143
140
|
<svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
@@ -180,7 +177,7 @@
|
|
|
180
177
|
<button
|
|
181
178
|
type="button"
|
|
182
179
|
onclick={() => (files = files.filter((_, at) => at !== i))}
|
|
183
|
-
aria-label="
|
|
180
|
+
aria-label="Remove {file.name}"
|
|
184
181
|
class="-m-1 shrink-0 p-1 text-text-muted transition-colors duration-150 hover:text-danger focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-danger"
|
|
185
182
|
>
|
|
186
183
|
<svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
@@ -15,9 +15,9 @@ interface Props {
|
|
|
15
15
|
size?: InputSize;
|
|
16
16
|
tone?: Tone;
|
|
17
17
|
invalid?: boolean;
|
|
18
|
-
/** Text on the button. Defaults to
|
|
18
|
+
/** Text on the button. Defaults to `Choose file`. */
|
|
19
19
|
label?: string;
|
|
20
|
-
/** Shown while nothing is chosen. Defaults to
|
|
20
|
+
/** Shown while nothing is chosen. Defaults to `No file chosen`. */
|
|
21
21
|
placeholder?: string;
|
|
22
22
|
/** Show the list of held files below the row. */
|
|
23
23
|
showList?: boolean;
|