svseeds 0.4.0 → 0.4.3
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/_svseeds/Accordion.svelte +31 -14
- package/_svseeds/Accordion.svelte.d.ts +20 -4
- package/_svseeds/DarkToggle.svelte +81 -41
- package/_svseeds/DarkToggle.svelte.d.ts +23 -4
- package/_svseeds/TagsInput.svelte +58 -28
- package/_svseeds/TagsInput.svelte.d.ts +34 -6
- package/_svseeds/TagsInputField.svelte +66 -33
- package/_svseeds/TagsInputField.svelte.d.ts +36 -4
- package/_svseeds/ToggleGroupField.svelte +59 -32
- package/_svseeds/ToggleGroupField.svelte.d.ts +30 -4
- package/_svseeds/_Badge.svelte +33 -17
- package/_svseeds/_Badge.svelte.d.ts +17 -2
- package/_svseeds/_Button.svelte +39 -20
- package/_svseeds/_Button.svelte.d.ts +20 -2
- package/_svseeds/_CheckField.svelte +54 -30
- package/_svseeds/_CheckField.svelte.d.ts +24 -2
- package/_svseeds/_ColorPicker.svelte +38 -17
- package/_svseeds/_ColorPicker.svelte.d.ts +21 -2
- package/_svseeds/_ComboBox.svelte +38 -20
- package/_svseeds/_ComboBox.svelte.d.ts +17 -2
- package/_svseeds/_ContextMenu.svelte +26 -12
- package/_svseeds/_ContextMenu.svelte.d.ts +15 -2
- package/_svseeds/_Disclosure.svelte +37 -21
- package/_svseeds/_Disclosure.svelte.d.ts +19 -3
- package/_svseeds/_HotkeyCapture.svelte +28 -13
- package/_svseeds/_HotkeyCapture.svelte.d.ts +16 -2
- package/_svseeds/_Modal.svelte +32 -15
- package/_svseeds/_Modal.svelte.d.ts +18 -3
- package/_svseeds/_ProgressTracker.svelte +31 -16
- package/_svseeds/_ProgressTracker.svelte.d.ts +16 -2
- package/_svseeds/_SelectField.svelte +53 -29
- package/_svseeds/_SelectField.svelte.d.ts +25 -2
- package/_svseeds/_Slider.svelte +43 -22
- package/_svseeds/_Slider.svelte.d.ts +22 -2
- package/_svseeds/_Sortable.svelte +57 -19
- package/_svseeds/_Sortable.svelte.d.ts +39 -2
- package/_svseeds/_Tabs.svelte +31 -17
- package/_svseeds/_Tabs.svelte.d.ts +16 -3
- package/_svseeds/_TextField.svelte +56 -31
- package/_svseeds/_TextField.svelte.d.ts +26 -2
- package/_svseeds/_Toggle.svelte +45 -25
- package/_svseeds/_Toggle.svelte.d.ts +21 -2
- package/_svseeds/_ToggleGroup.svelte +32 -15
- package/_svseeds/_ToggleGroup.svelte.d.ts +18 -2
- package/_svseeds/_Tooltip.svelte +32 -13
- package/_svseeds/_Tooltip.svelte.d.ts +20 -2
- package/_svseeds/core.d.ts +186 -8
- package/_svseeds/core.js +1 -1
- package/index.d.ts +24 -24
- package/index.js +3 -3
- package/package.json +1 -1
|
@@ -1,15 +1,32 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
default value: `<value>`
|
|
4
|
+
```ts
|
|
5
|
+
interface AccordionProps {
|
|
6
|
+
labels?: string[];
|
|
7
|
+
current?: number; // bindable <-1>
|
|
8
|
+
status?: string; // bindable <STATE.NEUTRAL>
|
|
9
|
+
style?: SVSStyle;
|
|
10
|
+
deps?: AccordionDeps;
|
|
11
|
+
[key: string]: unknown | Snippet;
|
|
12
|
+
}
|
|
13
|
+
interface AccordionDeps {
|
|
14
|
+
svsDisclosure?: Omit<DisclosureProps, DisclosureReqdProps | DisclosureBindProps>;
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
-->
|
|
1
18
|
<script module lang="ts">
|
|
2
|
-
export
|
|
3
|
-
labels?: string[]
|
|
4
|
-
current?: number
|
|
5
|
-
status?: string
|
|
6
|
-
style?: SVSStyle
|
|
7
|
-
deps?: AccordionDeps
|
|
8
|
-
[key: string]: unknown | Snippet
|
|
9
|
-
}
|
|
10
|
-
export
|
|
11
|
-
svsDisclosure?: Omit<DisclosureProps, DisclosureReqdProps | DisclosureBindProps
|
|
12
|
-
}
|
|
19
|
+
export interface AccordionProps {
|
|
20
|
+
labels?: string[];
|
|
21
|
+
current?: number; // bindable <-1>
|
|
22
|
+
status?: string; // bindable <STATE.NEUTRAL>
|
|
23
|
+
style?: SVSStyle;
|
|
24
|
+
deps?: AccordionDeps;
|
|
25
|
+
[key: string]: unknown | Snippet;
|
|
26
|
+
}
|
|
27
|
+
export interface AccordionDeps {
|
|
28
|
+
svsDisclosure?: Omit<DisclosureProps, DisclosureReqdProps | DisclosureBindProps>;
|
|
29
|
+
}
|
|
13
30
|
export type AccordionReqdProps = never;
|
|
14
31
|
export type AccordionBindProps = "current" | "status";
|
|
15
32
|
|
|
@@ -28,7 +45,7 @@
|
|
|
28
45
|
}
|
|
29
46
|
|
|
30
47
|
import { type Snippet, untrack } from "svelte";
|
|
31
|
-
import { type SVSStyle, STATE,
|
|
48
|
+
import { type SVSStyle, STATE, PARTS, elemId, fnClass, omit } from "./core";
|
|
32
49
|
import Disclosure, { type DisclosureProps, type DisclosureReqdProps, type DisclosureBindProps } from "./_Disclosure.svelte";
|
|
33
50
|
</script>
|
|
34
51
|
|
|
@@ -36,7 +53,7 @@
|
|
|
36
53
|
let { labels = [], current = $bindable(-1), status = $bindable(""), style, deps, ...rest }: AccordionProps = $props();
|
|
37
54
|
|
|
38
55
|
// *** Initialize *** //
|
|
39
|
-
if (!status) status = STATE.
|
|
56
|
+
if (!status) status = STATE.NEUTRAL;
|
|
40
57
|
const cls = fnClass(preset, style);
|
|
41
58
|
const isStrLabel = labels.length > 0;
|
|
42
59
|
const lbls = toNamedId(isStrLabel ? labels : getSnippetNames(roleLabel, rest));
|
|
@@ -84,7 +101,7 @@
|
|
|
84
101
|
<!---------------------------------------->
|
|
85
102
|
|
|
86
103
|
{#if isValidAccordion}
|
|
87
|
-
<div class={cls(
|
|
104
|
+
<div class={cls(PARTS.WHOLE, status)} role="group">
|
|
88
105
|
{#each lbls as { id, name }, i (id)}
|
|
89
106
|
{@const ontoggle = exclusiveToggle(i)}
|
|
90
107
|
<Disclosure bind:open={opens[i]} bind:element={elems[i]} label={isStrLabel ? name : (rest[name] as Snippet)} attributes={{...deps?.svsDisclosure?.attributes, ontoggle}} {...svsDisclosure}>
|
|
@@ -1,19 +1,35 @@
|
|
|
1
|
-
export
|
|
1
|
+
export interface AccordionProps {
|
|
2
2
|
labels?: string[];
|
|
3
3
|
current?: number;
|
|
4
4
|
status?: string;
|
|
5
5
|
style?: SVSStyle;
|
|
6
6
|
deps?: AccordionDeps;
|
|
7
7
|
[key: string]: unknown | Snippet;
|
|
8
|
-
}
|
|
9
|
-
export
|
|
8
|
+
}
|
|
9
|
+
export interface AccordionDeps {
|
|
10
10
|
svsDisclosure?: Omit<DisclosureProps, DisclosureReqdProps | DisclosureBindProps>;
|
|
11
|
-
}
|
|
11
|
+
}
|
|
12
12
|
export type AccordionReqdProps = never;
|
|
13
13
|
export type AccordionBindProps = "current" | "status";
|
|
14
14
|
import { type Snippet } from "svelte";
|
|
15
15
|
import { type SVSStyle } from "./core";
|
|
16
16
|
import { type DisclosureProps, type DisclosureReqdProps, type DisclosureBindProps } from "./_Disclosure.svelte";
|
|
17
|
+
/**
|
|
18
|
+
* default value: `<value>`
|
|
19
|
+
* ```ts
|
|
20
|
+
* interface AccordionProps {
|
|
21
|
+
* labels?: string[];
|
|
22
|
+
* current?: number; // bindable <-1>
|
|
23
|
+
* status?: string; // bindable <STATE.NEUTRAL>
|
|
24
|
+
* style?: SVSStyle;
|
|
25
|
+
* deps?: AccordionDeps;
|
|
26
|
+
* [key: string]: unknown | Snippet;
|
|
27
|
+
* }
|
|
28
|
+
* interface AccordionDeps {
|
|
29
|
+
* svsDisclosure?: Omit<DisclosureProps, DisclosureReqdProps | DisclosureBindProps>;
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
17
33
|
declare const Accordion: import("svelte").Component<AccordionProps, {}, "status" | "current">;
|
|
18
34
|
type Accordion = ReturnType<typeof Accordion>;
|
|
19
35
|
export default Accordion;
|
|
@@ -1,53 +1,81 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
default value: `<value>`
|
|
4
|
+
```ts
|
|
5
|
+
interface DarkToggleProps {
|
|
6
|
+
dark?: boolean; // bindable <(prefers-color-scheme)>
|
|
7
|
+
status?: string; // bindable <STATE.NEUTRAL>
|
|
8
|
+
element?: HTMLButtonElement; // bindable
|
|
9
|
+
deps?: DarkToggleDeps;
|
|
10
|
+
}
|
|
11
|
+
export interface DarkToggleDeps {
|
|
12
|
+
svsToggle?: Omit<ToggleProps, ToggleReqdProps | ToggleBindProps>;
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
-->
|
|
1
16
|
<script module lang="ts">
|
|
2
|
-
export
|
|
3
|
-
dark?: boolean
|
|
4
|
-
status?: string
|
|
5
|
-
element?: HTMLButtonElement
|
|
6
|
-
deps?: DarkToggleDeps
|
|
7
|
-
}
|
|
8
|
-
export
|
|
9
|
-
svsToggle?: Omit<ToggleProps, ToggleReqdProps | ToggleBindProps
|
|
10
|
-
}
|
|
17
|
+
export interface DarkToggleProps {
|
|
18
|
+
dark?: boolean; // bindable <(prefers-color-scheme)>
|
|
19
|
+
status?: string; // bindable <STATE.NEUTRAL>
|
|
20
|
+
element?: HTMLButtonElement; // bindable
|
|
21
|
+
deps?: DarkToggleDeps;
|
|
22
|
+
}
|
|
23
|
+
export interface DarkToggleDeps {
|
|
24
|
+
svsToggle?: Omit<ToggleProps, ToggleReqdProps | ToggleBindProps>;
|
|
25
|
+
}
|
|
11
26
|
export type DarkToggleReqdProps = never;
|
|
12
27
|
export type DarkToggleBindProps = "dark" | "status" | "element";
|
|
13
28
|
|
|
14
|
-
const
|
|
29
|
+
export const THEME = { LIGHT: "light", DARK: "dark" } as const;
|
|
30
|
+
export function setThemeToRoot(theme?: string) {
|
|
31
|
+
if (typeof window === "undefined") return;
|
|
32
|
+
if (!theme) theme = Theme.getPreferTheme();
|
|
33
|
+
window.document.documentElement.classList.add(theme);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const ariaLabel = "Toggle theme color";
|
|
15
37
|
const preset = "svs-dark-toggle";
|
|
16
38
|
|
|
17
39
|
class Theme {
|
|
18
|
-
static #
|
|
19
|
-
static #LIGHT = "light";
|
|
40
|
+
static #MEDIA_PREFER_DARK = "(prefers-color-scheme: dark)";
|
|
20
41
|
static #REGEX = /\(prefers-color-scheme:\s*(light|dark)\s*\)/i;
|
|
21
42
|
static #SELECTOR = ":root";
|
|
22
43
|
#props: Map<string, Record<string, string>> = new Map();
|
|
23
|
-
#
|
|
44
|
+
#theme;
|
|
24
45
|
set dark(bool: boolean) {
|
|
25
46
|
this.#switch(bool);
|
|
26
47
|
}
|
|
27
48
|
get dark(): boolean {
|
|
28
|
-
return this.#
|
|
49
|
+
return this.#theme === THEME.DARK;
|
|
29
50
|
}
|
|
30
51
|
|
|
31
52
|
constructor() {
|
|
32
|
-
this.#
|
|
53
|
+
this.#theme = Theme.getPreferTheme();
|
|
33
54
|
this.#initProps();
|
|
34
55
|
this.#setColorScheme();
|
|
56
|
+
setThemeToRoot(this.#theme);
|
|
35
57
|
}
|
|
36
58
|
#switch(dark: boolean) {
|
|
37
|
-
const theme =
|
|
59
|
+
const theme = Theme.#getThemeString(dark);
|
|
60
|
+
if (this.#theme === theme) return;
|
|
38
61
|
if (!this.#props.has(theme)) return;
|
|
39
|
-
this.#
|
|
40
|
-
this.#
|
|
62
|
+
this.#apply(theme, this.#theme);
|
|
63
|
+
this.#theme = theme;
|
|
41
64
|
}
|
|
42
|
-
#apply(
|
|
65
|
+
#apply(to: string, from: string) {
|
|
43
66
|
if (typeof window === "undefined") return;
|
|
44
|
-
const
|
|
45
|
-
Object.entries(this.#props.get(
|
|
46
|
-
.forEach(([name, value]) => style.setProperty(name, value));
|
|
67
|
+
const html = window.document.documentElement;
|
|
68
|
+
Object.entries(this.#props.get(to) ?? {})
|
|
69
|
+
.forEach(([name, value]) => html.style.setProperty(name, value));
|
|
70
|
+
if (html.classList.contains(from)) {
|
|
71
|
+
html.classList.replace(from, to);
|
|
72
|
+
} else {
|
|
73
|
+
html.classList.add(to);
|
|
74
|
+
}
|
|
47
75
|
}
|
|
48
76
|
#setColorScheme() {
|
|
49
77
|
if (typeof window === "undefined") return;
|
|
50
|
-
const themes = Object.keys(this.#props).filter((x) => x ===
|
|
78
|
+
const themes = Object.keys(this.#props).filter((x) => x === THEME.LIGHT || x === THEME.DARK);
|
|
51
79
|
window.document.documentElement.style.colorScheme = themes.join(" ");
|
|
52
80
|
}
|
|
53
81
|
#initProps() {
|
|
@@ -98,7 +126,13 @@
|
|
|
98
126
|
}
|
|
99
127
|
static #isPreferDark(): boolean {
|
|
100
128
|
if (typeof window === "undefined") return true;
|
|
101
|
-
return window.matchMedia(
|
|
129
|
+
return window.matchMedia(Theme.#MEDIA_PREFER_DARK).matches;
|
|
130
|
+
}
|
|
131
|
+
static #getThemeString(dark: boolean): string {
|
|
132
|
+
return dark ? THEME.DARK : THEME.LIGHT;
|
|
133
|
+
}
|
|
134
|
+
static getPreferTheme(): string {
|
|
135
|
+
return Theme.#getThemeString(Theme.#isPreferDark());
|
|
102
136
|
}
|
|
103
137
|
}
|
|
104
138
|
const theme = new Theme();
|
|
@@ -108,25 +142,42 @@
|
|
|
108
142
|
</script>
|
|
109
143
|
|
|
110
144
|
<script lang="ts">
|
|
111
|
-
let { dark = $bindable(
|
|
145
|
+
let { dark = $bindable(), status = $bindable(""), element = $bindable(), deps }: DarkToggleProps = $props();
|
|
112
146
|
|
|
113
147
|
// *** Initialize *** //
|
|
114
|
-
if (!status) status = STATE.
|
|
115
|
-
dark = theme.dark;
|
|
148
|
+
if (!status) status = STATE.NEUTRAL;
|
|
149
|
+
if (dark === undefined) dark = theme.dark;
|
|
150
|
+
theme.dark = dark;
|
|
116
151
|
|
|
117
152
|
// *** Initialize Deps *** //
|
|
118
153
|
const svsToggle = {
|
|
119
|
-
...omit(deps?.svsToggle, "main", "style"),
|
|
154
|
+
...omit(deps?.svsToggle, "main", "style", "attributes"),
|
|
120
155
|
style: deps?.svsToggle?.style ?? `${preset} svs-toggle`,
|
|
156
|
+
ariaLabel,
|
|
157
|
+
attributes: {
|
|
158
|
+
...deps?.svsToggle?.attributes,
|
|
159
|
+
},
|
|
121
160
|
};
|
|
122
161
|
|
|
123
162
|
// *** Bind Handlers *** //
|
|
124
|
-
$effect.pre(() => { theme.dark = dark });
|
|
163
|
+
$effect.pre(() => { theme.dark = dark!; });
|
|
125
164
|
</script>
|
|
126
165
|
|
|
127
166
|
<!---------------------------------------->
|
|
128
167
|
|
|
129
|
-
<Toggle bind:value={dark} bind:status bind:element {
|
|
168
|
+
<Toggle bind:value={dark} bind:status bind:element {...svsToggle}>
|
|
169
|
+
{#snippet main(status: string, value: boolean, element: HTMLButtonElement | undefined)}
|
|
170
|
+
{#if deps?.svsToggle?.main}
|
|
171
|
+
{@render deps.svsToggle.main(status, value, element)}
|
|
172
|
+
{:else}
|
|
173
|
+
{#if value}
|
|
174
|
+
{@render svgDark()}
|
|
175
|
+
{:else}
|
|
176
|
+
{@render svgLight()}
|
|
177
|
+
{/if}
|
|
178
|
+
{/if}
|
|
179
|
+
{/snippet}
|
|
180
|
+
</Toggle>
|
|
130
181
|
|
|
131
182
|
{#snippet svgDark()}
|
|
132
183
|
<svg style="width: 100%; height: 100%;" viewBox="0 0 24 24" aria-hidden="true">
|
|
@@ -139,14 +190,3 @@
|
|
|
139
190
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 0H13V4.06189C12.6724 4.02104 12.3387 4 12 4C11.6613 4 11.3276 4.02104 11 4.06189V0ZM7.0943 5.68018L4.22173 2.80761L2.80752 4.22183L5.6801 7.09441C6.09071 6.56618 6.56608 6.0908 7.0943 5.68018ZM4.06189 11H0V13H4.06189C4.02104 12.6724 4 12.3387 4 12C4 11.6613 4.02104 11.3276 4.06189 11ZM5.6801 16.9056L2.80751 19.7782L4.22173 21.1924L7.0943 18.3198C6.56608 17.9092 6.09071 17.4338 5.6801 16.9056ZM11 19.9381V24H13V19.9381C12.6724 19.979 12.3387 20 12 20C11.6613 20 11.3276 19.979 11 19.9381ZM16.9056 18.3199L19.7781 21.1924L21.1923 19.7782L18.3198 16.9057C17.9092 17.4339 17.4338 17.9093 16.9056 18.3199ZM19.9381 13H24V11H19.9381C19.979 11.3276 20 11.6613 20 12C20 12.3387 19.979 12.6724 19.9381 13ZM18.3198 7.0943L21.1923 4.22183L19.7781 2.80762L16.9056 5.6801C17.4338 6.09071 17.9092 6.56608 18.3198 7.0943Z" />
|
|
140
191
|
</svg>
|
|
141
192
|
{/snippet}
|
|
142
|
-
{#snippet main(status: string, value: boolean, element: HTMLButtonElement | undefined)}
|
|
143
|
-
{#if deps?.svsToggle?.main}
|
|
144
|
-
{@render deps.svsToggle.main(status, value, element)}
|
|
145
|
-
{:else}
|
|
146
|
-
{#if value}
|
|
147
|
-
{@render svgDark()}
|
|
148
|
-
{:else}
|
|
149
|
-
{@render svgLight()}
|
|
150
|
-
{/if}
|
|
151
|
-
{/if}
|
|
152
|
-
{/snippet}
|
|
@@ -1,15 +1,34 @@
|
|
|
1
|
-
export
|
|
1
|
+
export interface DarkToggleProps {
|
|
2
2
|
dark?: boolean;
|
|
3
3
|
status?: string;
|
|
4
4
|
element?: HTMLButtonElement;
|
|
5
5
|
deps?: DarkToggleDeps;
|
|
6
|
-
}
|
|
7
|
-
export
|
|
6
|
+
}
|
|
7
|
+
export interface DarkToggleDeps {
|
|
8
8
|
svsToggle?: Omit<ToggleProps, ToggleReqdProps | ToggleBindProps>;
|
|
9
|
-
}
|
|
9
|
+
}
|
|
10
10
|
export type DarkToggleReqdProps = never;
|
|
11
11
|
export type DarkToggleBindProps = "dark" | "status" | "element";
|
|
12
|
+
export declare const THEME: {
|
|
13
|
+
readonly LIGHT: "light";
|
|
14
|
+
readonly DARK: "dark";
|
|
15
|
+
};
|
|
16
|
+
export declare function setThemeToRoot(theme?: string): void;
|
|
12
17
|
import { type ToggleProps, type ToggleReqdProps, type ToggleBindProps } from "./_Toggle.svelte";
|
|
18
|
+
/**
|
|
19
|
+
* default value: `<value>`
|
|
20
|
+
* ```ts
|
|
21
|
+
* interface DarkToggleProps {
|
|
22
|
+
* dark?: boolean; // bindable <(prefers-color-scheme)>
|
|
23
|
+
* status?: string; // bindable <STATE.NEUTRAL>
|
|
24
|
+
* element?: HTMLButtonElement; // bindable
|
|
25
|
+
* deps?: DarkToggleDeps;
|
|
26
|
+
* }
|
|
27
|
+
* export interface DarkToggleDeps {
|
|
28
|
+
* svsToggle?: Omit<ToggleProps, ToggleReqdProps | ToggleBindProps>;
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
13
32
|
declare const DarkToggle: import("svelte").Component<DarkToggleProps, {}, "status" | "element" | "dark">;
|
|
14
33
|
type DarkToggle = ReturnType<typeof DarkToggle>;
|
|
15
34
|
export default DarkToggle;
|
|
@@ -1,29 +1,58 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
default value: `<value>`
|
|
4
|
+
```ts
|
|
5
|
+
interface TagsInputProps {
|
|
6
|
+
values?: string[]; // bindable
|
|
7
|
+
value?: string; // bindable
|
|
8
|
+
type?: "left" | "right"; // <"left">
|
|
9
|
+
confirm?: string[]; // <["Enter"]>
|
|
10
|
+
trim?: boolean; // <true>
|
|
11
|
+
unique?: boolean; // <true>
|
|
12
|
+
ariaErrMsgId?: string; // bindable
|
|
13
|
+
events?: TagsInputEvents;
|
|
14
|
+
status?: string; // bindable <STATE.NEUTRAL>
|
|
15
|
+
style?: SVSStyle;
|
|
16
|
+
attributes?: HTMLInputAttributes;
|
|
17
|
+
action?: Action;
|
|
18
|
+
element?: HTMLInputElement; // bindable
|
|
19
|
+
deps?: TagsInputDeps;
|
|
20
|
+
}
|
|
21
|
+
interface TagsInputDeps {
|
|
22
|
+
svsBadge?: Omit<BadgeProps, BadgeReqdProps | BadgeBindProps | "type" | "href">;
|
|
23
|
+
}
|
|
24
|
+
interface TagsInputEvents { // cancel if true
|
|
25
|
+
onadd?: (values: string[], value: string) => void | boolean;
|
|
26
|
+
onremove?: (values: string[], value: string, index: number) => void | boolean;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
-->
|
|
1
30
|
<script module lang="ts">
|
|
2
|
-
export
|
|
3
|
-
values?: string[]
|
|
4
|
-
value?: string
|
|
5
|
-
type?: "left" | "right"
|
|
6
|
-
confirm?: string[]
|
|
7
|
-
trim?: boolean
|
|
8
|
-
unique?: boolean
|
|
9
|
-
ariaErrMsgId?: string
|
|
10
|
-
events?: TagsInputEvents
|
|
11
|
-
status?: string
|
|
12
|
-
style?: SVSStyle
|
|
13
|
-
attributes?: HTMLInputAttributes
|
|
14
|
-
action?: Action
|
|
15
|
-
element?: HTMLInputElement
|
|
16
|
-
deps?: TagsInputDeps
|
|
17
|
-
}
|
|
18
|
-
export
|
|
19
|
-
svsBadge?: Omit<BadgeProps, BadgeReqdProps | BadgeBindProps | "type" | "href"
|
|
20
|
-
}
|
|
31
|
+
export interface TagsInputProps {
|
|
32
|
+
values?: string[]; // bindable
|
|
33
|
+
value?: string; // bindable
|
|
34
|
+
type?: "left" | "right"; // <"left">
|
|
35
|
+
confirm?: string[]; // <["Enter"]>
|
|
36
|
+
trim?: boolean; // <true>
|
|
37
|
+
unique?: boolean; // <true>
|
|
38
|
+
ariaErrMsgId?: string; // bindable
|
|
39
|
+
events?: TagsInputEvents;
|
|
40
|
+
status?: string; // bindable <STATE.NEUTRAL>
|
|
41
|
+
style?: SVSStyle;
|
|
42
|
+
attributes?: HTMLInputAttributes;
|
|
43
|
+
action?: Action;
|
|
44
|
+
element?: HTMLInputElement; // bindable
|
|
45
|
+
deps?: TagsInputDeps;
|
|
46
|
+
}
|
|
47
|
+
export interface TagsInputDeps {
|
|
48
|
+
svsBadge?: Omit<BadgeProps, BadgeReqdProps | BadgeBindProps | "type" | "href">;
|
|
49
|
+
}
|
|
21
50
|
export type TagsInputReqdProps = never;
|
|
22
51
|
export type TagsInputBindProps = "values" | "value" | "ariaErrMsgId" | "status" | "element";
|
|
23
|
-
export
|
|
24
|
-
onadd?: (values: string[], value: string) => void | boolean
|
|
25
|
-
onremove?: (values: string[], value: string, index: number) => void | boolean
|
|
26
|
-
}
|
|
52
|
+
export interface TagsInputEvents { // cancel if true
|
|
53
|
+
onadd?: (values: string[], value: string) => void | boolean;
|
|
54
|
+
onremove?: (values: string[], value: string, index: number) => void | boolean;
|
|
55
|
+
}
|
|
27
56
|
|
|
28
57
|
type TagsInputTarget = { currentTarget: EventTarget & HTMLInputElement };
|
|
29
58
|
type BadgeTarget = { currentTarget: EventTarget & HTMLButtonElement; };
|
|
@@ -32,7 +61,7 @@
|
|
|
32
61
|
|
|
33
62
|
import { type Action } from "svelte/action";
|
|
34
63
|
import { type HTMLInputAttributes } from "svelte/elements";
|
|
35
|
-
import { type SVSStyle, STATE,
|
|
64
|
+
import { type SVSStyle, STATE, PARTS, fnClass, omit } from "./core";
|
|
36
65
|
import Badge, { type BadgeProps, type BadgeReqdProps, type BadgeBindProps } from "./_Badge.svelte";
|
|
37
66
|
</script>
|
|
38
67
|
|
|
@@ -40,10 +69,11 @@
|
|
|
40
69
|
let { values = $bindable([]), value = $bindable(""), type = "left", confirm = [], trim = true, unique = true, ariaErrMsgId = $bindable(), events, status = $bindable(""), style, attributes, action, element = $bindable(), deps }: TagsInputProps = $props();
|
|
41
70
|
|
|
42
71
|
// *** Initialize *** //
|
|
43
|
-
if (!status) status = STATE.
|
|
72
|
+
if (!status) status = STATE.NEUTRAL;
|
|
44
73
|
const cls = fnClass(preset, style);
|
|
45
74
|
const attrs = omit(attributes, "class", "type", "onkeydown");
|
|
46
75
|
const confirmKeys = new Set([CONFIRM_KEY, ...confirm]);
|
|
76
|
+
let invalid = $derived(ariaErrMsgId ? true : undefined);
|
|
47
77
|
|
|
48
78
|
// *** Initialize Deps *** //
|
|
49
79
|
const svsBadge = {
|
|
@@ -77,12 +107,12 @@
|
|
|
77
107
|
|
|
78
108
|
<!---------------------------------------->
|
|
79
109
|
|
|
80
|
-
<div class={cls(
|
|
110
|
+
<div class={cls(PARTS.WHOLE, status)}>
|
|
81
111
|
{@render tags(type === "left")}
|
|
82
112
|
{#if action}
|
|
83
|
-
<input bind:value bind:this={element} class={cls(
|
|
113
|
+
<input bind:value bind:this={element} class={cls(PARTS.MAIN, status)} type="text" {onkeydown} aria-invalid={invalid} aria-errormessage={ariaErrMsgId} {...attrs} use:action />
|
|
84
114
|
{:else}
|
|
85
|
-
<input bind:value bind:this={element} class={cls(
|
|
115
|
+
<input bind:value bind:this={element} class={cls(PARTS.MAIN, status)} type="text" {onkeydown} aria-invalid={invalid} aria-errormessage={ariaErrMsgId} {...attrs} />
|
|
86
116
|
{/if}
|
|
87
117
|
{@render tags(type === "right")}
|
|
88
118
|
</div>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export
|
|
1
|
+
export interface TagsInputProps {
|
|
2
2
|
values?: string[];
|
|
3
3
|
value?: string;
|
|
4
4
|
type?: "left" | "right";
|
|
@@ -13,20 +13,48 @@ export type TagsInputProps = {
|
|
|
13
13
|
action?: Action;
|
|
14
14
|
element?: HTMLInputElement;
|
|
15
15
|
deps?: TagsInputDeps;
|
|
16
|
-
}
|
|
17
|
-
export
|
|
16
|
+
}
|
|
17
|
+
export interface TagsInputDeps {
|
|
18
18
|
svsBadge?: Omit<BadgeProps, BadgeReqdProps | BadgeBindProps | "type" | "href">;
|
|
19
|
-
}
|
|
19
|
+
}
|
|
20
20
|
export type TagsInputReqdProps = never;
|
|
21
21
|
export type TagsInputBindProps = "values" | "value" | "ariaErrMsgId" | "status" | "element";
|
|
22
|
-
export
|
|
22
|
+
export interface TagsInputEvents {
|
|
23
23
|
onadd?: (values: string[], value: string) => void | boolean;
|
|
24
24
|
onremove?: (values: string[], value: string, index: number) => void | boolean;
|
|
25
|
-
}
|
|
25
|
+
}
|
|
26
26
|
import { type Action } from "svelte/action";
|
|
27
27
|
import { type HTMLInputAttributes } from "svelte/elements";
|
|
28
28
|
import { type SVSStyle } from "./core";
|
|
29
29
|
import { type BadgeProps, type BadgeReqdProps, type BadgeBindProps } from "./_Badge.svelte";
|
|
30
|
+
/**
|
|
31
|
+
* default value: `<value>`
|
|
32
|
+
* ```ts
|
|
33
|
+
* interface TagsInputProps {
|
|
34
|
+
* values?: string[]; // bindable
|
|
35
|
+
* value?: string; // bindable
|
|
36
|
+
* type?: "left" | "right"; // <"left">
|
|
37
|
+
* confirm?: string[]; // <["Enter"]>
|
|
38
|
+
* trim?: boolean; // <true>
|
|
39
|
+
* unique?: boolean; // <true>
|
|
40
|
+
* ariaErrMsgId?: string; // bindable
|
|
41
|
+
* events?: TagsInputEvents;
|
|
42
|
+
* status?: string; // bindable <STATE.NEUTRAL>
|
|
43
|
+
* style?: SVSStyle;
|
|
44
|
+
* attributes?: HTMLInputAttributes;
|
|
45
|
+
* action?: Action;
|
|
46
|
+
* element?: HTMLInputElement; // bindable
|
|
47
|
+
* deps?: TagsInputDeps;
|
|
48
|
+
* }
|
|
49
|
+
* interface TagsInputDeps {
|
|
50
|
+
* svsBadge?: Omit<BadgeProps, BadgeReqdProps | BadgeBindProps | "type" | "href">;
|
|
51
|
+
* }
|
|
52
|
+
* interface TagsInputEvents { // cancel if true
|
|
53
|
+
* onadd?: (values: string[], value: string) => void | boolean;
|
|
54
|
+
* onremove?: (values: string[], value: string, index: number) => void | boolean;
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
30
58
|
declare const TagsInput: import("svelte").Component<TagsInputProps, {}, "value" | "status" | "element" | "values" | "ariaErrMsgId">;
|
|
31
59
|
type TagsInput = ReturnType<typeof TagsInput>;
|
|
32
60
|
export default TagsInput;
|
|
@@ -1,25 +1,58 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
default value: `<value>`
|
|
4
|
+
```ts
|
|
5
|
+
interface TagsInputFieldProps {
|
|
6
|
+
label?: string;
|
|
7
|
+
extra?: string;
|
|
8
|
+
aux?: Snippet<[string, string[], HTMLInputElement | undefined]>; // Snippet<[status,values,element]>
|
|
9
|
+
left?: Snippet<[string, string[], HTMLInputElement | undefined]>; // Snippet<[status,values,element]>
|
|
10
|
+
right?: Snippet<[string, string[], HTMLInputElement | undefined]>; // Snippet<[status,values,element]>
|
|
11
|
+
bottom?: string;
|
|
12
|
+
descFirst?: boolean; // <false>
|
|
13
|
+
values?: string[]; // bindable
|
|
14
|
+
min?: TagsInputFieldCountValidation;
|
|
15
|
+
max?: TagsInputFieldCountValidation;
|
|
16
|
+
constraints?: TagsInputFieldConstraint[];
|
|
17
|
+
validations?: TagsInputFieldValidation[];
|
|
18
|
+
status?: string; // bindable <STATE.NEUTRAL>
|
|
19
|
+
style?: SVSStyle;
|
|
20
|
+
element?: HTMLInputElement; // bindable
|
|
21
|
+
deps?: TagsInputFieldDeps;
|
|
22
|
+
}
|
|
23
|
+
interface TagsInputFieldDeps extends TagsInputDeps {
|
|
24
|
+
svsTagsInput?: Omit<TagsInputProps, TagsInputReqdProps | TagsInputBindProps | "deps">;
|
|
25
|
+
}
|
|
26
|
+
type TagsInputFieldConstraint = (value: string, validity: ValidityState) => string;
|
|
27
|
+
type TagsInputFieldValidation = (values: string[], validity: ValidityState) => string;
|
|
28
|
+
type TagsInputFieldCountValidation = {
|
|
29
|
+
value: number;
|
|
30
|
+
message: string;
|
|
19
31
|
};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
32
|
+
```
|
|
33
|
+
-->
|
|
34
|
+
<script module lang="ts">
|
|
35
|
+
export interface TagsInputFieldProps {
|
|
36
|
+
label?: string;
|
|
37
|
+
extra?: string;
|
|
38
|
+
aux?: Snippet<[string, string[], HTMLInputElement | undefined]>; // Snippet<[status,values,element]>
|
|
39
|
+
left?: Snippet<[string, string[], HTMLInputElement | undefined]>; // Snippet<[status,values,element]>
|
|
40
|
+
right?: Snippet<[string, string[], HTMLInputElement | undefined]>; // Snippet<[status,values,element]>
|
|
41
|
+
bottom?: string;
|
|
42
|
+
descFirst?: boolean; // <false>
|
|
43
|
+
values?: string[]; // bindable
|
|
44
|
+
min?: TagsInputFieldCountValidation;
|
|
45
|
+
max?: TagsInputFieldCountValidation;
|
|
46
|
+
constraints?: TagsInputFieldConstraint[];
|
|
47
|
+
validations?: TagsInputFieldValidation[];
|
|
48
|
+
status?: string; // bindable <STATE.NEUTRAL>
|
|
49
|
+
style?: SVSStyle;
|
|
50
|
+
element?: HTMLInputElement; // bindable
|
|
51
|
+
deps?: TagsInputFieldDeps;
|
|
52
|
+
}
|
|
53
|
+
export interface TagsInputFieldDeps extends TagsInputDeps {
|
|
54
|
+
svsTagsInput?: Omit<TagsInputProps, TagsInputReqdProps | TagsInputBindProps | "deps">;
|
|
55
|
+
}
|
|
23
56
|
export type TagsInputFieldReqdProps = never;
|
|
24
57
|
export type TagsInputFieldBindProps = "values" | "status" | "element";
|
|
25
58
|
export type TagsInputFieldConstraint = (value: string, validity: ValidityState) => string;
|
|
@@ -29,7 +62,7 @@
|
|
|
29
62
|
const preset = "svs-tags-input-field";
|
|
30
63
|
|
|
31
64
|
import { type Snippet, untrack } from "svelte";
|
|
32
|
-
import { type SVSStyle, STATE,
|
|
65
|
+
import { type SVSStyle, STATE, PARTS, elemId, fnClass, isNeutral, omit } from "./core";
|
|
33
66
|
import TagsInput, { type TagsInputProps, type TagsInputReqdProps, type TagsInputBindProps, type TagsInputDeps } from "./TagsInput.svelte";
|
|
34
67
|
</script>
|
|
35
68
|
|
|
@@ -37,7 +70,7 @@
|
|
|
37
70
|
let { label, extra, aux, left, right, bottom, descFirst = false, values = $bindable([]), min, max, constraints = [], validations = [], status = $bindable(""), style, element = $bindable(), deps }: TagsInputFieldProps = $props();
|
|
38
71
|
|
|
39
72
|
// *** Initialize *** //
|
|
40
|
-
if (!status) status = STATE.
|
|
73
|
+
if (!status) status = STATE.NEUTRAL;
|
|
41
74
|
const cls = fnClass(preset, style);
|
|
42
75
|
const id = deps?.svsTagsInput?.attributes?.id ? deps.svsTagsInput.attributes.id : elemId.get(label?.trim());
|
|
43
76
|
const idLabel = elemId.get(label?.trim());
|
|
@@ -67,7 +100,7 @@
|
|
|
67
100
|
};
|
|
68
101
|
|
|
69
102
|
// *** Status *** //
|
|
70
|
-
let neutral = isNeutral(status) ? status : STATE.
|
|
103
|
+
let neutral = isNeutral(status) ? status : STATE.NEUTRAL;
|
|
71
104
|
$effect(() => { neutral = isNeutral(status) ? status : neutral });
|
|
72
105
|
let live = $derived(status === STATE.INACTIVE ? "alert" : "status");
|
|
73
106
|
let idMsg = $derived(status === STATE.INACTIVE && message?.trim() ? idErr : undefined);
|
|
@@ -128,30 +161,30 @@
|
|
|
128
161
|
|
|
129
162
|
<!---------------------------------------->
|
|
130
163
|
|
|
131
|
-
<div class={cls(
|
|
164
|
+
<div class={cls(PARTS.WHOLE, status)} role="group" aria-labelledby={idLabel}>
|
|
132
165
|
{#if aux}
|
|
133
|
-
<div class={cls(
|
|
166
|
+
<div class={cls(PARTS.TOP, status)}>
|
|
134
167
|
{@render lbl()}
|
|
135
|
-
<span class={cls(
|
|
168
|
+
<span class={cls(PARTS.AUX, status)}>{@render aux(status, values, element)}</span>
|
|
136
169
|
</div>
|
|
137
170
|
{:else}
|
|
138
171
|
{@render lbl()}
|
|
139
172
|
{/if}
|
|
140
173
|
{@render desc(descFirst)}
|
|
141
|
-
<div class={cls(
|
|
142
|
-
{@render side(
|
|
174
|
+
<div class={cls(PARTS.MIDDLE, status)}>
|
|
175
|
+
{@render side(PARTS.LEFT, left)}
|
|
143
176
|
<TagsInput bind:values bind:value bind:status bind:element bind:ariaErrMsgId={idMsg} {...svsTagsInput} deps={{ svsBadge }} />
|
|
144
|
-
{@render side(
|
|
177
|
+
{@render side(PARTS.RIGHT, right)}
|
|
145
178
|
</div>
|
|
146
179
|
{@render desc(!descFirst)}
|
|
147
180
|
</div>
|
|
148
181
|
|
|
149
182
|
{#snippet lbl()}
|
|
150
183
|
{#if label?.trim()}
|
|
151
|
-
<label class={cls(
|
|
184
|
+
<label class={cls(PARTS.LABEL, status)} for={id} id={idLabel}>
|
|
152
185
|
{label}
|
|
153
186
|
{#if extra?.trim()}
|
|
154
|
-
<span class={cls(
|
|
187
|
+
<span class={cls(PARTS.EXTRA, status)}>{extra}</span>
|
|
155
188
|
{/if}
|
|
156
189
|
</label>
|
|
157
190
|
{/if}
|
|
@@ -163,7 +196,7 @@
|
|
|
163
196
|
{/snippet}
|
|
164
197
|
{#snippet desc(show: boolean)}
|
|
165
198
|
{#if show && message?.trim()}
|
|
166
|
-
<div class={cls(
|
|
199
|
+
<div class={cls(PARTS.BOTTOM, status)} id={idDesc ?? idErr} role={live}>{message}</div>
|
|
167
200
|
{/if}
|
|
168
201
|
{/snippet}
|
|
169
202
|
|