@nqmcreative/ui 0.2.3 → 0.4.0
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 +209 -15
- package/cli/index.mjs +328 -112
- package/dist/core/color.d.ts +24 -0
- package/dist/core/color.js +39 -0
- package/dist/core/index.d.ts +8 -1
- package/dist/core/index.js +5 -1
- package/dist/core/locale.svelte.d.ts +8 -0
- package/dist/core/locale.svelte.js +14 -0
- package/dist/core/number.d.ts +22 -0
- package/dist/core/number.js +33 -0
- 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/core/toast.svelte.d.ts +3 -0
- package/dist/styles/matte/AccordionItem.svelte +5 -0
- package/dist/styles/matte/AccordionItem.svelte.d.ts +2 -0
- package/dist/styles/matte/Avatar.svelte +15 -0
- package/dist/styles/matte/Avatar.svelte.d.ts +3 -0
- package/dist/styles/matte/Badge.svelte +5 -0
- package/dist/styles/matte/Badge.svelte.d.ts +2 -0
- package/dist/styles/matte/Breadcrumb.svelte +11 -4
- package/dist/styles/matte/Breadcrumb.svelte.d.ts +3 -0
- package/dist/styles/matte/Button.svelte +58 -7
- package/dist/styles/matte/Button.svelte.d.ts +14 -2
- package/dist/styles/matte/Card.svelte +6 -1
- package/dist/styles/matte/Card.svelte.d.ts +2 -0
- package/dist/styles/matte/CheckboxGroup.svelte +6 -0
- package/dist/styles/matte/CheckboxGroup.svelte.d.ts +2 -0
- package/dist/styles/matte/ColorInput.svelte +110 -0
- package/dist/styles/matte/ColorInput.svelte.d.ts +16 -0
- package/dist/styles/matte/Combobox.svelte +5 -0
- package/dist/styles/matte/Combobox.svelte.d.ts +3 -0
- package/dist/styles/matte/CommandPalette.svelte +6 -0
- package/dist/styles/matte/CommandPalette.svelte.d.ts +3 -0
- package/dist/styles/matte/CurrencyInput.svelte +108 -0
- package/dist/styles/matte/CurrencyInput.svelte.d.ts +26 -0
- package/dist/styles/matte/Drawer.svelte +5 -0
- package/dist/styles/matte/Drawer.svelte.d.ts +2 -0
- package/dist/styles/matte/FileInput.svelte +194 -0
- package/dist/styles/matte/FileInput.svelte.d.ts +32 -0
- package/dist/styles/matte/Footer.svelte +6 -2
- package/dist/styles/matte/Footer.svelte.d.ts +2 -0
- package/dist/styles/matte/InputAddon.svelte +36 -0
- package/dist/styles/matte/InputAddon.svelte.d.ts +13 -0
- package/dist/styles/matte/Link.svelte +9 -0
- package/dist/styles/matte/Link.svelte.d.ts +4 -0
- package/dist/styles/matte/MenuItem.svelte +2 -1
- package/dist/styles/matte/Modal.svelte +5 -0
- package/dist/styles/matte/Modal.svelte.d.ts +2 -0
- package/dist/styles/matte/MultiSelect.svelte +7 -1
- package/dist/styles/matte/MultiSelect.svelte.d.ts +3 -0
- package/dist/styles/matte/Navbar.svelte +18 -4
- package/dist/styles/matte/Navbar.svelte.d.ts +2 -0
- package/dist/styles/matte/PinInput.svelte +137 -0
- package/dist/styles/matte/PinInput.svelte.d.ts +25 -0
- package/dist/styles/matte/PricingCard.svelte +16 -8
- package/dist/styles/matte/PricingCard.svelte.d.ts +2 -0
- package/dist/styles/matte/RadioGroup.svelte +6 -0
- package/dist/styles/matte/RadioGroup.svelte.d.ts +2 -0
- package/dist/styles/matte/SearchInput.svelte +87 -0
- package/dist/styles/matte/SearchInput.svelte.d.ts +24 -0
- package/dist/styles/matte/SegmentedControl.svelte +16 -2
- package/dist/styles/matte/SegmentedControl.svelte.d.ts +5 -0
- package/dist/styles/matte/Sidebar.svelte +2 -1
- package/dist/styles/matte/Stat.svelte +8 -1
- package/dist/styles/matte/Stat.svelte.d.ts +3 -0
- package/dist/styles/matte/Steps.svelte +7 -0
- package/dist/styles/matte/Steps.svelte.d.ts +3 -0
- package/dist/styles/matte/Table.svelte +153 -97
- package/dist/styles/matte/Table.svelte.d.ts +12 -0
- package/dist/styles/matte/Tabs.svelte +5 -0
- package/dist/styles/matte/Tabs.svelte.d.ts +3 -0
- package/dist/styles/matte/TagsInput.svelte +173 -0
- package/dist/styles/matte/TagsInput.svelte.d.ts +29 -0
- package/dist/styles/matte/TimeInput.svelte +129 -0
- package/dist/styles/matte/TimeInput.svelte.d.ts +21 -0
- package/dist/styles/matte/Toaster.svelte +3 -0
- package/dist/styles/matte/icon.d.ts +21 -0
- package/dist/styles/matte/icon.js +21 -0
- package/dist/styles/matte/index.d.ts +8 -2
- package/dist/styles/matte/index.js +8 -2
- package/dist/styles/paper/AccordionItem.svelte +5 -0
- package/dist/styles/paper/AccordionItem.svelte.d.ts +2 -0
- package/dist/styles/paper/Avatar.svelte +15 -0
- package/dist/styles/paper/Avatar.svelte.d.ts +3 -0
- package/dist/styles/paper/Badge.svelte +5 -0
- package/dist/styles/paper/Badge.svelte.d.ts +2 -0
- package/dist/styles/paper/Breadcrumb.svelte +8 -4
- package/dist/styles/paper/Breadcrumb.svelte.d.ts +3 -0
- package/dist/styles/paper/Button.svelte +58 -7
- package/dist/styles/paper/Button.svelte.d.ts +14 -2
- package/dist/styles/paper/Card.svelte +6 -1
- package/dist/styles/paper/Card.svelte.d.ts +2 -0
- package/dist/styles/paper/CheckboxGroup.svelte +6 -0
- package/dist/styles/paper/CheckboxGroup.svelte.d.ts +2 -0
- package/dist/styles/paper/ColorInput.svelte +112 -0
- package/dist/styles/paper/ColorInput.svelte.d.ts +16 -0
- package/dist/styles/paper/Combobox.svelte +5 -0
- package/dist/styles/paper/Combobox.svelte.d.ts +3 -0
- package/dist/styles/paper/CommandPalette.svelte +6 -0
- package/dist/styles/paper/CommandPalette.svelte.d.ts +3 -0
- package/dist/styles/paper/CurrencyInput.svelte +108 -0
- package/dist/styles/paper/CurrencyInput.svelte.d.ts +26 -0
- package/dist/styles/paper/Drawer.svelte +5 -0
- package/dist/styles/paper/Drawer.svelte.d.ts +2 -0
- package/dist/styles/paper/FileInput.svelte +204 -0
- package/dist/styles/paper/FileInput.svelte.d.ts +32 -0
- package/dist/styles/paper/Footer.svelte +6 -2
- package/dist/styles/paper/Footer.svelte.d.ts +2 -0
- package/dist/styles/paper/InputAddon.svelte +36 -0
- package/dist/styles/paper/InputAddon.svelte.d.ts +13 -0
- package/dist/styles/paper/Link.svelte +9 -0
- package/dist/styles/paper/Link.svelte.d.ts +4 -0
- package/dist/styles/paper/MenuItem.svelte +2 -1
- package/dist/styles/paper/Modal.svelte +5 -0
- package/dist/styles/paper/Modal.svelte.d.ts +2 -0
- package/dist/styles/paper/MultiSelect.svelte +7 -1
- package/dist/styles/paper/MultiSelect.svelte.d.ts +3 -0
- package/dist/styles/paper/Navbar.svelte +18 -4
- package/dist/styles/paper/Navbar.svelte.d.ts +2 -0
- package/dist/styles/paper/PinInput.svelte +137 -0
- package/dist/styles/paper/PinInput.svelte.d.ts +25 -0
- package/dist/styles/paper/PricingCard.svelte +23 -15
- package/dist/styles/paper/PricingCard.svelte.d.ts +2 -0
- package/dist/styles/paper/RadioGroup.svelte +6 -0
- package/dist/styles/paper/RadioGroup.svelte.d.ts +2 -0
- package/dist/styles/paper/SearchInput.svelte +92 -0
- package/dist/styles/paper/SearchInput.svelte.d.ts +24 -0
- package/dist/styles/paper/SegmentedControl.svelte +16 -2
- package/dist/styles/paper/SegmentedControl.svelte.d.ts +5 -0
- package/dist/styles/paper/Sidebar.svelte +2 -1
- package/dist/styles/paper/Stat.svelte +8 -1
- package/dist/styles/paper/Stat.svelte.d.ts +3 -0
- package/dist/styles/paper/Steps.svelte +7 -0
- package/dist/styles/paper/Steps.svelte.d.ts +3 -0
- package/dist/styles/paper/Table.svelte +153 -97
- package/dist/styles/paper/Table.svelte.d.ts +12 -0
- package/dist/styles/paper/Tabs.svelte +5 -0
- package/dist/styles/paper/Tabs.svelte.d.ts +3 -0
- package/dist/styles/paper/TagsInput.svelte +178 -0
- package/dist/styles/paper/TagsInput.svelte.d.ts +29 -0
- package/dist/styles/paper/TimeInput.svelte +155 -0
- package/dist/styles/paper/TimeInput.svelte.d.ts +21 -0
- package/dist/styles/paper/Toaster.svelte +3 -0
- package/dist/styles/paper/icon.d.ts +21 -0
- package/dist/styles/paper/icon.js +21 -0
- package/dist/styles/paper/index.d.ts +8 -2
- package/dist/styles/paper/index.js +8 -2
- package/dist/styles/sprout/AccordionItem.svelte +5 -0
- package/dist/styles/sprout/AccordionItem.svelte.d.ts +2 -0
- package/dist/styles/sprout/Avatar.svelte +15 -0
- package/dist/styles/sprout/Avatar.svelte.d.ts +3 -0
- package/dist/styles/sprout/Badge.svelte +5 -0
- package/dist/styles/sprout/Badge.svelte.d.ts +2 -0
- package/dist/styles/sprout/Breadcrumb.svelte +8 -4
- package/dist/styles/sprout/Breadcrumb.svelte.d.ts +3 -0
- package/dist/styles/sprout/Button.svelte +58 -7
- package/dist/styles/sprout/Button.svelte.d.ts +14 -2
- package/dist/styles/sprout/Card.svelte +6 -1
- package/dist/styles/sprout/Card.svelte.d.ts +2 -0
- package/dist/styles/sprout/CheckboxGroup.svelte +6 -0
- package/dist/styles/sprout/CheckboxGroup.svelte.d.ts +2 -0
- package/dist/styles/sprout/ColorInput.svelte +112 -0
- package/dist/styles/sprout/ColorInput.svelte.d.ts +16 -0
- package/dist/styles/sprout/Combobox.svelte +5 -0
- package/dist/styles/sprout/Combobox.svelte.d.ts +3 -0
- package/dist/styles/sprout/CommandPalette.svelte +6 -0
- package/dist/styles/sprout/CommandPalette.svelte.d.ts +3 -0
- package/dist/styles/sprout/CurrencyInput.svelte +108 -0
- package/dist/styles/sprout/CurrencyInput.svelte.d.ts +26 -0
- package/dist/styles/sprout/Drawer.svelte +5 -0
- package/dist/styles/sprout/Drawer.svelte.d.ts +2 -0
- package/dist/styles/sprout/FileInput.svelte +205 -0
- package/dist/styles/sprout/FileInput.svelte.d.ts +32 -0
- package/dist/styles/sprout/Footer.svelte +6 -2
- package/dist/styles/sprout/Footer.svelte.d.ts +2 -0
- package/dist/styles/sprout/InputAddon.svelte +37 -0
- package/dist/styles/sprout/InputAddon.svelte.d.ts +13 -0
- package/dist/styles/sprout/Link.svelte +9 -0
- package/dist/styles/sprout/Link.svelte.d.ts +4 -0
- package/dist/styles/sprout/MenuItem.svelte +2 -1
- package/dist/styles/sprout/Modal.svelte +5 -0
- package/dist/styles/sprout/Modal.svelte.d.ts +2 -0
- package/dist/styles/sprout/MultiSelect.svelte +7 -1
- package/dist/styles/sprout/MultiSelect.svelte.d.ts +3 -0
- package/dist/styles/sprout/Navbar.svelte +18 -4
- package/dist/styles/sprout/Navbar.svelte.d.ts +2 -0
- package/dist/styles/sprout/PinInput.svelte +138 -0
- package/dist/styles/sprout/PinInput.svelte.d.ts +25 -0
- package/dist/styles/sprout/PricingCard.svelte +23 -15
- package/dist/styles/sprout/PricingCard.svelte.d.ts +2 -0
- package/dist/styles/sprout/RadioGroup.svelte +6 -0
- package/dist/styles/sprout/RadioGroup.svelte.d.ts +2 -0
- package/dist/styles/sprout/SearchInput.svelte +92 -0
- package/dist/styles/sprout/SearchInput.svelte.d.ts +24 -0
- package/dist/styles/sprout/SegmentedControl.svelte +16 -2
- package/dist/styles/sprout/SegmentedControl.svelte.d.ts +5 -0
- package/dist/styles/sprout/Sidebar.svelte +2 -1
- package/dist/styles/sprout/Stat.svelte +8 -1
- package/dist/styles/sprout/Stat.svelte.d.ts +3 -0
- package/dist/styles/sprout/Steps.svelte +7 -0
- package/dist/styles/sprout/Steps.svelte.d.ts +3 -0
- package/dist/styles/sprout/Table.svelte +153 -97
- package/dist/styles/sprout/Table.svelte.d.ts +12 -0
- package/dist/styles/sprout/Tabs.svelte +5 -0
- package/dist/styles/sprout/Tabs.svelte.d.ts +3 -0
- package/dist/styles/sprout/TagsInput.svelte +179 -0
- package/dist/styles/sprout/TagsInput.svelte.d.ts +29 -0
- package/dist/styles/sprout/TimeInput.svelte +155 -0
- package/dist/styles/sprout/TimeInput.svelte.d.ts +21 -0
- package/dist/styles/sprout/Toaster.svelte +3 -0
- package/dist/styles/sprout/icon.d.ts +21 -0
- package/dist/styles/sprout/icon.js +21 -0
- package/dist/styles/sprout/index.d.ts +8 -2
- package/dist/styles/sprout/index.js +8 -2
- package/package.json +98 -26
- package/registry.json +143 -35
- package/dist/styles/matte/NumberInput.svelte +0 -96
- package/dist/styles/matte/NumberInput.svelte.d.ts +0 -15
- package/dist/styles/matte/PasswordInput.svelte +0 -95
- package/dist/styles/matte/PasswordInput.svelte.d.ts +0 -12
- package/dist/styles/paper/NumberInput.svelte +0 -96
- package/dist/styles/paper/NumberInput.svelte.d.ts +0 -15
- package/dist/styles/paper/PasswordInput.svelte +0 -100
- package/dist/styles/paper/PasswordInput.svelte.d.ts +0 -12
- package/dist/styles/sprout/NumberInput.svelte +0 -97
- package/dist/styles/sprout/NumberInput.svelte.d.ts +0 -15
- package/dist/styles/sprout/PasswordInput.svelte +0 -101
- package/dist/styles/sprout/PasswordInput.svelte.d.ts +0 -12
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
4
|
+
import { useLocale } from '../../core/locale.svelte.js';
|
|
5
|
+
import type { Tone } from '../../core/tones.js';
|
|
6
|
+
import Input, { type InputSize } from './Input.svelte';
|
|
7
|
+
|
|
8
|
+
interface Props extends Omit<HTMLInputAttributes, 'size' | 'prefix' | 'type'> {
|
|
9
|
+
value?: string;
|
|
10
|
+
size?: InputSize;
|
|
11
|
+
tone?: Tone;
|
|
12
|
+
invalid?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Milliseconds to wait after the last keystroke before `onsearch` fires.
|
|
15
|
+
* `0` fires on every one.
|
|
16
|
+
*/
|
|
17
|
+
debounce?: number;
|
|
18
|
+
/** The debounced value. The bound `value` still updates immediately. */
|
|
19
|
+
onsearch?: (value: string) => void;
|
|
20
|
+
onclear?: () => void;
|
|
21
|
+
/** Replaces the magnifier. */
|
|
22
|
+
icon?: Snippet;
|
|
23
|
+
class?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let {
|
|
27
|
+
value = $bindable(''),
|
|
28
|
+
size = 'md',
|
|
29
|
+
tone = 'brand',
|
|
30
|
+
invalid = false,
|
|
31
|
+
disabled = false,
|
|
32
|
+
debounce = 0,
|
|
33
|
+
onsearch,
|
|
34
|
+
onclear,
|
|
35
|
+
icon,
|
|
36
|
+
class: className = '',
|
|
37
|
+
...rest
|
|
38
|
+
}: Props = $props();
|
|
39
|
+
|
|
40
|
+
const t = useLocale();
|
|
41
|
+
|
|
42
|
+
// The timer is cleared by the effect's own teardown, so a keystroke that
|
|
43
|
+
// lands mid-wait restarts the clock rather than queueing a second call.
|
|
44
|
+
$effect(() => {
|
|
45
|
+
const next = value;
|
|
46
|
+
if (!onsearch) return;
|
|
47
|
+
if (debounce <= 0) {
|
|
48
|
+
onsearch(next);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const timer = setTimeout(() => onsearch(next), debounce);
|
|
52
|
+
return () => clearTimeout(timer);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
function clear() {
|
|
56
|
+
value = '';
|
|
57
|
+
onclear?.();
|
|
58
|
+
}
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<Input bind:value type="search" {size} {tone} {invalid} {disabled} class={className} {...rest}>
|
|
62
|
+
{#snippet prefix()}
|
|
63
|
+
{#if icon}
|
|
64
|
+
{@render icon()}
|
|
65
|
+
{:else}
|
|
66
|
+
<svg class="size-4" viewBox="0 0 20 20" fill="none" aria-hidden="true">
|
|
67
|
+
<circle cx="9" cy="9" r="5.5" stroke="currentColor" stroke-width="1.6" />
|
|
68
|
+
<path d="m13 13 3.5 3.5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" />
|
|
69
|
+
</svg>
|
|
70
|
+
{/if}
|
|
71
|
+
{/snippet}
|
|
72
|
+
|
|
73
|
+
{#snippet suffix()}
|
|
74
|
+
{#if value}
|
|
75
|
+
<button
|
|
76
|
+
type="button"
|
|
77
|
+
onclick={clear}
|
|
78
|
+
aria-label={t.current.clearSearch}
|
|
79
|
+
class="-mr-1 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"
|
|
80
|
+
>
|
|
81
|
+
<svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
82
|
+
<path
|
|
83
|
+
d="m2 2 10 10M12 2 2 12"
|
|
84
|
+
stroke="currentColor"
|
|
85
|
+
stroke-width="1.6"
|
|
86
|
+
stroke-linecap="round"
|
|
87
|
+
/>
|
|
88
|
+
</svg>
|
|
89
|
+
</button>
|
|
90
|
+
{/if}
|
|
91
|
+
{/snippet}
|
|
92
|
+
</Input>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
3
|
+
import type { Tone } from '../../core/tones.js';
|
|
4
|
+
import { type InputSize } from './Input.svelte';
|
|
5
|
+
interface Props extends Omit<HTMLInputAttributes, 'size' | 'prefix' | 'type'> {
|
|
6
|
+
value?: string;
|
|
7
|
+
size?: InputSize;
|
|
8
|
+
tone?: Tone;
|
|
9
|
+
invalid?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Milliseconds to wait after the last keystroke before `onsearch` fires.
|
|
12
|
+
* `0` fires on every one.
|
|
13
|
+
*/
|
|
14
|
+
debounce?: number;
|
|
15
|
+
/** The debounced value. The bound `value` still updates immediately. */
|
|
16
|
+
onsearch?: (value: string) => void;
|
|
17
|
+
onclear?: () => void;
|
|
18
|
+
/** Replaces the magnifier. */
|
|
19
|
+
icon?: Snippet;
|
|
20
|
+
class?: string;
|
|
21
|
+
}
|
|
22
|
+
declare const SearchInput: import("svelte").Component<Props, {}, "value">;
|
|
23
|
+
type SearchInput = ReturnType<typeof SearchInput>;
|
|
24
|
+
export default SearchInput;
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
2
3
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
4
|
import { focusRing, toneRing, type Tone } from '../../core/tones.js';
|
|
5
|
+
import { iconMd } from './icon.js';
|
|
4
6
|
import { edge } from './lift.js';
|
|
5
7
|
|
|
6
8
|
export interface SegmentOption {
|
|
7
9
|
value: string;
|
|
8
10
|
label: string;
|
|
11
|
+
/** Leading icon, 16px. */
|
|
12
|
+
icon?: Snippet;
|
|
9
13
|
disabled?: boolean;
|
|
10
14
|
}
|
|
11
15
|
|
|
@@ -21,6 +25,8 @@
|
|
|
21
25
|
tone?: Tone;
|
|
22
26
|
/** Spread the segments evenly across the full width. */
|
|
23
27
|
fullWidth?: boolean;
|
|
28
|
+
/** Icons only — each option's `label` becomes its accessible name. */
|
|
29
|
+
iconOnly?: boolean;
|
|
24
30
|
label?: string;
|
|
25
31
|
disabled?: boolean;
|
|
26
32
|
onchange?: (value: string) => void;
|
|
@@ -33,6 +39,7 @@
|
|
|
33
39
|
size = 'md',
|
|
34
40
|
tone = 'brand',
|
|
35
41
|
fullWidth = false,
|
|
42
|
+
iconOnly = false,
|
|
36
43
|
label,
|
|
37
44
|
disabled = false,
|
|
38
45
|
class: className = '',
|
|
@@ -45,6 +52,11 @@
|
|
|
45
52
|
md: 'h-8 px-3.5 text-sm'
|
|
46
53
|
};
|
|
47
54
|
|
|
55
|
+
const squares: Record<SegmentedSize, string> = {
|
|
56
|
+
sm: 'size-7 text-[13px]',
|
|
57
|
+
md: 'size-8 text-sm'
|
|
58
|
+
};
|
|
59
|
+
|
|
48
60
|
function select(option: SegmentOption) {
|
|
49
61
|
if (option.disabled || disabled) return;
|
|
50
62
|
value = option.value;
|
|
@@ -75,14 +87,16 @@
|
|
|
75
87
|
type="button"
|
|
76
88
|
role="radio"
|
|
77
89
|
aria-checked={value === option.value}
|
|
90
|
+
aria-label={iconOnly ? option.label : undefined}
|
|
78
91
|
disabled={option.disabled || disabled}
|
|
79
92
|
tabindex={value === option.value ? 0 : -1}
|
|
80
93
|
onclick={() => select(option)}
|
|
81
94
|
class="inline-flex items-center justify-center rounded-xl font-sans font-medium whitespace-nowrap transition-all duration-150 ease-brand-out disabled:pointer-events-none disabled:opacity-40
|
|
82
|
-
{sizes[size]} {focusRing} {toneRing[tone]}
|
|
95
|
+
{iconOnly ? squares[size] : sizes[size]} {focusRing} {toneRing[tone]}
|
|
83
96
|
{value === option.value ? `bg-bg text-text ${edge}` : 'text-text-muted hover:text-text'}"
|
|
84
97
|
>
|
|
85
|
-
{option.
|
|
98
|
+
{#if option.icon}<span class={iconMd}>{@render option.icon()}</span>{/if}
|
|
99
|
+
{#if !iconOnly}{option.label}{/if}
|
|
86
100
|
</button>
|
|
87
101
|
{/each}
|
|
88
102
|
{#if name}<input type="hidden" {name} {value} />{/if}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
1
2
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
3
|
import { type Tone } from '../../core/tones.js';
|
|
3
4
|
export interface SegmentOption {
|
|
4
5
|
value: string;
|
|
5
6
|
label: string;
|
|
7
|
+
/** Leading icon, 16px. */
|
|
8
|
+
icon?: Snippet;
|
|
6
9
|
disabled?: boolean;
|
|
7
10
|
}
|
|
8
11
|
export type SegmentedSize = 'sm' | 'md';
|
|
@@ -16,6 +19,8 @@ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'onchange'> {
|
|
|
16
19
|
tone?: Tone;
|
|
17
20
|
/** Spread the segments evenly across the full width. */
|
|
18
21
|
fullWidth?: boolean;
|
|
22
|
+
/** Icons only — each option's `label` becomes its accessible name. */
|
|
23
|
+
iconOnly?: boolean;
|
|
19
24
|
label?: string;
|
|
20
25
|
disabled?: boolean;
|
|
21
26
|
onchange?: (value: string) => void;
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
|
|
37
37
|
<script lang="ts">
|
|
38
38
|
import { useLocale } from '../../core/locale.svelte.js';
|
|
39
|
+
import { iconMd } from './icon.js';
|
|
39
40
|
import { focusRing, toneRing, toneSoft, type Tone } from '../../core/tones.js';
|
|
40
41
|
|
|
41
42
|
interface Props {
|
|
@@ -126,7 +127,7 @@
|
|
|
126
127
|
<!-- One box for every glyph, so rows line up whether or not an item has one. -->
|
|
127
128
|
{#snippet glyph(item: SidebarItem)}
|
|
128
129
|
{#if item.icon}
|
|
129
|
-
<span class=
|
|
130
|
+
<span class={iconMd}>{@render item.icon()}</span>
|
|
130
131
|
{/if}
|
|
131
132
|
{/snippet}
|
|
132
133
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
2
3
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
4
|
import { toneText, type Tone } from '../../core/tones.js';
|
|
5
|
+
import { iconMd } from './icon.js';
|
|
4
6
|
|
|
5
7
|
export type StatTrend = 'up' | 'down' | 'flat';
|
|
6
8
|
|
|
@@ -11,6 +13,8 @@
|
|
|
11
13
|
delta?: string;
|
|
12
14
|
trend?: StatTrend;
|
|
13
15
|
hint?: string;
|
|
16
|
+
/** Leading icon on the label row. */
|
|
17
|
+
icon?: Snippet;
|
|
14
18
|
/** Colour of the value itself. Leave unset for plain `text`. */
|
|
15
19
|
tone?: Tone;
|
|
16
20
|
}
|
|
@@ -21,6 +25,7 @@
|
|
|
21
25
|
delta,
|
|
22
26
|
trend = 'flat',
|
|
23
27
|
hint,
|
|
28
|
+
icon,
|
|
24
29
|
tone,
|
|
25
30
|
class: className = '',
|
|
26
31
|
...rest
|
|
@@ -31,7 +36,9 @@
|
|
|
31
36
|
</script>
|
|
32
37
|
|
|
33
38
|
<div class="flex flex-col gap-1 font-sans {className}" {...rest}>
|
|
34
|
-
<span class="text-sm font-medium text-text-muted"
|
|
39
|
+
<span class="flex items-center gap-2 text-sm font-medium text-text-muted"
|
|
40
|
+
>{#if icon}<span class={iconMd}>{@render icon()}</span>{/if}{label}</span
|
|
41
|
+
>
|
|
35
42
|
<div class="flex items-baseline gap-2">
|
|
36
43
|
<span
|
|
37
44
|
class="font-heading text-3xl font-semibold tracking-tight tabular-nums {tone
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
1
2
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
3
|
import { type Tone } from '../../core/tones.js';
|
|
3
4
|
export type StatTrend = 'up' | 'down' | 'flat';
|
|
@@ -8,6 +9,8 @@ interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
|
8
9
|
delta?: string;
|
|
9
10
|
trend?: StatTrend;
|
|
10
11
|
hint?: string;
|
|
12
|
+
/** Leading icon on the label row. */
|
|
13
|
+
icon?: Snippet;
|
|
11
14
|
/** Colour of the value itself. Leave unset for plain `text`. */
|
|
12
15
|
tone?: Tone;
|
|
13
16
|
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
<script module lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
2
4
|
export interface StepItem {
|
|
3
5
|
label: string;
|
|
4
6
|
description?: string;
|
|
7
|
+
/** Replaces the step number. A finished step still shows its check. */
|
|
8
|
+
icon?: Snippet;
|
|
5
9
|
/** Marks the step as failed — shown in `danger` regardless of position. */
|
|
6
10
|
error?: boolean;
|
|
7
11
|
disabled?: boolean;
|
|
@@ -10,6 +14,7 @@
|
|
|
10
14
|
|
|
11
15
|
<script lang="ts">
|
|
12
16
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
17
|
+
import { iconMd } from './icon.js';
|
|
13
18
|
import { focusRing, toneFill, toneRing, toneText, type Tone } from '../../core/tones.js';
|
|
14
19
|
import { liftSm } from './lift.js';
|
|
15
20
|
|
|
@@ -75,6 +80,8 @@
|
|
|
75
80
|
stroke-linejoin="round"
|
|
76
81
|
/>
|
|
77
82
|
</svg>
|
|
83
|
+
{:else if item.icon}
|
|
84
|
+
<span class={iconMd}>{@render item.icon()}</span>
|
|
78
85
|
{:else}
|
|
79
86
|
{index + 1}
|
|
80
87
|
{/if}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
1
2
|
export interface StepItem {
|
|
2
3
|
label: string;
|
|
3
4
|
description?: string;
|
|
5
|
+
/** Replaces the step number. A finished step still shows its check. */
|
|
6
|
+
icon?: Snippet;
|
|
4
7
|
/** Marks the step as failed — shown in `danger` regardless of position. */
|
|
5
8
|
error?: boolean;
|
|
6
9
|
disabled?: boolean;
|
|
@@ -33,6 +33,16 @@
|
|
|
33
33
|
/** Header stays put while the body scrolls — pair with a `max-h-*` class. */
|
|
34
34
|
stickyHeader?: boolean;
|
|
35
35
|
|
|
36
|
+
/** Bar above the table — a title, a search field, a filter row. */
|
|
37
|
+
header?: Snippet;
|
|
38
|
+
/** Bar below the table — pagination, a total, a summary line. */
|
|
39
|
+
footer?: Snippet;
|
|
40
|
+
/**
|
|
41
|
+
* Shown in place of `header` while rows are ticked, after the count and the
|
|
42
|
+
* clear button. Passing it is what turns the bulk bar on.
|
|
43
|
+
*/
|
|
44
|
+
bulkActions?: Snippet<[{ selected: string[]; clear: () => void }]>;
|
|
45
|
+
|
|
36
46
|
/** Bindable. Set it to sort; leave `sortable` columns to drive it. */
|
|
37
47
|
sort?: TableSort | null;
|
|
38
48
|
/** Sort the rows here rather than refetching them server-side. Default true. */
|
|
@@ -58,6 +68,9 @@
|
|
|
58
68
|
hoverable = true,
|
|
59
69
|
compact = false,
|
|
60
70
|
stickyHeader = false,
|
|
71
|
+
header,
|
|
72
|
+
footer,
|
|
73
|
+
bulkActions,
|
|
61
74
|
sort = $bindable(null),
|
|
62
75
|
sortLocally = true,
|
|
63
76
|
onsort,
|
|
@@ -107,112 +120,155 @@
|
|
|
107
120
|
selected = toggleKey(selected, keyOf(row));
|
|
108
121
|
onselect?.(selected);
|
|
109
122
|
}
|
|
123
|
+
|
|
124
|
+
function clearSelection() {
|
|
125
|
+
selected = [];
|
|
126
|
+
onselect?.(selected);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** The bulk bar takes the header's place, so only one of the two is up. */
|
|
130
|
+
const bulkBar = $derived(selectable && bulkActions !== undefined && selected.length > 0);
|
|
110
131
|
</script>
|
|
111
132
|
|
|
112
133
|
<div
|
|
113
|
-
class="w-full overflow-
|
|
134
|
+
class="flex w-full flex-col overflow-hidden rounded-[20px] border border-hairline bg-bg {edge} {className}"
|
|
114
135
|
{...rest}
|
|
115
136
|
>
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
137
|
+
{#if bulkBar}
|
|
138
|
+
<div
|
|
139
|
+
class="flex flex-wrap items-center gap-3 border-b border-hairline bg-brand-light px-4 py-3"
|
|
140
|
+
>
|
|
141
|
+
<span class="font-sans text-[13px] font-semibold text-text">
|
|
142
|
+
{selected.length}
|
|
143
|
+
{t.current.selected}
|
|
144
|
+
</span>
|
|
145
|
+
<button
|
|
146
|
+
type="button"
|
|
147
|
+
onclick={clearSelection}
|
|
148
|
+
class="rounded-full font-sans text-[13px] font-medium text-text-muted transition-colors duration-150 hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
|
|
120
149
|
>
|
|
121
|
-
{
|
|
122
|
-
</
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
150
|
+
{t.current.clear}
|
|
151
|
+
</button>
|
|
152
|
+
<div class="ml-auto flex flex-wrap items-center gap-2">
|
|
153
|
+
{@render bulkActions?.({ selected, clear: clearSelection })}
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
{:else if header}
|
|
157
|
+
<div
|
|
158
|
+
class="flex flex-wrap items-center gap-3 border-b border-hairline px-4 py-3 font-sans text-[13px] text-text-secondary"
|
|
159
|
+
>
|
|
160
|
+
{@render header()}
|
|
161
|
+
</div>
|
|
162
|
+
{/if}
|
|
163
|
+
<div class="min-h-0 w-full flex-1 overflow-auto">
|
|
164
|
+
<table class="w-full border-collapse font-sans text-sm">
|
|
165
|
+
{#if caption}
|
|
166
|
+
<caption
|
|
167
|
+
class="border-b border-hairline px-4 py-3 text-left font-sans text-[13px] text-text-muted"
|
|
168
|
+
>
|
|
169
|
+
{caption}
|
|
170
|
+
</caption>
|
|
171
|
+
{/if}
|
|
172
|
+
<thead class={stickyHeader ? 'sticky top-0 z-10' : ''}>
|
|
173
|
+
<tr class="border-b border-hairline bg-bg-alt">
|
|
174
|
+
{#if selectable}
|
|
175
|
+
<th scope="col" class="w-10 {pad}">
|
|
176
|
+
<Checkbox
|
|
177
|
+
checked={allSelected}
|
|
178
|
+
indeterminate={someSelected}
|
|
179
|
+
{tone}
|
|
180
|
+
aria-label={t.current.selectAllRows}
|
|
181
|
+
onchange={toggleAll}
|
|
182
|
+
/>
|
|
183
|
+
</th>
|
|
184
|
+
{/if}
|
|
185
|
+
{#each columns as column (column.key)}
|
|
186
|
+
<th
|
|
187
|
+
scope="col"
|
|
188
|
+
aria-sort={sort?.key === column.key
|
|
189
|
+
? sort.direction === 'asc'
|
|
190
|
+
? 'ascending'
|
|
191
|
+
: 'descending'
|
|
192
|
+
: undefined}
|
|
193
|
+
class="text-[13px] font-semibold text-text-muted {pad} {aligns[
|
|
194
|
+
column.align ?? 'left'
|
|
195
|
+
]} {column.class ?? ''}"
|
|
196
|
+
>
|
|
197
|
+
{#if column.sortable}
|
|
198
|
+
<button
|
|
199
|
+
type="button"
|
|
200
|
+
onclick={() => toggleSort(column)}
|
|
201
|
+
class="inline-flex items-center gap-1.5 rounded transition-colors duration-150 hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current
|
|
154
202
|
{sort?.key === column.key ? 'text-text' : ''}"
|
|
155
|
-
|
|
203
|
+
>
|
|
204
|
+
{column.label}
|
|
205
|
+
<span class="text-[9px] leading-none" aria-hidden="true">
|
|
206
|
+
{#if sort?.key === column.key}
|
|
207
|
+
{sort.direction === 'asc' ? '▲' : '▼'}
|
|
208
|
+
{:else}
|
|
209
|
+
⇅
|
|
210
|
+
{/if}
|
|
211
|
+
</span>
|
|
212
|
+
</button>
|
|
213
|
+
{:else}
|
|
156
214
|
{column.label}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
{:else}
|
|
161
|
-
⇅
|
|
162
|
-
{/if}
|
|
163
|
-
</span>
|
|
164
|
-
</button>
|
|
165
|
-
{:else}
|
|
166
|
-
{column.label}
|
|
167
|
-
{/if}
|
|
168
|
-
</th>
|
|
169
|
-
{/each}
|
|
170
|
-
</tr>
|
|
171
|
-
</thead>
|
|
172
|
-
<tbody>
|
|
173
|
-
{#if sorted.length === 0}
|
|
174
|
-
<tr>
|
|
175
|
-
<td
|
|
176
|
-
colspan={columns.length + (selectable ? 1 : 0)}
|
|
177
|
-
class="px-4 py-10 text-center text-sm text-text-muted"
|
|
178
|
-
>
|
|
179
|
-
{#if empty}{@render empty()}{:else}{t.current.noData}{/if}
|
|
180
|
-
</td>
|
|
215
|
+
{/if}
|
|
216
|
+
</th>
|
|
217
|
+
{/each}
|
|
181
218
|
</tr>
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
<tr
|
|
186
|
-
|
|
187
|
-
|
|
219
|
+
</thead>
|
|
220
|
+
<tbody>
|
|
221
|
+
{#if sorted.length === 0}
|
|
222
|
+
<tr>
|
|
223
|
+
<td
|
|
224
|
+
colspan={columns.length + (selectable ? 1 : 0)}
|
|
225
|
+
class="px-4 py-10 text-center text-sm text-text-muted"
|
|
226
|
+
>
|
|
227
|
+
{#if empty}{@render empty()}{:else}{t.current.noData}{/if}
|
|
228
|
+
</td>
|
|
229
|
+
</tr>
|
|
230
|
+
{:else}
|
|
231
|
+
{#each sorted as row, i (selectable ? keyOf(row) : i)}
|
|
232
|
+
{@const isSelected = selectable && selected.includes(keyOf(row))}
|
|
233
|
+
<tr
|
|
234
|
+
aria-selected={selectable ? isSelected : undefined}
|
|
235
|
+
class="border-b border-hairline last:border-b-0
|
|
188
236
|
{isSelected ? 'bg-brand-light' : striped && i % 2 === 1 ? 'bg-bg-alt' : ''}
|
|
189
237
|
{hoverable ? 'transition-colors duration-150 hover:bg-bg-alt' : ''}"
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
238
|
+
>
|
|
239
|
+
{#if selectable}
|
|
240
|
+
<td class={pad}>
|
|
241
|
+
<Checkbox
|
|
242
|
+
checked={isSelected}
|
|
243
|
+
{tone}
|
|
244
|
+
aria-label={t.current.selectRow}
|
|
245
|
+
onchange={() => toggleRow(row)}
|
|
246
|
+
/>
|
|
247
|
+
</td>
|
|
248
|
+
{/if}
|
|
249
|
+
{#each columns as column (column.key)}
|
|
250
|
+
<td
|
|
251
|
+
class="text-text-secondary {pad} {aligns[column.align ?? 'left']} {column.class ??
|
|
252
|
+
''}"
|
|
253
|
+
>
|
|
254
|
+
{#if cell}
|
|
255
|
+
{@render cell({ row, column, value: row[column.key] })}
|
|
256
|
+
{:else}
|
|
257
|
+
{row[column.key] ?? ''}
|
|
258
|
+
{/if}
|
|
259
|
+
</td>
|
|
260
|
+
{/each}
|
|
261
|
+
</tr>
|
|
262
|
+
{/each}
|
|
263
|
+
{/if}
|
|
264
|
+
</tbody>
|
|
265
|
+
</table>
|
|
266
|
+
</div>
|
|
267
|
+
{#if footer}
|
|
268
|
+
<div
|
|
269
|
+
class="flex flex-wrap items-center gap-3 border-t border-hairline px-4 py-3 font-sans text-[13px] text-text-secondary"
|
|
270
|
+
>
|
|
271
|
+
{@render footer()}
|
|
272
|
+
</div>
|
|
273
|
+
{/if}
|
|
218
274
|
</div>
|
|
@@ -29,6 +29,18 @@ declare function $$render<T extends Record<string, unknown>>(): {
|
|
|
29
29
|
compact?: boolean;
|
|
30
30
|
/** Header stays put while the body scrolls — pair with a `max-h-*` class. */
|
|
31
31
|
stickyHeader?: boolean;
|
|
32
|
+
/** Bar above the table — a title, a search field, a filter row. */
|
|
33
|
+
header?: Snippet;
|
|
34
|
+
/** Bar below the table — pagination, a total, a summary line. */
|
|
35
|
+
footer?: Snippet;
|
|
36
|
+
/**
|
|
37
|
+
* Shown in place of `header` while rows are ticked, after the count and the
|
|
38
|
+
* clear button. Passing it is what turns the bulk bar on.
|
|
39
|
+
*/
|
|
40
|
+
bulkActions?: Snippet<[{
|
|
41
|
+
selected: string[];
|
|
42
|
+
clear: () => void;
|
|
43
|
+
}]>;
|
|
32
44
|
/** Bindable. Set it to sort; leave `sortable` columns to drive it. */
|
|
33
45
|
sort?: TableSort | null;
|
|
34
46
|
/** Sort the rows here rather than refetching them server-side. Default true. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
2
3
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
4
|
import { edge } from './lift.js';
|
|
4
5
|
import {
|
|
@@ -9,10 +10,13 @@
|
|
|
9
10
|
toneText,
|
|
10
11
|
type Tone
|
|
11
12
|
} from '../../core/tones.js';
|
|
13
|
+
import { iconMd } from './icon.js';
|
|
12
14
|
|
|
13
15
|
export interface TabItem {
|
|
14
16
|
value: string;
|
|
15
17
|
label: string;
|
|
18
|
+
/** Leading icon, 16px. */
|
|
19
|
+
icon?: Snippet;
|
|
16
20
|
disabled?: boolean;
|
|
17
21
|
/** Small count/label rendered after the tab title. */
|
|
18
22
|
badge?: string | number;
|
|
@@ -90,6 +94,7 @@
|
|
|
90
94
|
onclick={() => select(item)}
|
|
91
95
|
class={tabClass(value === item.value)}
|
|
92
96
|
>
|
|
97
|
+
{#if item.icon}<span class={iconMd}>{@render item.icon()}</span>{/if}
|
|
93
98
|
{item.label}
|
|
94
99
|
{#if item.badge !== undefined}
|
|
95
100
|
<span
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
1
2
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
3
|
import { type Tone } from '../../core/tones.js';
|
|
3
4
|
export interface TabItem {
|
|
4
5
|
value: string;
|
|
5
6
|
label: string;
|
|
7
|
+
/** Leading icon, 16px. */
|
|
8
|
+
icon?: Snippet;
|
|
6
9
|
disabled?: boolean;
|
|
7
10
|
/** Small count/label rendered after the tab title. */
|
|
8
11
|
badge?: string | number;
|