@nqmcreative/ui 0.3.0 → 0.4.1
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 +220 -28
- package/cli/index.mjs +247 -70
- package/dist/core/calendar.d.ts +1 -1
- package/dist/core/calendar.js +1 -1
- package/dist/core/color.d.ts +24 -0
- package/dist/core/color.js +39 -0
- 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 +9 -4
- package/dist/core/index.js +6 -4
- package/dist/core/number.d.ts +22 -0
- package/dist/core/number.js +33 -0
- package/dist/core/password.d.ts +1 -1
- package/dist/core/password.js +1 -1
- package/dist/core/pin.d.ts +23 -0
- package/dist/core/pin.js +38 -0
- package/dist/core/tags.d.ts +37 -0
- package/dist/core/tags.js +51 -0
- package/dist/core/time.d.ts +30 -0
- package/dist/core/time.js +78 -0
- package/dist/styles/matte/Alert.svelte +1 -3
- package/dist/styles/matte/Breadcrumb.svelte +1 -4
- package/dist/styles/matte/Calendar.svelte +6 -9
- package/dist/styles/matte/ColorInput.svelte +107 -0
- package/dist/styles/matte/ColorInput.svelte.d.ts +16 -0
- package/dist/styles/matte/Combobox.svelte +3 -6
- package/dist/styles/matte/CommandPalette.svelte +3 -6
- package/dist/styles/matte/ConfirmDialog.svelte +2 -5
- package/dist/styles/matte/ContextMenu.svelte +1 -4
- package/dist/styles/matte/CurrencyInput.svelte +108 -0
- package/dist/styles/matte/CurrencyInput.svelte.d.ts +26 -0
- package/dist/styles/matte/DatePicker.svelte +9 -9
- package/dist/styles/matte/DatePicker.svelte.d.ts +3 -0
- package/dist/styles/matte/Drawer.svelte +1 -3
- 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/FileInput.svelte +191 -0
- package/dist/styles/matte/FileInput.svelte.d.ts +32 -0
- package/dist/styles/matte/Footer.svelte +1 -1
- package/dist/styles/matte/Footer.svelte.d.ts +1 -1
- package/dist/styles/matte/InputAddon.svelte +36 -0
- package/dist/styles/matte/InputAddon.svelte.d.ts +13 -0
- package/dist/styles/matte/Modal.svelte +1 -3
- package/dist/styles/matte/MultiSelect.svelte +5 -8
- package/dist/styles/matte/Navbar.svelte +3 -9
- package/dist/styles/matte/Pagination.svelte +3 -6
- package/dist/styles/matte/PinInput.svelte +134 -0
- package/dist/styles/matte/PinInput.svelte.d.ts +25 -0
- package/dist/styles/matte/SearchInput.svelte +84 -0
- package/dist/styles/matte/SearchInput.svelte.d.ts +24 -0
- package/dist/styles/matte/Sidebar.svelte +3 -6
- package/dist/styles/matte/Spinner.svelte +2 -5
- package/dist/styles/matte/Spinner.svelte.d.ts +1 -1
- package/dist/styles/matte/Table.svelte +5 -8
- package/dist/styles/matte/TagsInput.svelte +170 -0
- package/dist/styles/matte/TagsInput.svelte.d.ts +29 -0
- package/dist/styles/matte/ThemeToggle.svelte +5 -8
- package/dist/styles/matte/TimeInput.svelte +126 -0
- package/dist/styles/matte/TimeInput.svelte.d.ts +21 -0
- package/dist/styles/matte/Toaster.svelte +2 -5
- package/dist/styles/matte/index.d.ts +10 -3
- package/dist/styles/matte/index.js +9 -3
- package/dist/styles/paper/Alert.svelte +1 -3
- package/dist/styles/paper/Breadcrumb.svelte +1 -4
- package/dist/styles/paper/Calendar.svelte +6 -9
- package/dist/styles/paper/ColorInput.svelte +109 -0
- package/dist/styles/paper/ColorInput.svelte.d.ts +16 -0
- package/dist/styles/paper/Combobox.svelte +3 -6
- package/dist/styles/paper/CommandPalette.svelte +3 -6
- package/dist/styles/paper/ConfirmDialog.svelte +2 -5
- package/dist/styles/paper/ContextMenu.svelte +1 -4
- package/dist/styles/paper/CurrencyInput.svelte +108 -0
- package/dist/styles/paper/CurrencyInput.svelte.d.ts +26 -0
- package/dist/styles/paper/DatePicker.svelte +9 -9
- package/dist/styles/paper/DatePicker.svelte.d.ts +3 -0
- package/dist/styles/paper/Drawer.svelte +1 -3
- 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/FileInput.svelte +201 -0
- package/dist/styles/paper/FileInput.svelte.d.ts +32 -0
- package/dist/styles/paper/Footer.svelte +1 -1
- package/dist/styles/paper/Footer.svelte.d.ts +1 -1
- package/dist/styles/paper/InputAddon.svelte +36 -0
- package/dist/styles/paper/InputAddon.svelte.d.ts +13 -0
- package/dist/styles/paper/Modal.svelte +1 -3
- package/dist/styles/paper/MultiSelect.svelte +5 -8
- package/dist/styles/paper/Navbar.svelte +3 -9
- package/dist/styles/paper/Pagination.svelte +3 -6
- package/dist/styles/paper/PinInput.svelte +134 -0
- package/dist/styles/paper/PinInput.svelte.d.ts +25 -0
- package/dist/styles/paper/SearchInput.svelte +89 -0
- package/dist/styles/paper/SearchInput.svelte.d.ts +24 -0
- package/dist/styles/paper/Sidebar.svelte +3 -6
- package/dist/styles/paper/Spinner.svelte +2 -5
- package/dist/styles/paper/Spinner.svelte.d.ts +1 -1
- package/dist/styles/paper/Table.svelte +5 -8
- package/dist/styles/paper/TagsInput.svelte +175 -0
- package/dist/styles/paper/TagsInput.svelte.d.ts +29 -0
- package/dist/styles/paper/ThemeToggle.svelte +5 -8
- package/dist/styles/paper/TimeInput.svelte +152 -0
- package/dist/styles/paper/TimeInput.svelte.d.ts +21 -0
- package/dist/styles/paper/Toaster.svelte +3 -6
- package/dist/styles/paper/index.d.ts +10 -3
- package/dist/styles/paper/index.js +9 -3
- package/dist/styles/sprout/Alert.svelte +1 -3
- package/dist/styles/sprout/Breadcrumb.svelte +1 -4
- package/dist/styles/sprout/Calendar.svelte +6 -9
- package/dist/styles/sprout/ColorInput.svelte +109 -0
- package/dist/styles/sprout/ColorInput.svelte.d.ts +16 -0
- package/dist/styles/sprout/Combobox.svelte +3 -6
- package/dist/styles/sprout/CommandPalette.svelte +3 -6
- package/dist/styles/sprout/ConfirmDialog.svelte +2 -5
- package/dist/styles/sprout/ContextMenu.svelte +1 -4
- package/dist/styles/sprout/CurrencyInput.svelte +108 -0
- package/dist/styles/sprout/CurrencyInput.svelte.d.ts +26 -0
- package/dist/styles/sprout/DatePicker.svelte +9 -9
- package/dist/styles/sprout/DatePicker.svelte.d.ts +3 -0
- package/dist/styles/sprout/Drawer.svelte +1 -3
- package/dist/styles/sprout/Dropdown.svelte +2 -5
- package/dist/styles/sprout/Dropdown.svelte.d.ts +1 -1
- package/dist/styles/sprout/Dropzone.svelte +9 -12
- package/dist/styles/sprout/Dropzone.svelte.d.ts +1 -1
- package/dist/styles/sprout/FileInput.svelte +202 -0
- package/dist/styles/sprout/FileInput.svelte.d.ts +32 -0
- package/dist/styles/sprout/Footer.svelte +1 -1
- package/dist/styles/sprout/Footer.svelte.d.ts +1 -1
- package/dist/styles/sprout/InputAddon.svelte +37 -0
- package/dist/styles/sprout/InputAddon.svelte.d.ts +13 -0
- package/dist/styles/sprout/Modal.svelte +1 -3
- package/dist/styles/sprout/MultiSelect.svelte +5 -8
- package/dist/styles/sprout/Navbar.svelte +3 -9
- package/dist/styles/sprout/Pagination.svelte +3 -6
- package/dist/styles/sprout/PinInput.svelte +135 -0
- package/dist/styles/sprout/PinInput.svelte.d.ts +25 -0
- package/dist/styles/sprout/SearchInput.svelte +89 -0
- package/dist/styles/sprout/SearchInput.svelte.d.ts +24 -0
- package/dist/styles/sprout/Sidebar.svelte +3 -6
- package/dist/styles/sprout/Spinner.svelte +2 -5
- package/dist/styles/sprout/Spinner.svelte.d.ts +1 -1
- package/dist/styles/sprout/Table.svelte +5 -8
- package/dist/styles/sprout/TagsInput.svelte +176 -0
- package/dist/styles/sprout/TagsInput.svelte.d.ts +29 -0
- package/dist/styles/sprout/ThemeToggle.svelte +5 -8
- package/dist/styles/sprout/TimeInput.svelte +152 -0
- package/dist/styles/sprout/TimeInput.svelte.d.ts +21 -0
- package/dist/styles/sprout/Toaster.svelte +3 -6
- package/dist/styles/sprout/index.d.ts +10 -3
- package/dist/styles/sprout/index.js +9 -3
- package/package.json +98 -19
- package/registry.json +139 -38
- package/dist/core/locale.svelte.d.ts +0 -93
- package/dist/core/locale.svelte.js +0 -153
- 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
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
import { toneText } from '../../core/tones.js';
|
|
7
7
|
import { iconMd } from './icon.js';
|
|
8
8
|
import Spinner from './Spinner.svelte';
|
|
9
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
10
9
|
|
|
11
10
|
export type ToasterPosition =
|
|
12
11
|
'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
|
|
@@ -18,8 +17,6 @@
|
|
|
18
17
|
|
|
19
18
|
let { position = 'bottom-right', class: className = '' }: Props = $props();
|
|
20
19
|
|
|
21
|
-
const t = useLocale();
|
|
22
|
-
|
|
23
20
|
const positions: Record<ToasterPosition, string> = {
|
|
24
21
|
'top-left': 'top-4 left-4 items-start',
|
|
25
22
|
'top-center': 'top-4 left-1/2 -translate-x-1/2 items-center',
|
|
@@ -41,8 +38,8 @@
|
|
|
41
38
|
<div
|
|
42
39
|
use:portal
|
|
43
40
|
aria-live="polite"
|
|
44
|
-
aria-label=
|
|
45
|
-
class="pointer-events-none fixed z-
|
|
41
|
+
aria-label="Notifications"
|
|
42
|
+
class="pointer-events-none fixed z-100 flex w-[min(24rem,calc(100vw-2rem))] flex-col gap-2.5 {positions[
|
|
46
43
|
position
|
|
47
44
|
]} {fromTop ? '' : 'flex-col-reverse'} {className}"
|
|
48
45
|
>
|
|
@@ -118,7 +115,7 @@
|
|
|
118
115
|
<button
|
|
119
116
|
type="button"
|
|
120
117
|
onclick={() => toast.dismiss(item.id)}
|
|
121
|
-
aria-label=
|
|
118
|
+
aria-label="Dismiss"
|
|
122
119
|
class="-m-1 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"
|
|
123
120
|
>
|
|
124
121
|
<svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
* `@nqmcreative/ui/core`, re-exported below so this one import covers
|
|
6
6
|
* the whole style.
|
|
7
7
|
*/
|
|
8
|
-
export { TONES, focusRing, peerFocusRing, toneBorder, toneBorderLeft, toneBorderSoft, toneFill, toneFocusBorder, toneFocusWithinBorder, toneHoverBorder, toneHoverText, tonePeerChecked, tonePeerFocus, toneRing, toneSoft, toneSoftHover, toneSolid, toneSolidHover, toneSurface, toneText, anchored, clickOutside, focusTrap, focusable, navigateList, portal, toast,
|
|
9
|
-
export type { Tone, Placement, AnchorOptions, ClickOutsideOptions, FocusTrapOptions, Toast, ToastOptions, ToastAction,
|
|
10
|
-
export { default as LocaleProvider } from './LocaleProvider.svelte';
|
|
8
|
+
export { TONES, focusRing, peerFocusRing, toneBorder, toneBorderLeft, toneBorderSoft, toneFill, toneFocusBorder, toneFocusWithinBorder, toneHoverBorder, toneHoverText, tonePeerChecked, tonePeerFocus, toneRing, toneSoft, toneSoftHover, toneSolid, toneSolidHover, toneSurface, toneText, anchored, clickOutside, focusTrap, focusable, navigateList, portal, toast, applyTheme, storedTheme, toISO, fromISO, today, isSameDay, addDays, addMonths, monthGrid, weekdayNames, monthLabel, isOutOfRange, formatISO, parseFormatted } from '../../core/index.js';
|
|
9
|
+
export type { Tone, Placement, AnchorOptions, ClickOutsideOptions, FocusTrapOptions, Toast, ToastOptions, ToastAction, Theme, DateFormat, TableSort, SortDirection, RejectedFile } from '../../core/index.js';
|
|
11
10
|
export { default as Button } from './Button.svelte';
|
|
12
11
|
export type { ButtonVariant, ButtonSize } from './Button.svelte';
|
|
13
12
|
export { default as Link } from './Link.svelte';
|
|
@@ -30,6 +29,14 @@ export type { CheckboxOption } from './CheckboxGroup.svelte';
|
|
|
30
29
|
export { default as SegmentedControl } from './SegmentedControl.svelte';
|
|
31
30
|
export type { SegmentOption, SegmentedSize } from './SegmentedControl.svelte';
|
|
32
31
|
export { default as InputGroup } from './InputGroup.svelte';
|
|
32
|
+
export { default as InputAddon } from './InputAddon.svelte';
|
|
33
|
+
export { default as SearchInput } from './SearchInput.svelte';
|
|
34
|
+
export { default as PinInput } from './PinInput.svelte';
|
|
35
|
+
export { default as TagsInput } from './TagsInput.svelte';
|
|
36
|
+
export { default as CurrencyInput } from './CurrencyInput.svelte';
|
|
37
|
+
export { default as TimeInput } from './TimeInput.svelte';
|
|
38
|
+
export { default as ColorInput } from './ColorInput.svelte';
|
|
39
|
+
export { default as FileInput } from './FileInput.svelte';
|
|
33
40
|
export { default as Combobox } from './Combobox.svelte';
|
|
34
41
|
export type { ComboboxOption } from './Combobox.svelte';
|
|
35
42
|
export { default as MultiSelect } from './MultiSelect.svelte';
|
|
@@ -6,9 +6,7 @@
|
|
|
6
6
|
* the whole style.
|
|
7
7
|
*/
|
|
8
8
|
/* --- shared behaviour --- */
|
|
9
|
-
export { TONES, focusRing, peerFocusRing, toneBorder, toneBorderLeft, toneBorderSoft, toneFill, toneFocusBorder, toneFocusWithinBorder, toneHoverBorder, toneHoverText, tonePeerChecked, tonePeerFocus, toneRing, toneSoft, toneSoftHover, toneSolid, toneSolidHover, toneSurface, toneText, anchored, clickOutside, focusTrap, focusable, navigateList, portal, toast,
|
|
10
|
-
/* --- locale --- */
|
|
11
|
-
export { default as LocaleProvider } from './LocaleProvider.svelte';
|
|
9
|
+
export { TONES, focusRing, peerFocusRing, toneBorder, toneBorderLeft, toneBorderSoft, toneFill, toneFocusBorder, toneFocusWithinBorder, toneHoverBorder, toneHoverText, tonePeerChecked, tonePeerFocus, toneRing, toneSoft, toneSoftHover, toneSolid, toneSolidHover, toneSurface, toneText, anchored, clickOutside, focusTrap, focusable, navigateList, portal, toast, applyTheme, storedTheme, toISO, fromISO, today, isSameDay, addDays, addMonths, monthGrid, weekdayNames, monthLabel, isOutOfRange, formatISO, parseFormatted } from '../../core/index.js';
|
|
12
10
|
/* --- actions --- */
|
|
13
11
|
export { default as Button } from './Button.svelte';
|
|
14
12
|
export { default as Link } from './Link.svelte';
|
|
@@ -26,6 +24,14 @@ export { default as RadioGroup } from './RadioGroup.svelte';
|
|
|
26
24
|
export { default as CheckboxGroup } from './CheckboxGroup.svelte';
|
|
27
25
|
export { default as SegmentedControl } from './SegmentedControl.svelte';
|
|
28
26
|
export { default as InputGroup } from './InputGroup.svelte';
|
|
27
|
+
export { default as InputAddon } from './InputAddon.svelte';
|
|
28
|
+
export { default as SearchInput } from './SearchInput.svelte';
|
|
29
|
+
export { default as PinInput } from './PinInput.svelte';
|
|
30
|
+
export { default as TagsInput } from './TagsInput.svelte';
|
|
31
|
+
export { default as CurrencyInput } from './CurrencyInput.svelte';
|
|
32
|
+
export { default as TimeInput } from './TimeInput.svelte';
|
|
33
|
+
export { default as ColorInput } from './ColorInput.svelte';
|
|
34
|
+
export { default as FileInput } from './FileInput.svelte';
|
|
29
35
|
export { default as Combobox } from './Combobox.svelte';
|
|
30
36
|
export { default as MultiSelect } from './MultiSelect.svelte';
|
|
31
37
|
export { default as DatePicker } from './DatePicker.svelte';
|
|
@@ -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,7 +36,6 @@
|
|
|
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({
|
|
@@ -83,7 +81,7 @@
|
|
|
83
81
|
<button
|
|
84
82
|
type="button"
|
|
85
83
|
onclick={dismiss}
|
|
86
|
-
aria-label=
|
|
84
|
+
aria-label="Dismiss"
|
|
87
85
|
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"
|
|
88
86
|
>
|
|
89
87
|
<svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
@@ -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">
|
|
@@ -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
|
>
|
|
@@ -196,7 +193,7 @@
|
|
|
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>
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
3
|
+
import { normaliseHex } from '../../core/color.js';
|
|
4
|
+
import type { Tone } from '../../core/tones.js';
|
|
5
|
+
import Input, { type InputSize } from './Input.svelte';
|
|
6
|
+
|
|
7
|
+
interface Props extends Omit<HTMLInputAttributes, 'size' | 'prefix' | 'suffix' | 'type'> {
|
|
8
|
+
/** Bindable `#rrggbb`. Shorthand and alpha are normalised away on blur. */
|
|
9
|
+
value?: string;
|
|
10
|
+
size?: InputSize;
|
|
11
|
+
tone?: Tone;
|
|
12
|
+
invalid?: boolean;
|
|
13
|
+
/** Fixed choices under the field. Empty hides the row. */
|
|
14
|
+
swatches?: string[];
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
value = $bindable('#000000'),
|
|
20
|
+
size = 'md',
|
|
21
|
+
tone = 'brand',
|
|
22
|
+
invalid = false,
|
|
23
|
+
disabled = false,
|
|
24
|
+
swatches = [],
|
|
25
|
+
class: className = '',
|
|
26
|
+
...rest
|
|
27
|
+
}: Props = $props();
|
|
28
|
+
|
|
29
|
+
// The text can be mid-typing (`#a2`) while the swatch needs a real colour,
|
|
30
|
+
// so the swatch follows the last value that parsed rather than the field.
|
|
31
|
+
let text = $state('');
|
|
32
|
+
let editing = $state(false);
|
|
33
|
+
|
|
34
|
+
$effect(() => {
|
|
35
|
+
if (!editing) text = value;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const resolved = $derived(normaliseHex(text) ?? normaliseHex(value) ?? '#000000');
|
|
39
|
+
|
|
40
|
+
// Same reason as CurrencyInput: at input time the bound `text` is one
|
|
41
|
+
// keystroke behind, so the element is the only current source.
|
|
42
|
+
function onText(event: Event & { currentTarget: HTMLInputElement }) {
|
|
43
|
+
const hex = normaliseHex(event.currentTarget.value);
|
|
44
|
+
if (hex) value = hex;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function settle() {
|
|
48
|
+
editing = false;
|
|
49
|
+
value = normaliseHex(text) ?? value;
|
|
50
|
+
text = value;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function choose(hex: string) {
|
|
54
|
+
value = normaliseHex(hex) ?? value;
|
|
55
|
+
text = value;
|
|
56
|
+
}
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<div class="flex w-full flex-col gap-2 {className}">
|
|
60
|
+
<Input
|
|
61
|
+
bind:value={text}
|
|
62
|
+
{size}
|
|
63
|
+
{tone}
|
|
64
|
+
{invalid}
|
|
65
|
+
{disabled}
|
|
66
|
+
spellcheck="false"
|
|
67
|
+
autocapitalize="none"
|
|
68
|
+
placeholder="#000000"
|
|
69
|
+
oninput={onText}
|
|
70
|
+
onfocus={() => (editing = true)}
|
|
71
|
+
onblur={settle}
|
|
72
|
+
class="[&_input]:uppercase [&_input]:tabular-nums"
|
|
73
|
+
{...rest}
|
|
74
|
+
>
|
|
75
|
+
{#snippet prefix()}
|
|
76
|
+
<span
|
|
77
|
+
class="relative grid size-5 place-items-center overflow-hidden rounded border border-hairline-strong"
|
|
78
|
+
>
|
|
79
|
+
<span class="absolute inset-0" style="background-color: {resolved}"></span>
|
|
80
|
+
<input
|
|
81
|
+
type="color"
|
|
82
|
+
value={resolved}
|
|
83
|
+
{disabled}
|
|
84
|
+
aria-label="Pick a colour"
|
|
85
|
+
oninput={(event) => choose(event.currentTarget.value)}
|
|
86
|
+
class="absolute inset-0 cursor-pointer opacity-0 disabled:cursor-not-allowed"
|
|
87
|
+
/>
|
|
88
|
+
</span>
|
|
89
|
+
{/snippet}
|
|
90
|
+
</Input>
|
|
91
|
+
|
|
92
|
+
{#if swatches.length}
|
|
93
|
+
<div class="flex flex-wrap gap-1.5">
|
|
94
|
+
{#each swatches as swatch (swatch)}
|
|
95
|
+
<button
|
|
96
|
+
type="button"
|
|
97
|
+
{disabled}
|
|
98
|
+
onclick={() => choose(swatch)}
|
|
99
|
+
title={swatch}
|
|
100
|
+
aria-label={swatch}
|
|
101
|
+
aria-pressed={resolved === normaliseHex(swatch)}
|
|
102
|
+
class="size-6 rounded-lg border transition-transform duration-150 hover:-translate-y-0.5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current disabled:pointer-events-none disabled:opacity-50
|
|
103
|
+
{resolved === normaliseHex(swatch) ? 'border-text' : 'border-hairline-strong'}"
|
|
104
|
+
style="background-color: {swatch}"
|
|
105
|
+
></button>
|
|
106
|
+
{/each}
|
|
107
|
+
</div>
|
|
108
|
+
{/if}
|
|
109
|
+
</div>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
2
|
+
import type { Tone } from '../../core/tones.js';
|
|
3
|
+
import { type InputSize } from './Input.svelte';
|
|
4
|
+
interface Props extends Omit<HTMLInputAttributes, 'size' | 'prefix' | 'suffix' | 'type'> {
|
|
5
|
+
/** Bindable `#rrggbb`. Shorthand and alpha are normalised away on blur. */
|
|
6
|
+
value?: string;
|
|
7
|
+
size?: InputSize;
|
|
8
|
+
tone?: Tone;
|
|
9
|
+
invalid?: boolean;
|
|
10
|
+
/** Fixed choices under the field. Empty hides the row. */
|
|
11
|
+
swatches?: string[];
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
declare const ColorInput: import("svelte").Component<Props, {}, "value">;
|
|
15
|
+
type ColorInput = ReturnType<typeof ColorInput>;
|
|
16
|
+
export default ColorInput;
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
import { clickOutside, portal } from '../../core/actions/dismissable.js';
|
|
5
5
|
import { ListCursor, groupItems, matchQuery, revealIndex } from '../../core/list.svelte.js';
|
|
6
6
|
import { toneFocusWithinBorder, toneSoft, toneText, type Tone } from '../../core/tones.js';
|
|
7
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
8
7
|
import { iconMd } from './icon.js';
|
|
9
8
|
import { edge, soft } from './lift.js';
|
|
10
9
|
|
|
@@ -52,8 +51,6 @@
|
|
|
52
51
|
class: className = ''
|
|
53
52
|
}: Props = $props();
|
|
54
53
|
|
|
55
|
-
const t = useLocale();
|
|
56
|
-
|
|
57
54
|
let open = $state(false);
|
|
58
55
|
let query = $state('');
|
|
59
56
|
let wrapper: HTMLElement | null = $state(null);
|
|
@@ -162,7 +159,7 @@
|
|
|
162
159
|
aria-controls="{id ?? 'combobox'}-listbox"
|
|
163
160
|
aria-activedescendant={open && flat[active] ? `${id ?? 'combobox'}-opt-${active}` : undefined}
|
|
164
161
|
aria-invalid={invalid ? 'true' : undefined}
|
|
165
|
-
placeholder={selected ? selected.label : (placeholder ??
|
|
162
|
+
placeholder={selected ? selected.label : (placeholder ?? 'Search…')}
|
|
166
163
|
value={open ? query : (selected?.label ?? '')}
|
|
167
164
|
oninput={(event) => {
|
|
168
165
|
query = event.currentTarget.value;
|
|
@@ -180,7 +177,7 @@
|
|
|
180
177
|
<button
|
|
181
178
|
type="button"
|
|
182
179
|
onclick={clear}
|
|
183
|
-
aria-label=
|
|
180
|
+
aria-label="Clear selection"
|
|
184
181
|
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"
|
|
185
182
|
>
|
|
186
183
|
<svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
@@ -218,7 +215,7 @@
|
|
|
218
215
|
>
|
|
219
216
|
{#if flat.length === 0}
|
|
220
217
|
<p class="px-3 py-6 text-center text-sm text-text-muted">
|
|
221
|
-
{emptyText ??
|
|
218
|
+
{emptyText ?? 'No matches'}
|
|
222
219
|
</p>
|
|
223
220
|
{:else}
|
|
224
221
|
{#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,8 +36,6 @@
|
|
|
37
36
|
children
|
|
38
37
|
}: Props = $props();
|
|
39
38
|
|
|
40
|
-
const t = useLocale();
|
|
41
|
-
|
|
42
39
|
async function confirm() {
|
|
43
40
|
try {
|
|
44
41
|
loading = true;
|
|
@@ -88,10 +85,10 @@
|
|
|
88
85
|
|
|
89
86
|
{#snippet footer()}
|
|
90
87
|
<Button variant="outline" tone="neutral" onclick={cancel} disabled={loading}>
|
|
91
|
-
{cancelLabel ??
|
|
88
|
+
{cancelLabel ?? 'Cancel'}
|
|
92
89
|
</Button>
|
|
93
90
|
<Button {tone} onclick={confirm} {loading}>
|
|
94
|
-
{confirmLabel ??
|
|
91
|
+
{confirmLabel ?? 'Confirm'}
|
|
95
92
|
</Button>
|
|
96
93
|
{/snippet}
|
|
97
94
|
</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
|
>
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
3
|
+
import { formatGrouped, parseGrouped } from '../../core/number.js';
|
|
4
|
+
import type { Tone } from '../../core/tones.js';
|
|
5
|
+
import Input, { type InputSize } from './Input.svelte';
|
|
6
|
+
|
|
7
|
+
interface Props extends Omit<
|
|
8
|
+
HTMLInputAttributes,
|
|
9
|
+
'size' | 'prefix' | 'suffix' | 'value' | 'min' | 'max'
|
|
10
|
+
> {
|
|
11
|
+
/** Bindable amount. `null` is an empty field, which is not the same as 0. */
|
|
12
|
+
value?: number | null;
|
|
13
|
+
min?: number;
|
|
14
|
+
max?: number;
|
|
15
|
+
/** Thousands separator. */
|
|
16
|
+
group?: string;
|
|
17
|
+
/** Decimal separator. */
|
|
18
|
+
decimal?: string;
|
|
19
|
+
/** Decimal places the amount settles to when the field loses focus. */
|
|
20
|
+
precision?: number;
|
|
21
|
+
/** Leading mark — `Rp`, `$`, `€`. */
|
|
22
|
+
currency?: string;
|
|
23
|
+
/** Trailing mark instead, for the regions that write it that way. */
|
|
24
|
+
unit?: string;
|
|
25
|
+
size?: InputSize;
|
|
26
|
+
tone?: Tone;
|
|
27
|
+
invalid?: boolean;
|
|
28
|
+
class?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let {
|
|
32
|
+
value = $bindable(null),
|
|
33
|
+
min,
|
|
34
|
+
max,
|
|
35
|
+
group = ',',
|
|
36
|
+
decimal = '.',
|
|
37
|
+
precision = 2,
|
|
38
|
+
currency,
|
|
39
|
+
unit,
|
|
40
|
+
size = 'md',
|
|
41
|
+
tone = 'brand',
|
|
42
|
+
invalid = false,
|
|
43
|
+
disabled = false,
|
|
44
|
+
class: className = '',
|
|
45
|
+
...rest
|
|
46
|
+
}: Props = $props();
|
|
47
|
+
|
|
48
|
+
const options = $derived({ group, decimal, precision });
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* What the field shows. Two states on purpose: grouped and padded while the
|
|
52
|
+
* caret is elsewhere, and exactly what was typed while it is not — grouping
|
|
53
|
+
* a number as it is typed moves the caret out from under the fingers.
|
|
54
|
+
*/
|
|
55
|
+
let text = $state('');
|
|
56
|
+
let editing = $state(false);
|
|
57
|
+
|
|
58
|
+
$effect(() => {
|
|
59
|
+
if (editing) return;
|
|
60
|
+
text = value === null || value === undefined ? '' : formatGrouped(value, options);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Read off the element, not off `text`: the binding that feeds `text` has
|
|
64
|
+
// not flushed yet when this fires, so it still holds the previous keystroke.
|
|
65
|
+
function onInput(event: Event & { currentTarget: HTMLInputElement }) {
|
|
66
|
+
const parsed = parseGrouped(event.currentTarget.value, options);
|
|
67
|
+
value = parsed === null ? null : clamp(parsed);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function clamp(amount: number) {
|
|
71
|
+
if (min !== undefined && amount < min) return min;
|
|
72
|
+
if (max !== undefined && amount > max) return max;
|
|
73
|
+
return amount;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function settle() {
|
|
77
|
+
editing = false;
|
|
78
|
+
const parsed = parseGrouped(text, options);
|
|
79
|
+
value = parsed === null ? null : clamp(parsed);
|
|
80
|
+
text = value === null ? '' : formatGrouped(value, options);
|
|
81
|
+
}
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<Input
|
|
85
|
+
bind:value={text}
|
|
86
|
+
{size}
|
|
87
|
+
{tone}
|
|
88
|
+
{invalid}
|
|
89
|
+
{disabled}
|
|
90
|
+
inputmode="decimal"
|
|
91
|
+
oninput={onInput}
|
|
92
|
+
onfocus={() => (editing = true)}
|
|
93
|
+
onblur={settle}
|
|
94
|
+
class={className}
|
|
95
|
+
{...rest}
|
|
96
|
+
>
|
|
97
|
+
{#snippet prefix()}
|
|
98
|
+
{#if currency}
|
|
99
|
+
<span class="text-xs font-medium text-text-muted">{currency}</span>
|
|
100
|
+
{/if}
|
|
101
|
+
{/snippet}
|
|
102
|
+
|
|
103
|
+
{#snippet suffix()}
|
|
104
|
+
{#if unit}
|
|
105
|
+
<span class="text-xs font-medium text-text-muted">{unit}</span>
|
|
106
|
+
{/if}
|
|
107
|
+
{/snippet}
|
|
108
|
+
</Input>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
2
|
+
import type { Tone } from '../../core/tones.js';
|
|
3
|
+
import { type InputSize } from './Input.svelte';
|
|
4
|
+
interface Props extends Omit<HTMLInputAttributes, 'size' | 'prefix' | 'suffix' | 'value' | 'min' | 'max'> {
|
|
5
|
+
/** Bindable amount. `null` is an empty field, which is not the same as 0. */
|
|
6
|
+
value?: number | null;
|
|
7
|
+
min?: number;
|
|
8
|
+
max?: number;
|
|
9
|
+
/** Thousands separator. */
|
|
10
|
+
group?: string;
|
|
11
|
+
/** Decimal separator. */
|
|
12
|
+
decimal?: string;
|
|
13
|
+
/** Decimal places the amount settles to when the field loses focus. */
|
|
14
|
+
precision?: number;
|
|
15
|
+
/** Leading mark — `Rp`, `$`, `€`. */
|
|
16
|
+
currency?: string;
|
|
17
|
+
/** Trailing mark instead, for the regions that write it that way. */
|
|
18
|
+
unit?: string;
|
|
19
|
+
size?: InputSize;
|
|
20
|
+
tone?: Tone;
|
|
21
|
+
invalid?: boolean;
|
|
22
|
+
class?: string;
|
|
23
|
+
}
|
|
24
|
+
declare const CurrencyInput: import("svelte").Component<Props, {}, "value">;
|
|
25
|
+
type CurrencyInput = ReturnType<typeof CurrencyInput>;
|
|
26
|
+
export default CurrencyInput;
|