@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
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
toneText,
|
|
9
9
|
type Tone
|
|
10
10
|
} from '../../core/tones.js';
|
|
11
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
12
11
|
import { edge } from './lift.js';
|
|
13
12
|
|
|
14
13
|
export type AlertVariant = 'soft' | 'outline' | 'accent';
|
|
@@ -37,13 +36,12 @@
|
|
|
37
36
|
...rest
|
|
38
37
|
}: Props = $props();
|
|
39
38
|
|
|
40
|
-
const t = useLocale();
|
|
41
39
|
let open = $state(true);
|
|
42
40
|
|
|
43
41
|
const variants = $derived({
|
|
44
42
|
soft: `${toneSurface[tone]} border ${toneBorderSoft[tone]}`,
|
|
45
43
|
outline: `bg-bg ${edge} border ${toneBorderSoft[tone]}`,
|
|
46
|
-
accent: `${toneSurface[tone]} border-l-
|
|
44
|
+
accent: `${toneSurface[tone]} border-l-2 ${toneBorder[tone]}`
|
|
47
45
|
});
|
|
48
46
|
|
|
49
47
|
function dismiss() {
|
|
@@ -72,7 +70,7 @@
|
|
|
72
70
|
|
|
73
71
|
<div class="flex min-w-0 flex-1 flex-col gap-1">
|
|
74
72
|
{#if title}
|
|
75
|
-
<p class="text-sm leading-snug font-semibold {toneText[tone]}">{title}</p>
|
|
73
|
+
<p class="font-heading text-sm leading-snug font-semibold {toneText[tone]}">{title}</p>
|
|
76
74
|
{/if}
|
|
77
75
|
{#if children}
|
|
78
76
|
<div class="text-[13px] leading-relaxed text-text-secondary">{@render children()}</div>
|
|
@@ -83,8 +81,8 @@
|
|
|
83
81
|
<button
|
|
84
82
|
type="button"
|
|
85
83
|
onclick={dismiss}
|
|
86
|
-
aria-label=
|
|
87
|
-
class="-m-1 shrink-0 rounded p-1 text-text-muted transition-colors duration-150 hover:bg-bg/60 hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
|
|
84
|
+
aria-label="Dismiss"
|
|
85
|
+
class="-m-1 shrink-0 rounded-xl p-1 text-text-muted transition-colors duration-150 hover:bg-bg/60 hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
|
|
88
86
|
>
|
|
89
87
|
<svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
90
88
|
<path
|
|
@@ -47,6 +47,12 @@
|
|
|
47
47
|
xl: 'grid place-items-center *:size-8'
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
+
// A src that 404s left the reader with the browser's broken-image mark while
|
|
51
|
+
// the initials sat right there unused. Holding the failed URL rather than a
|
|
52
|
+
// flag means a new src is tried again on its own.
|
|
53
|
+
let failed = $state('');
|
|
54
|
+
const broken = $derived(failed !== '' && failed === src);
|
|
55
|
+
|
|
50
56
|
const initials = $derived(
|
|
51
57
|
name
|
|
52
58
|
.trim()
|
|
@@ -60,13 +66,18 @@
|
|
|
60
66
|
|
|
61
67
|
<span
|
|
62
68
|
class="inline-flex shrink-0 items-center justify-center overflow-hidden font-sans font-semibold select-none
|
|
63
|
-
{squared ? 'rounded-2xl' : 'rounded-full'} {sizes[size]} {src
|
|
69
|
+
{squared ? 'rounded-2xl' : 'rounded-full'} {sizes[size]} {src && !broken
|
|
64
70
|
? 'bg-bg-inset'
|
|
65
71
|
: toneSoft[tone]} {className}"
|
|
66
72
|
{...rest}
|
|
67
73
|
>
|
|
68
|
-
{#if src}
|
|
69
|
-
<img
|
|
74
|
+
{#if src && !broken}
|
|
75
|
+
<img
|
|
76
|
+
{src}
|
|
77
|
+
alt={alt ?? name}
|
|
78
|
+
onerror={() => (failed = src ?? '')}
|
|
79
|
+
class="size-full object-cover"
|
|
80
|
+
/>
|
|
70
81
|
{:else if fallback}
|
|
71
82
|
<span class={glyphs[size]}>{@render fallback()}</span>
|
|
72
83
|
{:else}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
4
|
import { toneHoverText, type Tone } from '../../core/tones.js';
|
|
5
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
6
5
|
import { iconMd } from './icon.js';
|
|
7
6
|
|
|
8
7
|
export interface BreadcrumbItem {
|
|
@@ -19,11 +18,9 @@
|
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
let { items, separator = '/', tone = 'brand', class: className = '', ...rest }: Props = $props();
|
|
22
|
-
|
|
23
|
-
const t = useLocale();
|
|
24
21
|
</script>
|
|
25
22
|
|
|
26
|
-
<nav aria-label=
|
|
23
|
+
<nav aria-label="Breadcrumb" class="font-sans text-sm {className}" {...rest}>
|
|
27
24
|
<ol class="flex flex-wrap items-center gap-1.5">
|
|
28
25
|
{#each items as item, i (item.label)}
|
|
29
26
|
<li class="flex items-center gap-1.5">
|
|
@@ -33,7 +30,7 @@
|
|
|
33
30
|
{#if item.href && i < items.length - 1}
|
|
34
31
|
<a
|
|
35
32
|
href={item.href}
|
|
36
|
-
class="inline-flex items-center gap-1.5 rounded px-1 py-0.5 text-text-muted transition-colors duration-150 ease-brand-out hover:bg-bg-inset {toneHoverText[
|
|
33
|
+
class="inline-flex items-center gap-1.5 rounded-md px-1 py-0.5 text-text-muted transition-colors duration-150 ease-brand-out hover:bg-bg-inset {toneHoverText[
|
|
37
34
|
tone
|
|
38
35
|
]}"
|
|
39
36
|
>
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
toneText,
|
|
15
15
|
type Tone
|
|
16
16
|
} from '../../core/tones.js';
|
|
17
|
-
import {
|
|
17
|
+
import { lift, liftSoft } from './lift.js';
|
|
18
18
|
|
|
19
19
|
export type ButtonVariant = 'solid' | 'soft' | 'outline' | 'ghost' | 'link' | 'primary';
|
|
20
20
|
export type ButtonSize = 'sm' | 'md' | 'lg' | 'xl';
|
|
@@ -92,11 +92,11 @@
|
|
|
92
92
|
case 'solid':
|
|
93
93
|
return `border-transparent ${toneSolid[tone]} ${toneSolidHover[tone]} ${lift[tone]}`;
|
|
94
94
|
case 'soft':
|
|
95
|
-
return `border-transparent ${toneSoft[tone]} ${toneSoftHover[tone]} ${
|
|
95
|
+
return `border-transparent ${toneSoft[tone]} ${toneSoftHover[tone]} ${liftSoft[tone]}`;
|
|
96
96
|
// Outline keeps its own colours on hover — swapping to a solid fill is
|
|
97
97
|
// matte's move, and it fights sprout's soft surfaces.
|
|
98
98
|
case 'outline':
|
|
99
|
-
return `bg-bg ${
|
|
99
|
+
return `bg-bg ${liftSoft[tone]} ${toneText[tone]} ${toneBorderSoft[tone]} hover:bg-bg-alt`;
|
|
100
100
|
// Nothing raised, so nothing to sink: ghost stays flat on the page.
|
|
101
101
|
case 'ghost':
|
|
102
102
|
return `border-transparent bg-transparent ${toneText[tone]} ${toneSoftHover[tone]} active:translate-y-0`;
|
|
@@ -116,6 +116,10 @@
|
|
|
116
116
|
const iconClass = $derived(size === 'sm' ? iconSm : iconMd);
|
|
117
117
|
|
|
118
118
|
async function handleClick(event: MouseEvent) {
|
|
119
|
+
if (inert) {
|
|
120
|
+
event.preventDefault();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
119
123
|
const result = onclick?.(event);
|
|
120
124
|
// Anything else is a plain handler: nothing to wait for, nothing to lock.
|
|
121
125
|
if (!(result instanceof Promise)) return;
|
|
@@ -134,7 +138,7 @@
|
|
|
134
138
|
disabled={href ? undefined : inert}
|
|
135
139
|
aria-disabled={href && inert ? 'true' : undefined}
|
|
136
140
|
aria-busy={busy ? 'true' : undefined}
|
|
137
|
-
|
|
141
|
+
tabindex={href && inert ? -1 : undefined}
|
|
138
142
|
class="{base} {focusRing} {toneRing[tone]} {variantClass} {isLink
|
|
139
143
|
? ''
|
|
140
144
|
: iconOnly
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
weekdayNames
|
|
12
12
|
} from '../../core/date.js';
|
|
13
13
|
import { calendarKeyMove } from '../../core/calendar.js';
|
|
14
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
15
14
|
import {
|
|
16
15
|
focusRing,
|
|
17
16
|
toneFill,
|
|
@@ -53,8 +52,6 @@
|
|
|
53
52
|
class: className = ''
|
|
54
53
|
}: Props = $props();
|
|
55
54
|
|
|
56
|
-
const t = useLocale();
|
|
57
|
-
|
|
58
55
|
const selected = $derived(fromISO(value));
|
|
59
56
|
const now = today();
|
|
60
57
|
|
|
@@ -71,7 +68,7 @@
|
|
|
71
68
|
});
|
|
72
69
|
|
|
73
70
|
const days = $derived(monthGrid(view, weekStart));
|
|
74
|
-
const weekdays = $derived(weekdayNames(
|
|
71
|
+
const weekdays = $derived(weekdayNames(undefined, weekStart));
|
|
75
72
|
|
|
76
73
|
function blocked(date: Date) {
|
|
77
74
|
return isOutOfRange(date, min, max) || (isDisabled?.(date) ?? false);
|
|
@@ -119,7 +116,7 @@
|
|
|
119
116
|
<button
|
|
120
117
|
type="button"
|
|
121
118
|
onclick={() => show(addMonths(view, -1))}
|
|
122
|
-
aria-label=
|
|
119
|
+
aria-label="Previous month"
|
|
123
120
|
class="{arrow} {focusRing} {toneRing[tone]}"
|
|
124
121
|
>
|
|
125
122
|
<svg class="size-4" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
|
@@ -128,13 +125,13 @@
|
|
|
128
125
|
</button>
|
|
129
126
|
|
|
130
127
|
<span aria-live="polite" class="text-sm font-semibold text-text">
|
|
131
|
-
{monthLabel(view
|
|
128
|
+
{monthLabel(view)}
|
|
132
129
|
</span>
|
|
133
130
|
|
|
134
131
|
<button
|
|
135
132
|
type="button"
|
|
136
133
|
onclick={() => show(addMonths(view, 1))}
|
|
137
|
-
aria-label=
|
|
134
|
+
aria-label="Next month"
|
|
138
135
|
class="{arrow} {focusRing} {toneRing[tone]}"
|
|
139
136
|
>
|
|
140
137
|
<svg class="size-4" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
|
@@ -154,7 +151,7 @@
|
|
|
154
151
|
bind:this={grid}
|
|
155
152
|
role="grid"
|
|
156
153
|
tabindex="-1"
|
|
157
|
-
aria-label=
|
|
154
|
+
aria-label="Select a date"
|
|
158
155
|
onkeydown={onKeydown}
|
|
159
156
|
class="grid grid-cols-7 gap-0.5 focus:outline-none"
|
|
160
157
|
>
|
|
@@ -192,11 +189,11 @@
|
|
|
192
189
|
type="button"
|
|
193
190
|
onclick={() => pick(now)}
|
|
194
191
|
disabled={blocked(now)}
|
|
195
|
-
class="self-start rounded px-1 text-[13px] font-medium transition-colors duration-150 disabled:pointer-events-none disabled:opacity-40 {toneText[
|
|
192
|
+
class="self-start rounded-md px-1 text-[13px] font-medium transition-colors duration-150 disabled:pointer-events-none disabled:opacity-40 {toneText[
|
|
196
193
|
tone
|
|
197
194
|
]} hover:underline hover:underline-offset-2 {focusRing} {toneRing[tone]}"
|
|
198
195
|
>
|
|
199
|
-
|
|
196
|
+
Today
|
|
200
197
|
</button>
|
|
201
198
|
{/if}
|
|
202
199
|
</div>
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
...rest
|
|
41
41
|
}: Props = $props();
|
|
42
42
|
|
|
43
|
-
// The default card is the one the style is named for:
|
|
43
|
+
// The default card is the one the style is named for: white, a hairline, and
|
|
44
44
|
// the lip. `filled` and `tinted` stay flat on purpose — a tinted panel that
|
|
45
45
|
// also floats is two claims about the same box.
|
|
46
46
|
const variants = $derived({
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
|
|
53
53
|
<div
|
|
54
54
|
class="flex flex-col rounded-[28px] {variants[variant]}
|
|
55
|
-
{accent ? `border-l-
|
|
55
|
+
{accent ? `border-l-2 ${toneBorderLeft[tone]}` : ''}
|
|
56
56
|
{interactive
|
|
57
57
|
? `transition-[box-shadow,transform,border-color] duration-150 ease-brand-out hover:-translate-y-0.5 ${softOnHover} ${toneHoverBorder[tone]}`
|
|
58
58
|
: ''}
|
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
...rest
|
|
27
27
|
}: Props = $props();
|
|
28
28
|
|
|
29
|
+
const uid = $props.id();
|
|
30
|
+
|
|
29
31
|
let el: HTMLInputElement | null = $state(null);
|
|
30
32
|
|
|
31
33
|
$effect(() => {
|
|
@@ -33,44 +35,61 @@
|
|
|
33
35
|
});
|
|
34
36
|
</script>
|
|
35
37
|
|
|
36
|
-
<
|
|
37
|
-
class="
|
|
38
|
-
|
|
38
|
+
<div
|
|
39
|
+
class="inline-block font-sans text-sm {disabled
|
|
40
|
+
? 'pointer-events-none opacity-50'
|
|
41
|
+
: ''} {className}"
|
|
39
42
|
>
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
<label class="group inline-flex items-start gap-2.5 {disabled ? '' : 'cursor-pointer'}">
|
|
44
|
+
<input
|
|
45
|
+
bind:this={el}
|
|
46
|
+
type="checkbox"
|
|
47
|
+
bind:checked
|
|
48
|
+
{disabled}
|
|
49
|
+
aria-describedby={description ? `${uid}-description` : undefined}
|
|
50
|
+
class="peer sr-only"
|
|
51
|
+
{...rest}
|
|
52
|
+
/>
|
|
53
|
+
<span
|
|
54
|
+
class="mt-0.5 flex size-[18px] shrink-0 items-center justify-center rounded-md border border-hairline-strong bg-bg {edge} transition-colors duration-150 ease-brand-out
|
|
43
55
|
{tonePeerChecked[tone]} {peerFocusRing} {tonePeerFocus[tone]}"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
{/if}
|
|
63
|
-
</span>
|
|
64
|
-
{#if label || description || children}
|
|
65
|
-
<span class="flex flex-col gap-0.5">
|
|
66
|
-
{#if children}
|
|
67
|
-
{@render children()}
|
|
68
|
-
{:else if label}
|
|
69
|
-
<span class="leading-snug text-text">{label}</span>
|
|
70
|
-
{/if}
|
|
71
|
-
{#if description}
|
|
72
|
-
<span class="text-[13px] leading-snug text-text-muted">{description}</span>
|
|
56
|
+
aria-hidden="true"
|
|
57
|
+
>
|
|
58
|
+
{#if indeterminate}
|
|
59
|
+
<span class="h-0.5 w-2.5 rounded-full bg-text-muted"></span>
|
|
60
|
+
{:else}
|
|
61
|
+
<svg
|
|
62
|
+
class="size-3 text-text-inverse opacity-0 transition-opacity duration-150 group-has-[:checked]:opacity-100"
|
|
63
|
+
viewBox="0 0 12 12"
|
|
64
|
+
fill="none"
|
|
65
|
+
>
|
|
66
|
+
<path
|
|
67
|
+
d="m2 6 2.6 2.6L10 3.2"
|
|
68
|
+
stroke="currentColor"
|
|
69
|
+
stroke-width="2"
|
|
70
|
+
stroke-linecap="round"
|
|
71
|
+
stroke-linejoin="round"
|
|
72
|
+
/>
|
|
73
|
+
</svg>
|
|
73
74
|
{/if}
|
|
74
75
|
</span>
|
|
76
|
+
{#if label || children}
|
|
77
|
+
<span class="flex flex-col gap-0.5">
|
|
78
|
+
{#if children}
|
|
79
|
+
{@render children()}
|
|
80
|
+
{:else if label}
|
|
81
|
+
<span class="leading-snug text-text">{label}</span>
|
|
82
|
+
{/if}
|
|
83
|
+
</span>
|
|
84
|
+
{/if}
|
|
85
|
+
</label>
|
|
86
|
+
{#if description}
|
|
87
|
+
<!-- ponytail: the indent tracks the 18px box plus the label's gap; move one, move both. -->
|
|
88
|
+
<span
|
|
89
|
+
id="{uid}-description"
|
|
90
|
+
class="mt-0.5 block pl-[28px] text-[13px] leading-snug text-text-muted"
|
|
91
|
+
>
|
|
92
|
+
{description}
|
|
93
|
+
</span>
|
|
75
94
|
{/if}
|
|
76
|
-
</
|
|
95
|
+
</div>
|
|
@@ -51,6 +51,11 @@
|
|
|
51
51
|
...rest
|
|
52
52
|
}: Props = $props();
|
|
53
53
|
|
|
54
|
+
const uid = $props.id();
|
|
55
|
+
// The group's own hint and error sit outside every control in it, so the
|
|
56
|
+
// fieldset is what has to point at them.
|
|
57
|
+
const messageId = $derived(error ? `${uid}-error` : hint ? `${uid}-hint` : undefined);
|
|
58
|
+
|
|
54
59
|
const full = $derived(max > 0 && value.length >= max);
|
|
55
60
|
|
|
56
61
|
function toggle(option: CheckboxOption) {
|
|
@@ -61,11 +66,17 @@
|
|
|
61
66
|
}
|
|
62
67
|
</script>
|
|
63
68
|
|
|
64
|
-
<fieldset
|
|
69
|
+
<fieldset
|
|
70
|
+
aria-describedby={messageId}
|
|
71
|
+
class="flex flex-col gap-3 border-0 p-0 {className}"
|
|
72
|
+
{...rest}
|
|
73
|
+
>
|
|
65
74
|
{#if legend}
|
|
66
75
|
<legend class="mb-1 font-sans text-sm font-medium text-text">
|
|
67
76
|
{legend}
|
|
68
|
-
{#if required}<span class="ml-0.5 text-danger" aria-hidden="true">*</span
|
|
77
|
+
{#if required}<span class="ml-0.5 text-danger" aria-hidden="true">*</span><span
|
|
78
|
+
class="sr-only">(required)</span
|
|
79
|
+
>{/if}
|
|
69
80
|
</legend>
|
|
70
81
|
{/if}
|
|
71
82
|
|
|
@@ -111,8 +122,8 @@
|
|
|
111
122
|
</div>
|
|
112
123
|
|
|
113
124
|
{#if error}
|
|
114
|
-
<p class="font-sans text-[13px] text-danger">{error}</p>
|
|
125
|
+
<p id="{uid}-error" role="alert" class="font-sans text-[13px] text-danger">{error}</p>
|
|
115
126
|
{:else if hint}
|
|
116
|
-
<p class="font-sans text-[13px] text-text-muted">{hint}</p>
|
|
127
|
+
<p id="{uid}-hint" class="font-sans text-[13px] text-text-muted">{hint}</p>
|
|
117
128
|
{/if}
|
|
118
129
|
</fieldset>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
3
3
|
import { normaliseHex } from '../../core/color.js';
|
|
4
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
5
4
|
import type { Tone } from '../../core/tones.js';
|
|
6
5
|
import Input, { type InputSize } from './Input.svelte';
|
|
7
6
|
|
|
@@ -27,8 +26,6 @@
|
|
|
27
26
|
...rest
|
|
28
27
|
}: Props = $props();
|
|
29
28
|
|
|
30
|
-
const t = useLocale();
|
|
31
|
-
|
|
32
29
|
// The text can be mid-typing (`#a2`) while the swatch needs a real colour,
|
|
33
30
|
// so the swatch follows the last value that parsed rather than the field.
|
|
34
31
|
let text = $state('');
|
|
@@ -77,14 +74,14 @@
|
|
|
77
74
|
>
|
|
78
75
|
{#snippet prefix()}
|
|
79
76
|
<span
|
|
80
|
-
class="relative grid size-5 place-items-center overflow-hidden rounded border border-hairline-strong"
|
|
77
|
+
class="relative grid size-5 place-items-center overflow-hidden rounded-lg border border-hairline-strong"
|
|
81
78
|
>
|
|
82
79
|
<span class="absolute inset-0" style="background-color: {resolved}"></span>
|
|
83
80
|
<input
|
|
84
81
|
type="color"
|
|
85
82
|
value={resolved}
|
|
86
83
|
{disabled}
|
|
87
|
-
aria-label=
|
|
84
|
+
aria-label="Pick a colour"
|
|
88
85
|
oninput={(event) => choose(event.currentTarget.value)}
|
|
89
86
|
class="absolute inset-0 cursor-pointer opacity-0 disabled:cursor-not-allowed"
|
|
90
87
|
/>
|
|
@@ -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
|
import { edge, soft } from './lift.js';
|
|
10
16
|
|
|
@@ -52,8 +58,6 @@
|
|
|
52
58
|
class: className = ''
|
|
53
59
|
}: Props = $props();
|
|
54
60
|
|
|
55
|
-
const t = useLocale();
|
|
56
|
-
|
|
57
61
|
let open = $state(false);
|
|
58
62
|
let query = $state('');
|
|
59
63
|
let wrapper: HTMLElement | null = $state(null);
|
|
@@ -147,7 +151,9 @@
|
|
|
147
151
|
|
|
148
152
|
<div
|
|
149
153
|
bind:this={wrapper}
|
|
150
|
-
class="relative inline-flex w-full items-center gap-2 rounded-xl border bg-bg pr-1.5 pl-3 {edge} transition-colors duration-150 ease-brand-out
|
|
154
|
+
class="relative inline-flex w-full items-center gap-2 rounded-xl border bg-bg pr-1.5 pl-3 {edge} transition-colors duration-150 ease-brand-out {focusWithinRing} {toneFocusWithinRing[
|
|
155
|
+
tone
|
|
156
|
+
]}
|
|
151
157
|
{invalid ? 'border-danger' : `border-hairline-strong ${toneFocusWithinBorder[tone]}`}
|
|
152
158
|
{disabled ? 'pointer-events-none opacity-50' : ''} {className}"
|
|
153
159
|
>
|
|
@@ -162,7 +168,7 @@
|
|
|
162
168
|
aria-controls="{id ?? 'combobox'}-listbox"
|
|
163
169
|
aria-activedescendant={open && flat[active] ? `${id ?? 'combobox'}-opt-${active}` : undefined}
|
|
164
170
|
aria-invalid={invalid ? 'true' : undefined}
|
|
165
|
-
placeholder={selected ? selected.label : (placeholder ??
|
|
171
|
+
placeholder={selected ? selected.label : (placeholder ?? 'Search…')}
|
|
166
172
|
value={open ? query : (selected?.label ?? '')}
|
|
167
173
|
oninput={(event) => {
|
|
168
174
|
query = event.currentTarget.value;
|
|
@@ -180,8 +186,8 @@
|
|
|
180
186
|
<button
|
|
181
187
|
type="button"
|
|
182
188
|
onclick={clear}
|
|
183
|
-
aria-label=
|
|
184
|
-
class="shrink-0 rounded p-1 text-text-muted transition-colors duration-150 hover:bg-bg-inset hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
|
|
189
|
+
aria-label="Clear selection"
|
|
190
|
+
class="shrink-0 rounded-xl p-1 text-text-muted transition-colors duration-150 hover:bg-bg-inset hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
|
|
185
191
|
>
|
|
186
192
|
<svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
187
193
|
<path
|
|
@@ -218,7 +224,7 @@
|
|
|
218
224
|
>
|
|
219
225
|
{#if flat.length === 0}
|
|
220
226
|
<p class="px-3 py-6 text-center text-sm text-text-muted">
|
|
221
|
-
{emptyText ??
|
|
227
|
+
{emptyText ?? 'No matches'}
|
|
222
228
|
</p>
|
|
223
229
|
{:else}
|
|
224
230
|
{#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';
|
|
@@ -62,8 +61,6 @@
|
|
|
62
61
|
class: className = ''
|
|
63
62
|
}: Props = $props();
|
|
64
63
|
|
|
65
|
-
const t = useLocale();
|
|
66
|
-
|
|
67
64
|
let dialog: HTMLDialogElement | null = $state(null);
|
|
68
65
|
let inputEl: HTMLInputElement | null = $state(null);
|
|
69
66
|
let listEl: HTMLElement | null = $state(null);
|
|
@@ -183,8 +180,8 @@
|
|
|
183
180
|
oninput={() => cursor.reset()}
|
|
184
181
|
type="text"
|
|
185
182
|
autocomplete="off"
|
|
186
|
-
aria-label={placeholder ??
|
|
187
|
-
placeholder={placeholder ??
|
|
183
|
+
aria-label={placeholder ?? 'Type a command or search…'}
|
|
184
|
+
placeholder={placeholder ?? 'Type a command or search…'}
|
|
188
185
|
class="h-12 w-full min-w-0 bg-transparent font-sans text-sm text-text placeholder:text-text-muted focus:outline-none"
|
|
189
186
|
/>
|
|
190
187
|
<Kbd>esc</Kbd>
|
|
@@ -198,7 +195,7 @@
|
|
|
198
195
|
>
|
|
199
196
|
{#if flat.length === 0}
|
|
200
197
|
<p class="px-4 py-10 text-center font-sans text-sm text-text-muted">
|
|
201
|
-
{emptyText ??
|
|
198
|
+
{emptyText ?? 'No results'}
|
|
202
199
|
</p>
|
|
203
200
|
{:else}
|
|
204
201
|
{#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,11 @@
|
|
|
76
77
|
{/if}
|
|
77
78
|
</span>
|
|
78
79
|
<div class="flex min-w-0 flex-1 flex-col gap-1.5">
|
|
79
|
-
<h2 class="font-heading text-base font-semibold text-text">{title}</h2>
|
|
80
|
+
<h2 id="{uid}-title" class="font-heading text-base font-semibold text-text">{title}</h2>
|
|
80
81
|
{#if description}
|
|
81
|
-
<p class="text-[13px] leading-relaxed text-text-secondary">
|
|
82
|
+
<p id="{uid}-description" class="text-[13px] leading-relaxed text-text-secondary">
|
|
83
|
+
{description}
|
|
84
|
+
</p>
|
|
82
85
|
{/if}
|
|
83
86
|
{#if children}
|
|
84
87
|
{@render children()}
|
|
@@ -88,10 +91,10 @@
|
|
|
88
91
|
|
|
89
92
|
{#snippet footer()}
|
|
90
93
|
<Button variant="outline" tone="neutral" onclick={cancel} disabled={loading}>
|
|
91
|
-
{cancelLabel ??
|
|
94
|
+
{cancelLabel ?? 'Cancel'}
|
|
92
95
|
</Button>
|
|
93
96
|
<Button {tone} onclick={confirm} {loading}>
|
|
94
|
-
{confirmLabel ??
|
|
97
|
+
{confirmLabel ?? 'Confirm'}
|
|
95
98
|
</Button>
|
|
96
99
|
{/snippet}
|
|
97
100
|
</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
|
import { soft } from './lift.js';
|
|
8
7
|
|
|
9
8
|
interface Props {
|
|
@@ -28,8 +27,6 @@
|
|
|
28
27
|
menuClass = ''
|
|
29
28
|
}: Props = $props();
|
|
30
29
|
|
|
31
|
-
const t = useLocale();
|
|
32
|
-
|
|
33
30
|
let menuEl: HTMLElement | null = $state(null);
|
|
34
31
|
/**
|
|
35
32
|
* A 0×0 element parked at the pointer, so the same `anchored` action that
|
|
@@ -80,7 +77,7 @@
|
|
|
80
77
|
if ((event.target as HTMLElement).closest('[role="menuitem"]:not([disabled])')) open = false;
|
|
81
78
|
}}
|
|
82
79
|
role="menu"
|
|
83
|
-
aria-label={label ??
|
|
80
|
+
aria-label={label ?? 'Menu'}
|
|
84
81
|
tabindex="-1"
|
|
85
82
|
class="z-50 flex max-h-[min(24rem,80vh)] min-w-48 flex-col overflow-y-auto rounded-2xl border border-hairline bg-bg py-1 font-sans {soft} {menuClass}"
|
|
86
83
|
>
|
|
@@ -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;
|