svseeds 0.4.8 → 0.4.10
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/TagsInputField.svelte +14 -3
- package/_svseeds/TagsInputField.svelte.d.ts +2 -0
- package/_svseeds/ToggleGroupField.svelte +6 -4
- package/_svseeds/_CheckField.svelte +1 -1
- package/_svseeds/_ColorPicker.svelte +3 -2
- package/_svseeds/_ColorPicker.svelte.d.ts +2 -1
- package/_svseeds/_ComboBox.svelte +55 -37
- package/_svseeds/_ComboBox.svelte.d.ts +5 -0
- package/_svseeds/_ContextMenu.svelte +18 -8
- package/_svseeds/_ContextMenu.svelte.d.ts +2 -0
- package/_svseeds/_Modal.svelte +5 -3
- package/_svseeds/_Modal.svelte.d.ts +2 -2
- package/_svseeds/_ProgressTracker.svelte +22 -16
- package/_svseeds/_ProgressTracker.svelte.d.ts +3 -3
- package/_svseeds/_SelectField.svelte +3 -3
- package/_svseeds/_TagsInput.svelte +7 -7
- package/_svseeds/_TagsInput.svelte.d.ts +2 -2
- package/_svseeds/_TextField.svelte +6 -6
- package/_svseeds/_TextField.svelte.d.ts +5 -5
- package/_svseeds/_ToggleGroup.svelte +2 -2
- package/_svseeds/_ToggleGroup.svelte.d.ts +1 -1
- package/package.json +1 -1
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
max?: TagsInputFieldCountValidation;
|
|
16
16
|
constraints?: TagsInputFieldConstraint[];
|
|
17
17
|
validations?: TagsInputFieldValidation[];
|
|
18
|
+
name?: string;
|
|
18
19
|
element?: HTMLInputElement; // bindable
|
|
19
20
|
styling?: SVSClass;
|
|
20
21
|
variant?: string; // bindable (VARIANT.NEUTRAL)
|
|
@@ -45,6 +46,7 @@
|
|
|
45
46
|
max?: TagsInputFieldCountValidation;
|
|
46
47
|
constraints?: TagsInputFieldConstraint[];
|
|
47
48
|
validations?: TagsInputFieldValidation[];
|
|
49
|
+
name?: string;
|
|
48
50
|
element?: HTMLInputElement; // bindable
|
|
49
51
|
styling?: SVSClass;
|
|
50
52
|
variant?: string; // bindable (VARIANT.NEUTRAL)
|
|
@@ -67,7 +69,7 @@
|
|
|
67
69
|
</script>
|
|
68
70
|
|
|
69
71
|
<script lang="ts">
|
|
70
|
-
let { label, extra, aux, left, right, bottom, descFirst = false, values = $bindable([]), min, max, constraints = [], validations = [], element = $bindable(), styling, variant = $bindable(""), deps }: TagsInputFieldProps = $props();
|
|
72
|
+
let { label, extra, aux, left, right, bottom, descFirst = false, values = $bindable([]), min, max, constraints = [], validations = [], name, element = $bindable(), styling, variant = $bindable(""), deps }: TagsInputFieldProps = $props();
|
|
71
73
|
|
|
72
74
|
// *** Initialize *** //
|
|
73
75
|
if (!variant) variant = VARIANT.NEUTRAL;
|
|
@@ -78,6 +80,7 @@
|
|
|
78
80
|
const idErr = idDesc ?? elemId.id;
|
|
79
81
|
let message = $state(bottom);
|
|
80
82
|
let value = $state("");
|
|
83
|
+
if (!name) name = deps?.svsTagsInput?.attributes?.name as string | undefined;
|
|
81
84
|
if (max) constraints.unshift(() => values.length >= max.value ? max.message : "");
|
|
82
85
|
if (min) validations.unshift(() => values.length < min.value ? min.message : "");
|
|
83
86
|
|
|
@@ -87,7 +90,7 @@
|
|
|
87
90
|
events: { onadd, onremove: deps?.svsTagsInput?.events?.onremove },
|
|
88
91
|
styling: deps?.svsTagsInput?.styling ?? `${preset} svs-tags-input`,
|
|
89
92
|
attributes: {
|
|
90
|
-
...omit(deps?.svsTagsInput?.attributes, "id", "onchange", "oninvalid", "aria-describedby"),
|
|
93
|
+
...omit(deps?.svsTagsInput?.attributes, "id", "name", "onchange", "oninvalid", "aria-describedby"),
|
|
91
94
|
id,
|
|
92
95
|
onchange,
|
|
93
96
|
oninvalid,
|
|
@@ -95,7 +98,7 @@
|
|
|
95
98
|
},
|
|
96
99
|
};
|
|
97
100
|
|
|
98
|
-
// ***
|
|
101
|
+
// *** States *** //
|
|
99
102
|
let neutral = isNeutral(variant) ? variant : VARIANT.NEUTRAL;
|
|
100
103
|
$effect(() => { neutral = isNeutral(variant) ? variant : neutral });
|
|
101
104
|
let live = $derived(variant === VARIANT.INACTIVE ? "alert" : "status");
|
|
@@ -169,6 +172,7 @@
|
|
|
169
172
|
{@render desc(descFirst)}
|
|
170
173
|
<div class={cls(PARTS.MIDDLE, variant)}>
|
|
171
174
|
{@render side(PARTS.LEFT, left)}
|
|
175
|
+
{@render fnForm()}
|
|
172
176
|
<TagsInput bind:values bind:value bind:variant bind:element bind:ariaErrMsgId={idMsg} {...svsTagsInput} />
|
|
173
177
|
{@render side(PARTS.RIGHT, right)}
|
|
174
178
|
</div>
|
|
@@ -195,3 +199,10 @@
|
|
|
195
199
|
<div class={cls(PARTS.BOTTOM, variant)} id={idDesc ?? idErr} role={live}>{message}</div>
|
|
196
200
|
{/if}
|
|
197
201
|
{/snippet}
|
|
202
|
+
{#snippet fnForm()}
|
|
203
|
+
{#if name}
|
|
204
|
+
{#each values as value}
|
|
205
|
+
<input type="hidden" {name} {value} />
|
|
206
|
+
{/each}
|
|
207
|
+
{/if}
|
|
208
|
+
{/snippet}
|
|
@@ -11,6 +11,7 @@ export interface TagsInputFieldProps {
|
|
|
11
11
|
max?: TagsInputFieldCountValidation;
|
|
12
12
|
constraints?: TagsInputFieldConstraint[];
|
|
13
13
|
validations?: TagsInputFieldValidation[];
|
|
14
|
+
name?: string;
|
|
14
15
|
element?: HTMLInputElement;
|
|
15
16
|
styling?: SVSClass;
|
|
16
17
|
variant?: string;
|
|
@@ -46,6 +47,7 @@ import { type TagsInputProps, type TagsInputReqdProps, type TagsInputBindProps }
|
|
|
46
47
|
* max?: TagsInputFieldCountValidation;
|
|
47
48
|
* constraints?: TagsInputFieldConstraint[];
|
|
48
49
|
* validations?: TagsInputFieldValidation[];
|
|
50
|
+
* name?: string;
|
|
49
51
|
* element?: HTMLInputElement; // bindable
|
|
50
52
|
* styling?: SVSClass;
|
|
51
53
|
* variant?: string; // bindable (VARIANT.NEUTRAL)
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
action: deps?.svsToggleGroup?.action as Action,
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
-
// ***
|
|
84
|
+
// *** States *** //
|
|
85
85
|
let neutral = $state(isNeutral(variant) ? variant : VARIANT.NEUTRAL);
|
|
86
86
|
$effect(() => { neutral = isNeutral(variant) ? variant : neutral });
|
|
87
87
|
let live = $derived(variant === VARIANT.INACTIVE ? "alert" : "status");
|
|
@@ -163,7 +163,9 @@
|
|
|
163
163
|
{/snippet}
|
|
164
164
|
{#snippet fnForm()}
|
|
165
165
|
<input bind:this={element} style="display: none;" aria-hidden="true" {oninvalid} />
|
|
166
|
-
{#
|
|
167
|
-
|
|
168
|
-
|
|
166
|
+
{#if name}
|
|
167
|
+
{#each values as value}
|
|
168
|
+
<input type="hidden" {name} {value} />
|
|
169
|
+
{/each}
|
|
170
|
+
{/if}
|
|
169
171
|
{/snippet}
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
const roleGroup = multiple ? "group" : "radiogroup";
|
|
67
67
|
let message = $state(bottom);
|
|
68
68
|
|
|
69
|
-
// ***
|
|
69
|
+
// *** States *** //
|
|
70
70
|
let neutral = $state(isNeutral(variant) ? variant : VARIANT.NEUTRAL);
|
|
71
71
|
$effect(() => { neutral = isNeutral(variant) ? variant : neutral });
|
|
72
72
|
let live = $derived(variant === VARIANT.INACTIVE ? "alert" : "status");
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
```ts
|
|
16
16
|
type RgbColor = [number, number, number];
|
|
17
17
|
function getHex(rgb: RgbColor): string
|
|
18
|
-
// getHex([255, 123, 34])
|
|
18
|
+
// getHex([255, 123, 34]) => "#ff7b22"
|
|
19
|
+
// getHex([255, 255, 255]) => "#ffffff"
|
|
19
20
|
```
|
|
20
21
|
-->
|
|
21
22
|
<script module lang="ts">
|
|
@@ -82,7 +83,7 @@
|
|
|
82
83
|
<!---------------------------------------->
|
|
83
84
|
|
|
84
85
|
<label class={cls(PARTS.WHOLE, variant)}>
|
|
85
|
-
<div style="display: inline-block; background-image: linear-gradient(45deg,#ccc 25%,transparent 25%),linear-gradient(-45deg,#ccc 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#ccc 75%),linear-gradient(-45deg,transparent 75%,#ccc 75%); background-size: 20px 20px; background-position: 0 0,0 10px,10px -10px,-10px 0px;">
|
|
86
|
+
<div class={cls(PARTS.MIDDLE, variant)} style="display: inline-block; background-image: linear-gradient(45deg,#ccc 25%,transparent 25%),linear-gradient(-45deg,#ccc 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#ccc 75%),linear-gradient(-45deg,transparent 75%,#ccc 75%); background-size: 20px 20px; background-position: 0 0,0 10px,10px -10px,-10px 0px;">
|
|
86
87
|
<div class={cls(PARTS.MAIN, variant)} style={`background-color: rgba(${rgb[0]},${rgb[1]},${rgb[2]},${alp})`}>
|
|
87
88
|
{#if action}
|
|
88
89
|
<input bind:value bind:this={element} type="color" style="visibility: hidden;" {...attrs} use:action />
|
|
@@ -30,7 +30,8 @@ import { type SVSClass } from "./core";
|
|
|
30
30
|
* ```ts
|
|
31
31
|
* type RgbColor = [number, number, number];
|
|
32
32
|
* function getHex(rgb: RgbColor): string
|
|
33
|
-
* // getHex([255, 123, 34])
|
|
33
|
+
* // getHex([255, 123, 34]) => "#ff7b22"
|
|
34
|
+
* // getHex([255, 255, 255]) => "#ffffff"
|
|
34
35
|
* ```
|
|
35
36
|
*/
|
|
36
37
|
declare const ColorPicker: import("svelte").Component<ColorPickerProps, {}, "variant" | "alpha" | "value" | "element">;
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
```ts
|
|
5
5
|
interface ComboBoxProps {
|
|
6
6
|
options: SvelteSet<string> | Set<string>;
|
|
7
|
+
extra?: Snippet<[boolean, string]>; // Snippet<[expanded,variant]>
|
|
7
8
|
value?: string; // bindable
|
|
8
9
|
expanded?: boolean; // bindable
|
|
10
|
+
search?: boolean // (true)
|
|
9
11
|
attributes?: HTMLInputAttributes;
|
|
10
12
|
action?: Action;
|
|
11
13
|
element?: HTMLInputElement; // bindable
|
|
@@ -17,8 +19,10 @@
|
|
|
17
19
|
<script module lang="ts">
|
|
18
20
|
export interface ComboBoxProps {
|
|
19
21
|
options: SvelteSet<string> | Set<string>;
|
|
22
|
+
extra?: Snippet<[boolean, string]>; // Snippet<[expanded,variant]>
|
|
20
23
|
value?: string; // bindable
|
|
21
24
|
expanded?: boolean; // bindable
|
|
25
|
+
search?: boolean // (true)
|
|
22
26
|
attributes?: HTMLInputAttributes;
|
|
23
27
|
action?: Action;
|
|
24
28
|
element?: HTMLInputElement; // bindable
|
|
@@ -30,9 +34,8 @@
|
|
|
30
34
|
|
|
31
35
|
const preset = "svs-combo-box";
|
|
32
36
|
const NA = -1;
|
|
33
|
-
const optionStyle = "cursor: default; user-select: none;";
|
|
34
37
|
|
|
35
|
-
import {
|
|
38
|
+
import { type Snippet } from "svelte";
|
|
36
39
|
import { type Action } from "svelte/action";
|
|
37
40
|
import { type SvelteSet } from "svelte/reactivity";
|
|
38
41
|
import { type HTMLInputAttributes } from "svelte/elements";
|
|
@@ -40,7 +43,7 @@
|
|
|
40
43
|
</script>
|
|
41
44
|
|
|
42
45
|
<script lang="ts">
|
|
43
|
-
let { options, value = $bindable(""), expanded = $bindable(false), attributes, action, element = $bindable(), styling, variant = $bindable("") }: ComboBoxProps = $props();
|
|
46
|
+
let { options, extra, value = $bindable(""), expanded = $bindable(false), search = true, attributes, action, element = $bindable(), styling, variant = $bindable("") }: ComboBoxProps = $props();
|
|
44
47
|
|
|
45
48
|
// *** Initialize *** //
|
|
46
49
|
if (!variant) variant = VARIANT.NEUTRAL;
|
|
@@ -52,25 +55,24 @@
|
|
|
52
55
|
let listElem: HTMLUListElement | undefined = $state();
|
|
53
56
|
|
|
54
57
|
// *** Bind Handlers *** //
|
|
55
|
-
let listboxStyle = $derived(`position:
|
|
58
|
+
let listboxStyle = $derived(`position:absolute;cursor:default;user-select:none;visibility: ${expanded ? "visible" : "hidden"};${overflow.x ? "right:0%;" : ""}${overflow.y ? "bottom:100%;" : ""}`);
|
|
56
59
|
let opts = $derived([...options.keys()]);
|
|
57
|
-
$
|
|
58
|
-
untrack(() => observeOverflow());
|
|
59
|
-
});
|
|
60
|
-
function observeOverflow() {
|
|
61
|
-
if (!listElem || !window) return;
|
|
62
|
-
const rect = listElem.getBoundingClientRect();
|
|
63
|
-
overflow.x = window.innerWidth < rect.right;
|
|
64
|
-
overflow.y = window.innerHeight < rect.bottom;
|
|
65
|
-
}
|
|
60
|
+
let maxlen = $derived(opts.reduce((max, x) => Math.max(max, [...x].length), 0));
|
|
66
61
|
|
|
67
62
|
// *** Event Handlers *** //
|
|
68
63
|
function open(activate: boolean = false, bottom: boolean = false) {
|
|
69
64
|
if (expanded) return;
|
|
70
65
|
selected = opts.indexOf(value);
|
|
71
66
|
if (activate && selected === NA) selected = bottom ? opts.length - 1 : 0;
|
|
67
|
+
observeOverflow();
|
|
72
68
|
expanded = true;
|
|
73
69
|
}
|
|
70
|
+
function observeOverflow() {
|
|
71
|
+
if (!listElem || !window) return;
|
|
72
|
+
const rect = listElem.getBoundingClientRect();
|
|
73
|
+
overflow.x = window.innerWidth < rect.right;
|
|
74
|
+
overflow.y = window.innerHeight < rect.bottom;
|
|
75
|
+
}
|
|
74
76
|
function apply() {
|
|
75
77
|
if (!expanded) return;
|
|
76
78
|
value = opts[selected];
|
|
@@ -81,25 +83,37 @@
|
|
|
81
83
|
expanded = false;
|
|
82
84
|
selected = NA;
|
|
83
85
|
}
|
|
86
|
+
function oninput(ev: Event) {
|
|
87
|
+
if ((ev as InputEvent).isComposing) return;
|
|
88
|
+
if ([...value].length > maxlen) return;
|
|
89
|
+
if (options.has(value)) {
|
|
90
|
+
selected = opts.indexOf(value);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (!search) return;
|
|
94
|
+
selected = opts.findIndex((x) => x.startsWith(value));
|
|
95
|
+
}
|
|
84
96
|
function onkeydown(ev: KeyboardEvent) {
|
|
85
97
|
if (ev.isComposing) return;
|
|
86
98
|
if (ev.ctrlKey || ev.shiftKey || ev.metaKey) return;
|
|
87
99
|
switch (ev.key) {
|
|
88
100
|
case "Escape": if (!ev.altKey && expanded) close(); break;
|
|
89
101
|
case "Enter": if (!ev.altKey && expanded && selected > NA) apply(); break;
|
|
90
|
-
case "ArrowDown": caseArrowDown(ev.altKey); break;
|
|
91
|
-
case "ArrowUp": caseArrowUp(ev.altKey); break;
|
|
102
|
+
case "ArrowDown": caseArrowDown(ev, ev.altKey); break;
|
|
103
|
+
case "ArrowUp": caseArrowUp(ev, ev.altKey); break;
|
|
92
104
|
}
|
|
93
105
|
}
|
|
94
|
-
function caseArrowDown(alt: boolean) {
|
|
95
|
-
if (alt && !expanded) return open();
|
|
106
|
+
function caseArrowDown(ev: KeyboardEvent, alt: boolean) {
|
|
96
107
|
if (alt && expanded) return;
|
|
108
|
+
ev.preventDefault();
|
|
109
|
+
if (alt && !expanded) return open();
|
|
97
110
|
if (!alt && !expanded) return open(true);
|
|
98
111
|
if (selected < opts.length - 1) selected++;
|
|
99
112
|
if (selected === NA) selected = 0;
|
|
100
113
|
}
|
|
101
|
-
function caseArrowUp(alt: boolean) {
|
|
114
|
+
function caseArrowUp(ev: KeyboardEvent, alt: boolean) {
|
|
102
115
|
if (alt && !expanded) return;
|
|
116
|
+
ev.preventDefault();
|
|
103
117
|
if (alt && expanded) return close();
|
|
104
118
|
if (!alt && !expanded) return open(true, true);
|
|
105
119
|
if (selected > 0) selected--;
|
|
@@ -108,25 +122,29 @@
|
|
|
108
122
|
</script>
|
|
109
123
|
|
|
110
124
|
<!---------------------------------------->
|
|
125
|
+
<svelte:document onscroll={() => close()} />
|
|
111
126
|
|
|
112
127
|
{#if options.size}
|
|
113
|
-
<
|
|
114
|
-
|
|
115
|
-
{
|
|
116
|
-
|
|
117
|
-
{
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
<
|
|
121
|
-
{
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
{
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
128
|
+
<span class={cls(PARTS.WHOLE, variant)} style="position:relative;">
|
|
129
|
+
{#if action}
|
|
130
|
+
<input bind:value bind:this={element} class={cls(PARTS.MAIN, variant)} type="text" role="combobox" aria-haspopup="listbox" aria-autocomplete="none" aria-controls={idList} aria-expanded={expanded} onfocus={() => open()} onblur={close} {onkeydown} {oninput} {...attrs} use:action />
|
|
131
|
+
{:else}
|
|
132
|
+
<input bind:value bind:this={element} class={cls(PARTS.MAIN, variant)} type="text" role="combobox" aria-haspopup="listbox" aria-autocomplete="none" aria-controls={idList} aria-expanded={expanded} onfocus={() => open()} onblur={close} {onkeydown} {oninput} {...attrs} />
|
|
133
|
+
{/if}
|
|
134
|
+
{#if extra}
|
|
135
|
+
<div class={cls(PARTS.EXTRA, variant)} style="position:absolute;top:50%;right:0%;transform:translateY(-50%);pointer-events:none;">
|
|
136
|
+
{@render extra(expanded, variant)}
|
|
137
|
+
</div>
|
|
138
|
+
{/if}
|
|
139
|
+
<ul bind:this={listElem} class={cls(PARTS.BOTTOM, expanded ? VARIANT.ACTIVE : variant)} id={idList} role="listbox" style={listboxStyle}>
|
|
140
|
+
{#each opts as opt, i (opt)}
|
|
141
|
+
{@const isSelected = i === selected}
|
|
142
|
+
{@const labelStatus = isSelected ? VARIANT.ACTIVE : variant}
|
|
143
|
+
{@const onpointerenter = () => selected = i}
|
|
144
|
+
<li class={cls(PARTS.LABEL, labelStatus)} aria-selected={isSelected} role="option" onpointerdown={apply} {onpointerenter}>
|
|
145
|
+
{opt}
|
|
146
|
+
</li>
|
|
147
|
+
{/each}
|
|
148
|
+
</ul>
|
|
149
|
+
</span>
|
|
132
150
|
{/if}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export interface ComboBoxProps {
|
|
2
2
|
options: SvelteSet<string> | Set<string>;
|
|
3
|
+
extra?: Snippet<[boolean, string]>;
|
|
3
4
|
value?: string;
|
|
4
5
|
expanded?: boolean;
|
|
6
|
+
search?: boolean;
|
|
5
7
|
attributes?: HTMLInputAttributes;
|
|
6
8
|
action?: Action;
|
|
7
9
|
element?: HTMLInputElement;
|
|
@@ -10,6 +12,7 @@ export interface ComboBoxProps {
|
|
|
10
12
|
}
|
|
11
13
|
export type ComboBoxReqdProps = "options";
|
|
12
14
|
export type ComboBoxBindProps = "value" | "expanded" | "variant" | "element";
|
|
15
|
+
import { type Snippet } from "svelte";
|
|
13
16
|
import { type Action } from "svelte/action";
|
|
14
17
|
import { type SvelteSet } from "svelte/reactivity";
|
|
15
18
|
import { type HTMLInputAttributes } from "svelte/elements";
|
|
@@ -19,8 +22,10 @@ import { type SVSClass } from "./core";
|
|
|
19
22
|
* ```ts
|
|
20
23
|
* interface ComboBoxProps {
|
|
21
24
|
* options: SvelteSet<string> | Set<string>;
|
|
25
|
+
* extra?: Snippet<[boolean, string]>; // Snippet<[expanded,variant]>
|
|
22
26
|
* value?: string; // bindable
|
|
23
27
|
* expanded?: boolean; // bindable
|
|
28
|
+
* search?: boolean // (true)
|
|
24
29
|
* attributes?: HTMLInputAttributes;
|
|
25
30
|
* action?: Action;
|
|
26
31
|
* element?: HTMLInputElement; // bindable
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
children: Snippet<[string]>; // Snippet<[variant]>
|
|
7
7
|
open?: boolean; // bindable (false); to observe state, not to control
|
|
8
8
|
lock?: boolean; // bindable (false)
|
|
9
|
+
target?: HTMLElement;
|
|
9
10
|
element?: HTMLElement; // bindable
|
|
10
11
|
styling?: SVSClass;
|
|
11
12
|
variant?: string; // bindable (VARIANT.NEUTRAL)
|
|
@@ -17,6 +18,7 @@
|
|
|
17
18
|
children: Snippet<[string]>; // Snippet<[variant]>
|
|
18
19
|
open?: boolean; // bindable (false); to observe state, not to control
|
|
19
20
|
lock?: boolean; // bindable (false)
|
|
21
|
+
target?: HTMLElement;
|
|
20
22
|
element?: HTMLElement; // bindable
|
|
21
23
|
styling?: SVSClass;
|
|
22
24
|
variant?: string; // bindable (VARIANT.NEUTRAL)
|
|
@@ -26,39 +28,47 @@
|
|
|
26
28
|
|
|
27
29
|
const preset = "svs-context-menu";
|
|
28
30
|
|
|
29
|
-
import { type Snippet } from "svelte";
|
|
31
|
+
import { type Snippet, onDestroy } from "svelte";
|
|
32
|
+
import { on } from "svelte/events";
|
|
30
33
|
import { type SVSClass, VARIANT, PARTS, fnClass } from "./core";
|
|
31
34
|
</script>
|
|
32
35
|
|
|
33
36
|
<!---------------------------------------->
|
|
34
37
|
|
|
35
38
|
<script lang="ts">
|
|
36
|
-
let { children, open = $bindable(false), lock = $bindable(false), element = $bindable(), styling, variant = $bindable("") }: ContextMenuProps = $props();
|
|
39
|
+
let { children, open = $bindable(false), lock = $bindable(false), target, element = $bindable(), styling, variant = $bindable("") }: ContextMenuProps = $props();
|
|
37
40
|
|
|
38
41
|
// *** Initialize *** //
|
|
39
42
|
if (!variant) variant = VARIANT.NEUTRAL;
|
|
40
43
|
const cls = fnClass(preset, styling);
|
|
41
44
|
let position = $state({ x: 0, y: 0 });
|
|
45
|
+
let listeners: (() => void)[] = [];
|
|
46
|
+
$effect(() => {
|
|
47
|
+
listeners.push(on(target ?? document, "contextmenu", show));
|
|
48
|
+
if (target) listeners.push(on(document, "contextmenu", hide));
|
|
49
|
+
});
|
|
50
|
+
onDestroy(() => listeners.forEach((x) => x()));
|
|
42
51
|
|
|
43
52
|
// *** Bind Handlers *** //
|
|
44
|
-
let
|
|
45
|
-
let dynStyle = $derived(`position: fixed; left:${position.x}px; top:${position.y}px; ${visibility}`);
|
|
53
|
+
let dynStyle = $derived(`position:fixed;left:${position.x}px;top:${position.y}px;${open ? "visibility:visible;" : "visibility:hidden;z-index:-9999;"}`);
|
|
46
54
|
|
|
47
55
|
// *** Event Handlers *** //
|
|
48
|
-
function show(ev:
|
|
56
|
+
function show(ev: Event) {
|
|
49
57
|
if (lock) return;
|
|
50
58
|
ev.preventDefault();
|
|
59
|
+
ev.stopPropagation();
|
|
51
60
|
|
|
61
|
+
const [x, y] = [(ev as MouseEvent).clientX, (ev as MouseEvent).clientY];
|
|
52
62
|
const menu = { width: element?.offsetWidth ?? 0, height: element?.offsetHeight ?? 0 };
|
|
53
|
-
position.x = window.innerWidth-
|
|
54
|
-
position.y = window.innerHeight-
|
|
63
|
+
position.x = window.innerWidth-x < menu.width ? x-menu.width : x;
|
|
64
|
+
position.y = window.innerHeight-y < menu.height ? (y < menu.height ? y : y-menu.height) : y;
|
|
55
65
|
open = true;
|
|
56
66
|
}
|
|
57
67
|
function hide() { if (!lock) open = false; }
|
|
58
68
|
</script>
|
|
59
69
|
|
|
60
70
|
<!---------------------------------------->
|
|
61
|
-
<svelte:document
|
|
71
|
+
<svelte:document onclick={hide} />
|
|
62
72
|
|
|
63
73
|
<nav class={cls(PARTS.WHOLE, variant)} style={dynStyle} bind:this={element}>
|
|
64
74
|
{@render children(variant)}
|
|
@@ -2,6 +2,7 @@ export interface ContextMenuProps {
|
|
|
2
2
|
children: Snippet<[string]>;
|
|
3
3
|
open?: boolean;
|
|
4
4
|
lock?: boolean;
|
|
5
|
+
target?: HTMLElement;
|
|
5
6
|
element?: HTMLElement;
|
|
6
7
|
styling?: SVSClass;
|
|
7
8
|
variant?: string;
|
|
@@ -17,6 +18,7 @@ import { type SVSClass } from "./core";
|
|
|
17
18
|
* children: Snippet<[string]>; // Snippet<[variant]>
|
|
18
19
|
* open?: boolean; // bindable (false); to observe state, not to control
|
|
19
20
|
* lock?: boolean; // bindable (false)
|
|
21
|
+
* target?: HTMLElement;
|
|
20
22
|
* element?: HTMLElement; // bindable
|
|
21
23
|
* styling?: SVSClass;
|
|
22
24
|
* variant?: string; // bindable (VARIANT.NEUTRAL)
|
package/_svseeds/_Modal.svelte
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
closable?: boolean; // (true)
|
|
9
9
|
trigger?: HTMLElement; // bindable
|
|
10
10
|
ariaLabel?: string;
|
|
11
|
-
variant?: string; // bindable (VARIANT.NEUTRAL)
|
|
12
|
-
styling?: SVSClass;
|
|
13
11
|
element?: HTMLDialogElement; // bindable
|
|
12
|
+
styling?: SVSClass;
|
|
13
|
+
variant?: string; // bindable (VARIANT.NEUTRAL)
|
|
14
14
|
}
|
|
15
15
|
```
|
|
16
16
|
-->
|
|
@@ -44,7 +44,9 @@
|
|
|
44
44
|
// *** Bind Handlers *** //
|
|
45
45
|
$effect(() => {
|
|
46
46
|
open;
|
|
47
|
-
untrack(() =>
|
|
47
|
+
untrack(() => {
|
|
48
|
+
if (!open) trigger?.focus();
|
|
49
|
+
});
|
|
48
50
|
});
|
|
49
51
|
$effect.pre(() => {
|
|
50
52
|
open;
|
|
@@ -21,9 +21,9 @@ import { type SVSClass } from "./core";
|
|
|
21
21
|
* closable?: boolean; // (true)
|
|
22
22
|
* trigger?: HTMLElement; // bindable
|
|
23
23
|
* ariaLabel?: string;
|
|
24
|
-
* variant?: string; // bindable (VARIANT.NEUTRAL)
|
|
25
|
-
* styling?: SVSClass;
|
|
26
24
|
* element?: HTMLDialogElement; // bindable
|
|
25
|
+
* styling?: SVSClass;
|
|
26
|
+
* variant?: string; // bindable (VARIANT.NEUTRAL)
|
|
27
27
|
* }
|
|
28
28
|
* ```
|
|
29
29
|
*/
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
interface ProgressTrackerProps {
|
|
6
6
|
current: number; // bindable (0)
|
|
7
7
|
labels: string[];
|
|
8
|
-
aux?: Snippet<[
|
|
9
|
-
extra?: Snippet<[
|
|
8
|
+
aux?: Snippet<[number, string]>; // Snippet<[index,variant]>
|
|
9
|
+
extra?: Snippet<[number, string]>; // Snippet<[index,variant]>
|
|
10
|
+
styling?: SVSClass;
|
|
10
11
|
variant?: string; // bindable (VARIANT.NEUTRAL)
|
|
11
12
|
eachVariant?: SvelteMap<number, string> | Map<number, string>;
|
|
12
|
-
styling?: SVSClass;
|
|
13
13
|
}
|
|
14
14
|
```
|
|
15
15
|
-->
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
if (!isUnsignedInteger(current)) current = 0;
|
|
42
42
|
const cls = fnClass(preset, styling);
|
|
43
43
|
|
|
44
|
-
// ***
|
|
45
|
-
function
|
|
44
|
+
// *** States *** //
|
|
45
|
+
function getEachVariant(index: number): string {
|
|
46
46
|
if (eachVariant?.has(index)) return eachVariant.get(index)!;
|
|
47
47
|
if (index < current) return VARIANT.ACTIVE;
|
|
48
48
|
if (index > current) return VARIANT.INACTIVE;
|
|
@@ -55,19 +55,25 @@
|
|
|
55
55
|
{#if labels.length}
|
|
56
56
|
<ol class={cls(PARTS.WHOLE, variant)}>
|
|
57
57
|
{#each labels as label, i}
|
|
58
|
-
{@const stat =
|
|
59
|
-
{#if i > current}
|
|
60
|
-
{@render separator(i)}
|
|
61
|
-
{/if}
|
|
58
|
+
{@const stat = getEachVariant(i)}
|
|
62
59
|
<li class={cls(PARTS.MAIN, stat)} aria-current={i === current ? "step" : false}>
|
|
60
|
+
{#if i > current}
|
|
61
|
+
{@render separator(i)}
|
|
62
|
+
{/if}
|
|
63
63
|
{#if aux}
|
|
64
|
-
<div class={cls(PARTS.
|
|
65
|
-
{
|
|
64
|
+
<div class={cls(PARTS.MIDDLE, stat)}>
|
|
65
|
+
<div class={cls(PARTS.AUX, stat)}>
|
|
66
|
+
{@render aux(i, stat)}
|
|
67
|
+
</div>
|
|
68
|
+
<div class={cls(PARTS.LABEL, stat)}>
|
|
69
|
+
{label}
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
{:else}
|
|
73
|
+
<div class={cls(PARTS.LABEL, stat)}>
|
|
74
|
+
{label}
|
|
66
75
|
</div>
|
|
67
76
|
{/if}
|
|
68
|
-
<div class={cls(PARTS.LABEL, stat)}>
|
|
69
|
-
{label}
|
|
70
|
-
</div>
|
|
71
77
|
</li>
|
|
72
78
|
{#if i < current}
|
|
73
79
|
{@render separator(i)}
|
|
@@ -79,8 +85,8 @@
|
|
|
79
85
|
{#snippet separator(i: number)}
|
|
80
86
|
{#if extra}
|
|
81
87
|
{@const stat = i < current ? VARIANT.ACTIVE : VARIANT.INACTIVE}
|
|
82
|
-
<
|
|
88
|
+
<div class={cls(PARTS.EXTRA, stat)} role="separator">
|
|
83
89
|
{@render extra(i, stat)}
|
|
84
|
-
</
|
|
90
|
+
</div>
|
|
85
91
|
{/if}
|
|
86
92
|
{/snippet}
|
|
@@ -18,11 +18,11 @@ import { type SVSClass } from "./core";
|
|
|
18
18
|
* interface ProgressTrackerProps {
|
|
19
19
|
* current: number; // bindable (0)
|
|
20
20
|
* labels: string[];
|
|
21
|
-
* aux?: Snippet<[
|
|
22
|
-
* extra?: Snippet<[
|
|
21
|
+
* aux?: Snippet<[number, string]>; // Snippet<[index,variant]>
|
|
22
|
+
* extra?: Snippet<[number, string]>; // Snippet<[index,variant]>
|
|
23
|
+
* styling?: SVSClass;
|
|
23
24
|
* variant?: string; // bindable (VARIANT.NEUTRAL)
|
|
24
25
|
* eachVariant?: SvelteMap<number, string> | Map<number, string>;
|
|
25
|
-
* styling?: SVSClass;
|
|
26
26
|
* }
|
|
27
27
|
* ```
|
|
28
28
|
*/
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
const attrs = omit(attributes, "class", "id", "value", "oninvalid");
|
|
67
67
|
let message = $state(bottom);
|
|
68
68
|
|
|
69
|
-
// ***
|
|
69
|
+
// *** States *** //
|
|
70
70
|
let neutral = isNeutral(variant) ? variant : VARIANT.NEUTRAL;
|
|
71
71
|
$effect(() => { neutral = isNeutral(variant) ? variant : neutral; });
|
|
72
72
|
let live = $derived(variant === VARIANT.INACTIVE ? "alert" : "status");
|
|
@@ -130,12 +130,12 @@
|
|
|
130
130
|
|
|
131
131
|
{#snippet lbl()}
|
|
132
132
|
{#if label?.trim()}
|
|
133
|
-
<
|
|
133
|
+
<label class={cls(PARTS.LABEL, variant)} for={id} id={idLabel}>
|
|
134
134
|
{label}
|
|
135
135
|
{#if extra?.trim()}
|
|
136
136
|
<span class={cls(PARTS.EXTRA, variant)}>{extra}</span>
|
|
137
137
|
{/if}
|
|
138
|
-
</
|
|
138
|
+
</label>
|
|
139
139
|
{/if}
|
|
140
140
|
{/snippet}
|
|
141
141
|
{#snippet side(area: string, body?: Snippet<[string, string, HTMLSelectElement | undefined]>)}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
```ts
|
|
5
5
|
interface TagsInputProps {
|
|
6
6
|
label?: Snippet<[string, string]>; // Snippet<[value,variant]>
|
|
7
|
-
|
|
7
|
+
extra?: Snippet<[string, string]>; // Snippet<[value,variant]>
|
|
8
8
|
values?: string[]; // bindable
|
|
9
9
|
value?: string; // bindable
|
|
10
10
|
type?: "left" | "right"; // ("left")
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
<script module lang="ts">
|
|
29
29
|
export interface TagsInputProps {
|
|
30
30
|
label?: Snippet<[string, string]>; // Snippet<[value,variant]>
|
|
31
|
-
|
|
31
|
+
extra?: Snippet<[string, string]>; // Snippet<[value,variant]>
|
|
32
32
|
values?: string[]; // bindable
|
|
33
33
|
value?: string; // bindable
|
|
34
34
|
type?: "left" | "right"; // ("left")
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
</script>
|
|
63
63
|
|
|
64
64
|
<script lang="ts">
|
|
65
|
-
let { label,
|
|
65
|
+
let { label, extra, values = $bindable([]), value = $bindable(""), type = "left", confirm = [], trim = true, unique = true, ariaErrMsgId = $bindable(), events, attributes, action, element = $bindable(), styling, variant = $bindable("") }: TagsInputProps = $props();
|
|
66
66
|
|
|
67
67
|
// *** Initialize *** //
|
|
68
68
|
if (!variant) variant = VARIANT.NEUTRAL;
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
|
|
109
109
|
{#snippet tags(render: boolean)}
|
|
110
110
|
{#if render}
|
|
111
|
-
<span class={cls(
|
|
111
|
+
<span class={cls(PARTS.AUX, variant)}>
|
|
112
112
|
{#each values as value, i}
|
|
113
113
|
<span class={cls(PARTS.LABEL, variant)}>
|
|
114
114
|
{#if label}
|
|
@@ -116,9 +116,9 @@
|
|
|
116
116
|
{:else}
|
|
117
117
|
{value}
|
|
118
118
|
{/if}
|
|
119
|
-
<button class={cls(PARTS.
|
|
120
|
-
{#if
|
|
121
|
-
{@render
|
|
119
|
+
<button class={cls(PARTS.EXTRA, variant)} onclick={remove(i)}>
|
|
120
|
+
{#if extra}
|
|
121
|
+
{@render extra(value, variant)}
|
|
122
122
|
{:else}
|
|
123
123
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="10" height="10"><path d="M511.998 70.682 441.315 0 256.002 185.313 70.685 0 .002 70.692l185.314 185.314L.002 441.318 70.69 512l185.312-185.312L441.315 512l70.683-70.682-185.314-185.312z" /></svg>
|
|
124
124
|
{/if}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export interface TagsInputProps {
|
|
2
2
|
label?: Snippet<[string, string]>;
|
|
3
|
-
|
|
3
|
+
extra?: Snippet<[string, string]>;
|
|
4
4
|
values?: string[];
|
|
5
5
|
value?: string;
|
|
6
6
|
type?: "left" | "right";
|
|
@@ -30,7 +30,7 @@ import { type SVSClass } from "./core";
|
|
|
30
30
|
* ```ts
|
|
31
31
|
* interface TagsInputProps {
|
|
32
32
|
* label?: Snippet<[string, string]>; // Snippet<[value,variant]>
|
|
33
|
-
*
|
|
33
|
+
* extra?: Snippet<[string, string]>; // Snippet<[value,variant]>
|
|
34
34
|
* values?: string[]; // bindable
|
|
35
35
|
* value?: string; // bindable
|
|
36
36
|
* type?: "left" | "right"; // ("left")
|
|
@@ -5,20 +5,20 @@
|
|
|
5
5
|
interface TextFieldProps {
|
|
6
6
|
label?: string;
|
|
7
7
|
extra?: string;
|
|
8
|
-
aux?: Snippet<[string, string, HTMLInputElement | HTMLTextAreaElement | undefined]>; // Snippet<[variant,
|
|
9
|
-
left?: Snippet<[string, string, HTMLInputElement | HTMLTextAreaElement | undefined]>; // Snippet<[variant,
|
|
10
|
-
right?: Snippet<[string, string, HTMLInputElement | HTMLTextAreaElement | undefined]>; // Snippet<[variant,
|
|
8
|
+
aux?: Snippet<[string, string, HTMLInputElement | HTMLTextAreaElement | undefined]>; // Snippet<[value,variant,element]>
|
|
9
|
+
left?: Snippet<[string, string, HTMLInputElement | HTMLTextAreaElement | undefined]>; // Snippet<[value,variant,element]>
|
|
10
|
+
right?: Snippet<[string, string, HTMLInputElement | HTMLTextAreaElement | undefined]>; // Snippet<[value,variant,element]>
|
|
11
11
|
bottom?: string;
|
|
12
12
|
descFirst?: boolean; // (false)
|
|
13
13
|
value?: string; // bindable
|
|
14
14
|
type?: "text" | "area" | "email" | "password" | "search" | "tel" | "url"; // bindable ("text")
|
|
15
15
|
options?: SvelteSet<string> | Set<string>;
|
|
16
16
|
validations?: TextFieldValidation[];
|
|
17
|
-
variant?: string; // bindable (VARIANT.NEUTRAL)
|
|
18
|
-
styling?: SVSClass;
|
|
19
17
|
attributes?: HTMLInputAttributes | HTMLTextareaAttributes;
|
|
20
18
|
action?: Action;
|
|
21
19
|
element?: HTMLInputElement | HTMLTextAreaElement; // bindable
|
|
20
|
+
styling?: SVSClass;
|
|
21
|
+
variant?: string; // bindable (VARIANT.NEUTRAL)
|
|
22
22
|
}
|
|
23
23
|
type TextFieldValidation = (value: string, validity: ValidityState) => string | undefined;
|
|
24
24
|
```
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
const attrs = omit(attributes as any, "class", "id", "type", "value", "list", "onchange", "oninvalid");
|
|
70
70
|
let message = $state(bottom);
|
|
71
71
|
|
|
72
|
-
// ***
|
|
72
|
+
// *** States *** //
|
|
73
73
|
let neutral = isNeutral(variant) ? variant : VARIANT.NEUTRAL;
|
|
74
74
|
$effect(() => { neutral = isNeutral(variant) ? variant : neutral });
|
|
75
75
|
let live = $derived(variant === VARIANT.INACTIVE ? "alert" : "status");
|
|
@@ -30,20 +30,20 @@ import { type SVSClass } from "./core";
|
|
|
30
30
|
* interface TextFieldProps {
|
|
31
31
|
* label?: string;
|
|
32
32
|
* extra?: string;
|
|
33
|
-
* aux?: Snippet<[string, string, HTMLInputElement | HTMLTextAreaElement | undefined]>; // Snippet<[variant,
|
|
34
|
-
* left?: Snippet<[string, string, HTMLInputElement | HTMLTextAreaElement | undefined]>; // Snippet<[variant,
|
|
35
|
-
* right?: Snippet<[string, string, HTMLInputElement | HTMLTextAreaElement | undefined]>; // Snippet<[variant,
|
|
33
|
+
* aux?: Snippet<[string, string, HTMLInputElement | HTMLTextAreaElement | undefined]>; // Snippet<[value,variant,element]>
|
|
34
|
+
* left?: Snippet<[string, string, HTMLInputElement | HTMLTextAreaElement | undefined]>; // Snippet<[value,variant,element]>
|
|
35
|
+
* right?: Snippet<[string, string, HTMLInputElement | HTMLTextAreaElement | undefined]>; // Snippet<[value,variant,element]>
|
|
36
36
|
* bottom?: string;
|
|
37
37
|
* descFirst?: boolean; // (false)
|
|
38
38
|
* value?: string; // bindable
|
|
39
39
|
* type?: "text" | "area" | "email" | "password" | "search" | "tel" | "url"; // bindable ("text")
|
|
40
40
|
* options?: SvelteSet<string> | Set<string>;
|
|
41
41
|
* validations?: TextFieldValidation[];
|
|
42
|
-
* variant?: string; // bindable (VARIANT.NEUTRAL)
|
|
43
|
-
* styling?: SVSClass;
|
|
44
42
|
* attributes?: HTMLInputAttributes | HTMLTextareaAttributes;
|
|
45
43
|
* action?: Action;
|
|
46
44
|
* element?: HTMLInputElement | HTMLTextAreaElement; // bindable
|
|
45
|
+
* styling?: SVSClass;
|
|
46
|
+
* variant?: string; // bindable (VARIANT.NEUTRAL)
|
|
47
47
|
* }
|
|
48
48
|
* type TextFieldValidation = (value: string, validity: ValidityState) => string | undefined;
|
|
49
49
|
* ```
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
action?: Action;
|
|
12
12
|
styling?: SVSClass;
|
|
13
13
|
variant?: string; // bindable (VARIANT.NEUTRAL)
|
|
14
|
-
[key: string]: unknown | Snippet<[string]>; // contents instead of the
|
|
14
|
+
[key: string]: unknown | Snippet<[string]>; // contents instead of the text
|
|
15
15
|
}
|
|
16
16
|
```
|
|
17
17
|
-->
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
action?: Action;
|
|
26
26
|
styling?: SVSClass;
|
|
27
27
|
variant?: string; // bindable (VARIANT.NEUTRAL)
|
|
28
|
-
[key: string]: unknown | Snippet<[string]>; // contents instead of the
|
|
28
|
+
[key: string]: unknown | Snippet<[string]>; // contents instead of the text
|
|
29
29
|
}
|
|
30
30
|
export type ToggleGroupReqdProps = "options";
|
|
31
31
|
export type ToggleGroupBindProps = "values" | "ariaErrMsgId" | "variant";
|
|
@@ -27,7 +27,7 @@ import { type SVSClass } from "./core";
|
|
|
27
27
|
* action?: Action;
|
|
28
28
|
* styling?: SVSClass;
|
|
29
29
|
* variant?: string; // bindable (VARIANT.NEUTRAL)
|
|
30
|
-
* [key: string]: unknown | Snippet<[string]>; // contents instead of the
|
|
30
|
+
* [key: string]: unknown | Snippet<[string]>; // contents instead of the text
|
|
31
31
|
* }
|
|
32
32
|
* ```
|
|
33
33
|
*/
|