@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
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
export interface FooterLink {
|
|
3
3
|
label: string;
|
|
4
4
|
href?: string;
|
|
5
|
+
/** Leading icon, 14px. */
|
|
6
|
+
icon?: Snippet;
|
|
5
7
|
external?: boolean;
|
|
6
8
|
}
|
|
7
9
|
|
|
@@ -14,6 +16,7 @@
|
|
|
14
16
|
<script lang="ts">
|
|
15
17
|
import type { Snippet } from 'svelte';
|
|
16
18
|
import { toneHoverText, type Tone } from '../../core/tones.js';
|
|
19
|
+
import { iconSm } from './icon.js';
|
|
17
20
|
|
|
18
21
|
interface Props {
|
|
19
22
|
columns?: FooterColumn[];
|
|
@@ -57,11 +60,12 @@
|
|
|
57
60
|
href={item.href}
|
|
58
61
|
target={item.external ? '_blank' : undefined}
|
|
59
62
|
rel={item.external ? 'noreferrer' : undefined}
|
|
60
|
-
class="font-sans text-[13px] text-text-muted transition-colors duration-150 ease-brand-out {toneHoverText[
|
|
63
|
+
class="inline-flex items-center gap-2 font-sans text-[13px] text-text-muted transition-colors duration-150 ease-brand-out {toneHoverText[
|
|
61
64
|
tone
|
|
62
65
|
]}"
|
|
63
66
|
>
|
|
64
|
-
{item.
|
|
67
|
+
{#if item.icon}<span class={iconSm}>{@render item.icon()}</span
|
|
68
|
+
>{/if}{item.label}
|
|
65
69
|
{#if item.external}<span aria-hidden="true" class="text-[0.85em]">↗</span
|
|
66
70
|
>{/if}
|
|
67
71
|
</a>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import { edge } from './lift.js';
|
|
5
|
+
import type { InputSize } from './Input.svelte';
|
|
6
|
+
|
|
7
|
+
interface Props extends HTMLAttributes<HTMLSpanElement> {
|
|
8
|
+
/** Match the Input it sits beside — the heights have to agree. */
|
|
9
|
+
size?: InputSize;
|
|
10
|
+
/** Paint it like a field instead of a fixed label. */
|
|
11
|
+
variant?: 'muted' | 'plain';
|
|
12
|
+
children: Snippet;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
size = 'md',
|
|
17
|
+
variant = 'muted',
|
|
18
|
+
class: className = '',
|
|
19
|
+
children,
|
|
20
|
+
...rest
|
|
21
|
+
}: Props = $props();
|
|
22
|
+
|
|
23
|
+
const sizes: Record<InputSize, string> = {
|
|
24
|
+
sm: 'h-8 px-2.5 text-[13px]',
|
|
25
|
+
md: 'h-10 px-3 text-sm',
|
|
26
|
+
lg: 'h-11 px-3 text-[15px]'
|
|
27
|
+
};
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<span
|
|
31
|
+
class="inline-flex shrink-0 items-center gap-1.5 rounded-xl border border-hairline-strong font-sans whitespace-nowrap text-text-secondary {edge}
|
|
32
|
+
{sizes[size]}
|
|
33
|
+
{variant === 'muted' ? 'bg-bg-alt' : 'bg-bg'} {className}"
|
|
34
|
+
{...rest}
|
|
35
|
+
>
|
|
36
|
+
{@render children()}
|
|
37
|
+
</span>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import type { InputSize } from './Input.svelte';
|
|
4
|
+
interface Props extends HTMLAttributes<HTMLSpanElement> {
|
|
5
|
+
/** Match the Input it sits beside — the heights have to agree. */
|
|
6
|
+
size?: InputSize;
|
|
7
|
+
/** Paint it like a field instead of a fixed label. */
|
|
8
|
+
variant?: 'muted' | 'plain';
|
|
9
|
+
children: Snippet;
|
|
10
|
+
}
|
|
11
|
+
declare const InputAddon: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type InputAddon = ReturnType<typeof InputAddon>;
|
|
13
|
+
export default InputAddon;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { HTMLAnchorAttributes } from 'svelte/elements';
|
|
4
4
|
import { focusRing, toneHoverText, toneRing, toneText, type Tone } from '../../core/tones.js';
|
|
5
|
+
import { iconSm } from './icon.js';
|
|
5
6
|
|
|
6
7
|
interface Props extends HTMLAnchorAttributes {
|
|
7
8
|
tone?: Tone;
|
|
@@ -11,6 +12,10 @@
|
|
|
11
12
|
external?: boolean;
|
|
12
13
|
/** Inherit the surrounding text colour instead of the tone. */
|
|
13
14
|
muted?: boolean;
|
|
15
|
+
/** Leading icon, 14px. */
|
|
16
|
+
icon?: Snippet;
|
|
17
|
+
/** Trailing icon, before the external mark. */
|
|
18
|
+
iconEnd?: Snippet;
|
|
14
19
|
children: Snippet;
|
|
15
20
|
}
|
|
16
21
|
|
|
@@ -19,6 +24,8 @@
|
|
|
19
24
|
underline = 'hover',
|
|
20
25
|
external = false,
|
|
21
26
|
muted = false,
|
|
27
|
+
icon,
|
|
28
|
+
iconEnd,
|
|
22
29
|
class: className = '',
|
|
23
30
|
children,
|
|
24
31
|
...rest
|
|
@@ -39,6 +46,8 @@
|
|
|
39
46
|
{underlines[underline]} {focusRing} {toneRing[tone]} {className}"
|
|
40
47
|
{...rest}
|
|
41
48
|
>
|
|
49
|
+
{#if icon}<span class={iconSm}>{@render icon()}</span>{/if}
|
|
42
50
|
{@render children()}
|
|
51
|
+
{#if iconEnd}<span class={iconSm}>{@render iconEnd()}</span>{/if}
|
|
43
52
|
{#if external}<span aria-hidden="true" class="text-[0.85em]">↗</span>{/if}
|
|
44
53
|
</a>
|
|
@@ -9,6 +9,10 @@ interface Props extends HTMLAnchorAttributes {
|
|
|
9
9
|
external?: boolean;
|
|
10
10
|
/** Inherit the surrounding text colour instead of the tone. */
|
|
11
11
|
muted?: boolean;
|
|
12
|
+
/** Leading icon, 14px. */
|
|
13
|
+
icon?: Snippet;
|
|
14
|
+
/** Trailing icon, before the external mark. */
|
|
15
|
+
iconEnd?: Snippet;
|
|
12
16
|
children: Snippet;
|
|
13
17
|
}
|
|
14
18
|
declare const Link: import("svelte").Component<Props, {}, "">;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
4
4
|
import { toneSoftHover, toneText, type Tone } from '../../core/tones.js';
|
|
5
|
+
import { iconMd } from './icon.js';
|
|
5
6
|
|
|
6
7
|
interface Props extends HTMLButtonAttributes {
|
|
7
8
|
/** Renders an `<a>` instead of a `<button>`. */
|
|
@@ -55,7 +56,7 @@
|
|
|
55
56
|
/>
|
|
56
57
|
</svg>
|
|
57
58
|
{:else if icon}
|
|
58
|
-
<span class=
|
|
59
|
+
<span class={iconMd}>{@render icon()}</span>
|
|
59
60
|
{/if}
|
|
60
61
|
<span class="min-w-0 flex-1 truncate">{@render children()}</span>
|
|
61
62
|
{#if shortcut}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
|
+
import { iconLg } from './icon.js';
|
|
3
4
|
import type { HTMLDialogAttributes } from 'svelte/elements';
|
|
4
5
|
import { useLocale } from '../../core/locale.svelte.js';
|
|
5
6
|
import { isBackdropClick, syncDialog } from '../../core/dialog.svelte.js';
|
|
@@ -12,6 +13,8 @@
|
|
|
12
13
|
open?: boolean;
|
|
13
14
|
title?: string;
|
|
14
15
|
description?: string;
|
|
16
|
+
/** Leading icon beside the title. */
|
|
17
|
+
icon?: Snippet;
|
|
15
18
|
size?: ModalSize;
|
|
16
19
|
/** Clicking the backdrop or pressing Escape closes the dialog. */
|
|
17
20
|
dismissible?: boolean;
|
|
@@ -27,6 +30,7 @@
|
|
|
27
30
|
open = $bindable(false),
|
|
28
31
|
title,
|
|
29
32
|
description,
|
|
33
|
+
icon,
|
|
30
34
|
size = 'md',
|
|
31
35
|
dismissible = true,
|
|
32
36
|
showClose,
|
|
@@ -76,6 +80,7 @@
|
|
|
76
80
|
<div class="flex flex-col">
|
|
77
81
|
{#if title || closable}
|
|
78
82
|
<div class="flex items-start gap-4 px-6 pt-5 pb-3">
|
|
83
|
+
{#if icon}<span class={iconLg}>{@render icon()}</span>{/if}
|
|
79
84
|
<div class="flex min-w-0 flex-1 flex-col gap-1">
|
|
80
85
|
{#if title}
|
|
81
86
|
<h2 class="font-heading text-base font-semibold text-text">{title}</h2>
|
|
@@ -6,6 +6,8 @@ interface Props extends HTMLDialogAttributes {
|
|
|
6
6
|
open?: boolean;
|
|
7
7
|
title?: string;
|
|
8
8
|
description?: string;
|
|
9
|
+
/** Leading icon beside the title. */
|
|
10
|
+
icon?: Snippet;
|
|
9
11
|
size?: ModalSize;
|
|
10
12
|
/** Clicking the backdrop or pressing Escape closes the dialog. */
|
|
11
13
|
dismissible?: boolean;
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
<script module lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
2
4
|
export interface MultiSelectOption {
|
|
3
5
|
value: string;
|
|
4
6
|
label: string;
|
|
7
|
+
/** Leading icon, 16px. */
|
|
8
|
+
icon?: Snippet;
|
|
5
9
|
description?: string;
|
|
6
10
|
disabled?: boolean;
|
|
7
11
|
group?: string;
|
|
@@ -13,6 +17,7 @@
|
|
|
13
17
|
import { clickOutside, portal } from '../../core/actions/dismissable.js';
|
|
14
18
|
import { ListCursor, groupItems, matchQuery, revealIndex } from '../../core/list.svelte.js';
|
|
15
19
|
import { useLocale } from '../../core/locale.svelte.js';
|
|
20
|
+
import { iconMd, iconSm } from './icon.js';
|
|
16
21
|
import { toneFocusWithinBorder, toneSoft, toneText, type Tone } from '../../core/tones.js';
|
|
17
22
|
import { edge, soft } from './lift.js';
|
|
18
23
|
|
|
@@ -154,7 +159,7 @@
|
|
|
154
159
|
tone
|
|
155
160
|
]}"
|
|
156
161
|
>
|
|
157
|
-
{option.label}
|
|
162
|
+
{#if option.icon}<span class={iconSm}>{@render option.icon()}</span>{/if}{option.label}
|
|
158
163
|
<button
|
|
159
164
|
type="button"
|
|
160
165
|
onclick={() => remove(option)}
|
|
@@ -274,6 +279,7 @@
|
|
|
274
279
|
</svg>
|
|
275
280
|
{/if}
|
|
276
281
|
</span>
|
|
282
|
+
{#if option.icon}<span class={iconMd}>{@render option.icon()}</span>{/if}
|
|
277
283
|
<span class="flex min-w-0 flex-1 flex-col gap-0.5">
|
|
278
284
|
<span class="truncate">{option.label}</span>
|
|
279
285
|
{#if option.description}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
export interface NavItem {
|
|
3
3
|
label: string;
|
|
4
4
|
href?: string;
|
|
5
|
+
/** Leading icon, 16px. */
|
|
6
|
+
icon?: Snippet;
|
|
5
7
|
/** Marks the current page — also sets `aria-current`. */
|
|
6
8
|
active?: boolean;
|
|
7
9
|
disabled?: boolean;
|
|
@@ -15,6 +17,7 @@
|
|
|
15
17
|
<script lang="ts">
|
|
16
18
|
import type { Snippet } from 'svelte';
|
|
17
19
|
import { useLocale } from '../../core/locale.svelte.js';
|
|
20
|
+
import { iconMd } from './icon.js';
|
|
18
21
|
import { focusRing, toneRing, toneSoft, toneText, type Tone } from '../../core/tones.js';
|
|
19
22
|
import Drawer from './Drawer.svelte';
|
|
20
23
|
import Dropdown from './Dropdown.svelte';
|
|
@@ -84,6 +87,7 @@
|
|
|
84
87
|
? `${toneSoft[tone]}`
|
|
85
88
|
: 'text-text-secondary hover:bg-bg-inset hover:text-text'}"
|
|
86
89
|
>
|
|
90
|
+
{#if item.icon}<span class={iconMd}>{@render item.icon()}</span>{/if}
|
|
87
91
|
{item.label}
|
|
88
92
|
<svg class="size-3" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
|
89
93
|
<path
|
|
@@ -96,7 +100,12 @@
|
|
|
96
100
|
</button>
|
|
97
101
|
{/snippet}
|
|
98
102
|
{#each item.items as child (child.label)}
|
|
99
|
-
<MenuItem
|
|
103
|
+
<MenuItem
|
|
104
|
+
href={child.href}
|
|
105
|
+
disabled={child.disabled}
|
|
106
|
+
selected={child.active}
|
|
107
|
+
icon={child.icon}
|
|
108
|
+
>
|
|
100
109
|
{child.label}
|
|
101
110
|
</MenuItem>
|
|
102
111
|
{/each}
|
|
@@ -111,6 +120,7 @@
|
|
|
111
120
|
? 'pointer-events-none opacity-50'
|
|
112
121
|
: ''}"
|
|
113
122
|
>
|
|
123
|
+
{#if item.icon}<span class={iconMd}>{@render item.icon()}</span>{/if}
|
|
114
124
|
{item.label}
|
|
115
125
|
{#if item.badge !== undefined}
|
|
116
126
|
<span class="text-[11px] text-text-muted tabular-nums">{item.badge}</span>
|
|
@@ -163,7 +173,10 @@
|
|
|
163
173
|
? 'pointer-events-none opacity-50'
|
|
164
174
|
: ''}"
|
|
165
175
|
>
|
|
166
|
-
|
|
176
|
+
<span class="flex min-w-0 items-center gap-2.5">
|
|
177
|
+
{#if item.icon}<span class={iconMd}>{@render item.icon()}</span>{/if}
|
|
178
|
+
{item.label}
|
|
179
|
+
</span>
|
|
167
180
|
{#if item.badge !== undefined}
|
|
168
181
|
<span class="text-[11px] text-text-muted tabular-nums">{item.badge}</span>
|
|
169
182
|
{/if}
|
|
@@ -175,11 +188,12 @@
|
|
|
175
188
|
<a
|
|
176
189
|
href={child.href}
|
|
177
190
|
onclick={() => (menuOpen = false)}
|
|
178
|
-
class="
|
|
191
|
+
class="flex items-center gap-2.5 rounded-xl px-3 py-2 font-sans text-[13px] text-text-muted hover:bg-bg-inset {toneText[
|
|
179
192
|
tone
|
|
180
193
|
]}"
|
|
181
194
|
>
|
|
182
|
-
{child.
|
|
195
|
+
{#if child.icon}<span class={iconMd}>{@render child.icon()}</span
|
|
196
|
+
>{/if}{child.label}
|
|
183
197
|
</a>
|
|
184
198
|
</li>
|
|
185
199
|
{/each}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { useLocale } from '../../core/locale.svelte.js';
|
|
3
|
+
import { caretIndex, clearFrom, fillFrom, sanitisePin, type PinMode } from '../../core/pin.js';
|
|
4
|
+
import { toneFocusBorder, type Tone } from '../../core/tones.js';
|
|
5
|
+
import { edge } from './lift.js';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
/** Bindable code. Shorter than `length` means still incomplete. */
|
|
9
|
+
value?: string;
|
|
10
|
+
/** How many boxes. */
|
|
11
|
+
length?: number;
|
|
12
|
+
/** What a box accepts. */
|
|
13
|
+
mode?: PinMode;
|
|
14
|
+
/** Render the characters as dots. */
|
|
15
|
+
mask?: boolean;
|
|
16
|
+
/** Draw a gap after this many boxes — `3` gives `123 456`. */
|
|
17
|
+
groupAfter?: number;
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
invalid?: boolean;
|
|
20
|
+
tone?: Tone;
|
|
21
|
+
/** `name` for a plain form submit — one hidden field holding the code. */
|
|
22
|
+
name?: string;
|
|
23
|
+
/** Fires once the last box is filled. */
|
|
24
|
+
oncomplete?: (value: string) => void;
|
|
25
|
+
class?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let {
|
|
29
|
+
value = $bindable(''),
|
|
30
|
+
length = 6,
|
|
31
|
+
mode = 'numeric',
|
|
32
|
+
mask = false,
|
|
33
|
+
groupAfter = 0,
|
|
34
|
+
disabled = false,
|
|
35
|
+
invalid = false,
|
|
36
|
+
tone = 'brand',
|
|
37
|
+
name,
|
|
38
|
+
oncomplete,
|
|
39
|
+
class: className = ''
|
|
40
|
+
}: Props = $props();
|
|
41
|
+
|
|
42
|
+
const t = useLocale();
|
|
43
|
+
|
|
44
|
+
let boxes: HTMLInputElement[] = $state([]);
|
|
45
|
+
|
|
46
|
+
const chars = $derived(Array.from({ length }, (_, i) => value[i] ?? ''));
|
|
47
|
+
|
|
48
|
+
function focusAt(index: number) {
|
|
49
|
+
boxes[Math.max(0, Math.min(length - 1, index))]?.focus();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The DOM is written back on every keystroke, not only when the value
|
|
54
|
+
* changed: a refused character leaves the box holding text the state never
|
|
55
|
+
* accepted, and nothing else would clear it.
|
|
56
|
+
*/
|
|
57
|
+
function sync(box: HTMLInputElement, index: number) {
|
|
58
|
+
box.value = value[index] ?? '';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function commit(next: string, from: number) {
|
|
62
|
+
const was = value;
|
|
63
|
+
value = next;
|
|
64
|
+
if (value.length === length && was.length < length) oncomplete?.(value);
|
|
65
|
+
// Land on the first empty box, or the one after whatever was just typed.
|
|
66
|
+
focusAt(Math.min(Math.max(from, caretIndex(value, length)), length - 1));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function onInput(event: Event & { currentTarget: HTMLInputElement }, index: number) {
|
|
70
|
+
const typed = sanitisePin(event.currentTarget.value, mode);
|
|
71
|
+
if (!typed) {
|
|
72
|
+
sync(event.currentTarget, index);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const next = fillFrom(value, index, typed, length, mode);
|
|
76
|
+
commit(next, index + typed.length);
|
|
77
|
+
for (const [at, box] of boxes.entries()) sync(box, at);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function onKeydown(event: KeyboardEvent, index: number) {
|
|
81
|
+
if (event.key === 'Backspace') {
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
// An empty box deletes the one before it, the way a caret would.
|
|
84
|
+
const target = value[index] ? index : index - 1;
|
|
85
|
+
if (target < 0) return;
|
|
86
|
+
value = clearFrom(value, target);
|
|
87
|
+
for (const [at, box] of boxes.entries()) sync(box, at);
|
|
88
|
+
focusAt(target);
|
|
89
|
+
} else if (event.key === 'ArrowLeft') {
|
|
90
|
+
event.preventDefault();
|
|
91
|
+
focusAt(index - 1);
|
|
92
|
+
} else if (event.key === 'ArrowRight') {
|
|
93
|
+
event.preventDefault();
|
|
94
|
+
focusAt(index + 1);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function onPaste(event: ClipboardEvent, index: number) {
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
const text = event.clipboardData?.getData('text') ?? '';
|
|
101
|
+
const clean = sanitisePin(text, mode);
|
|
102
|
+
if (!clean) return;
|
|
103
|
+
commit(fillFrom(value, index, clean, length, mode), index + clean.length);
|
|
104
|
+
for (const [at, box] of boxes.entries()) sync(box, at);
|
|
105
|
+
}
|
|
106
|
+
</script>
|
|
107
|
+
|
|
108
|
+
<div class="flex items-center gap-2 {className}">
|
|
109
|
+
{#each chars as char, index (index)}
|
|
110
|
+
{#if groupAfter && index > 0 && index % groupAfter === 0}
|
|
111
|
+
<span class="h-px w-2 bg-hairline-strong" aria-hidden="true"></span>
|
|
112
|
+
{/if}
|
|
113
|
+
<input
|
|
114
|
+
bind:this={boxes[index]}
|
|
115
|
+
value={char}
|
|
116
|
+
{disabled}
|
|
117
|
+
type={mask ? 'password' : 'text'}
|
|
118
|
+
inputmode={mode === 'numeric' ? 'numeric' : 'text'}
|
|
119
|
+
autocomplete={index === 0 ? 'one-time-code' : 'off'}
|
|
120
|
+
autocapitalize="characters"
|
|
121
|
+
spellcheck="false"
|
|
122
|
+
maxlength={length}
|
|
123
|
+
aria-label="{t.current.digit} {index + 1}"
|
|
124
|
+
aria-invalid={invalid ? 'true' : undefined}
|
|
125
|
+
oninput={(event) => onInput(event, index)}
|
|
126
|
+
onkeydown={(event) => onKeydown(event, index)}
|
|
127
|
+
onpaste={(event) => onPaste(event, index)}
|
|
128
|
+
onfocus={(event) => event.currentTarget.select()}
|
|
129
|
+
class="size-11 rounded-xl border bg-bg text-center font-sans text-base font-medium text-text tabular-nums {edge} transition-colors duration-150 ease-brand-out focus:outline-none
|
|
130
|
+
{invalid ? 'border-danger' : `border-hairline-strong ${toneFocusBorder[tone]}`}
|
|
131
|
+
{disabled ? 'pointer-events-none opacity-50' : ''}"
|
|
132
|
+
/>
|
|
133
|
+
{/each}
|
|
134
|
+
|
|
135
|
+
{#if name}
|
|
136
|
+
<input type="hidden" {name} {value} />
|
|
137
|
+
{/if}
|
|
138
|
+
</div>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type PinMode } from '../../core/pin.js';
|
|
2
|
+
import { type Tone } from '../../core/tones.js';
|
|
3
|
+
interface Props {
|
|
4
|
+
/** Bindable code. Shorter than `length` means still incomplete. */
|
|
5
|
+
value?: string;
|
|
6
|
+
/** How many boxes. */
|
|
7
|
+
length?: number;
|
|
8
|
+
/** What a box accepts. */
|
|
9
|
+
mode?: PinMode;
|
|
10
|
+
/** Render the characters as dots. */
|
|
11
|
+
mask?: boolean;
|
|
12
|
+
/** Draw a gap after this many boxes — `3` gives `123 456`. */
|
|
13
|
+
groupAfter?: number;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
invalid?: boolean;
|
|
16
|
+
tone?: Tone;
|
|
17
|
+
/** `name` for a plain form submit — one hidden field holding the code. */
|
|
18
|
+
name?: string;
|
|
19
|
+
/** Fires once the last box is filled. */
|
|
20
|
+
oncomplete?: (value: string) => void;
|
|
21
|
+
class?: string;
|
|
22
|
+
}
|
|
23
|
+
declare const PinInput: import("svelte").Component<Props, {}, "value">;
|
|
24
|
+
type PinInput = ReturnType<typeof PinInput>;
|
|
25
|
+
export default PinInput;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
4
|
import { toneBorder, toneSoft, toneSolid, type Tone } from '../../core/tones.js';
|
|
5
|
+
import { iconMd } from './icon.js';
|
|
5
6
|
import { edge, soft } from './lift.js';
|
|
6
7
|
|
|
7
8
|
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
@@ -13,6 +14,8 @@
|
|
|
13
14
|
description?: string;
|
|
14
15
|
/** One line each. Rendered with a tick. */
|
|
15
16
|
features?: string[];
|
|
17
|
+
/** Replaces the tick beside every feature. */
|
|
18
|
+
featureIcon?: Snippet;
|
|
16
19
|
/** Pill in the top corner, e.g. "Most popular". */
|
|
17
20
|
badge?: string;
|
|
18
21
|
/** Ring, lift and a tinted header — for the plan you want chosen. */
|
|
@@ -30,6 +33,7 @@
|
|
|
30
33
|
period,
|
|
31
34
|
description,
|
|
32
35
|
features = [],
|
|
36
|
+
featureIcon,
|
|
33
37
|
badge,
|
|
34
38
|
featured = false,
|
|
35
39
|
tone = 'brand',
|
|
@@ -78,21 +82,25 @@
|
|
|
78
82
|
<ul class="flex flex-1 flex-col gap-3 border-t border-hairline p-6">
|
|
79
83
|
{#each features as feature (feature)}
|
|
80
84
|
<li class="flex items-start gap-3 font-sans text-sm text-text-secondary">
|
|
81
|
-
|
|
82
|
-
class="mt-0.5
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
85
|
+
{#if featureIcon}
|
|
86
|
+
<span class="mt-0.5 {iconMd}">{@render featureIcon()}</span>
|
|
87
|
+
{:else}
|
|
88
|
+
<span
|
|
89
|
+
class="mt-0.5 flex size-4 shrink-0 items-center justify-center rounded-full {toneSoft[
|
|
90
|
+
tone
|
|
91
|
+
]}"
|
|
92
|
+
>
|
|
93
|
+
<svg class="size-2.5" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
|
94
|
+
<path
|
|
95
|
+
d="m3 8 3.2 3.2L13 4.4"
|
|
96
|
+
stroke="currentColor"
|
|
97
|
+
stroke-width="2.4"
|
|
98
|
+
stroke-linecap="round"
|
|
99
|
+
stroke-linejoin="round"
|
|
100
|
+
/>
|
|
101
|
+
</svg>
|
|
102
|
+
</span>
|
|
103
|
+
{/if}
|
|
96
104
|
<span class="leading-snug">{feature}</span>
|
|
97
105
|
</li>
|
|
98
106
|
{/each}
|
|
@@ -10,6 +10,8 @@ interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
|
10
10
|
description?: string;
|
|
11
11
|
/** One line each. Rendered with a tick. */
|
|
12
12
|
features?: string[];
|
|
13
|
+
/** Replaces the tick beside every feature. */
|
|
14
|
+
featureIcon?: Snippet;
|
|
13
15
|
/** Pill in the top corner, e.g. "Most popular". */
|
|
14
16
|
badge?: string;
|
|
15
17
|
/** Ring, lift and a tinted header — for the plan you want chosen. */
|
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
import type { HTMLFieldsetAttributes } from 'svelte/elements';
|
|
4
4
|
import Radio from './Radio.svelte';
|
|
5
5
|
import { toneBorder, toneSurface, type Tone } from '../../core/tones.js';
|
|
6
|
+
import { iconLg } from './icon.js';
|
|
6
7
|
import { edge } from './lift.js';
|
|
7
8
|
|
|
8
9
|
export interface RadioOption {
|
|
9
10
|
value: string;
|
|
10
11
|
label: string;
|
|
12
|
+
/** Leading icon, 20px. Drawn in `boxed` mode only. */
|
|
13
|
+
icon?: Snippet;
|
|
11
14
|
description?: string;
|
|
12
15
|
disabled?: boolean;
|
|
13
16
|
}
|
|
@@ -63,6 +66,9 @@
|
|
|
63
66
|
: 'border-hairline bg-bg hover:border-hairline-strong'}
|
|
64
67
|
{option.disabled ? 'pointer-events-none opacity-50' : ''}"
|
|
65
68
|
>
|
|
69
|
+
{#if option.icon}
|
|
70
|
+
<span class={iconLg}>{@render option.icon()}</span>
|
|
71
|
+
{/if}
|
|
66
72
|
<Radio
|
|
67
73
|
bind:group={value}
|
|
68
74
|
value={option.value}
|