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.
@@ -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
- // *** Status *** //
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
- // *** Status *** //
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
- {#each values as value}
167
- <input type="hidden" {name} {value} />
168
- {/each}
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
- // *** Status *** //
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]) => "#ff7b22"
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]) => "#ff7b22"
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 { untrack } from "svelte";
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: absolute;visibility: ${expanded ? "visible" : "hidden"};${overflow.x ? "right: 0%;" : ""}${overflow.y ? "bottom: 100%;" : ""}`);
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
- $effect(() => { options;
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
- <div class={cls(PARTS.WHOLE, variant)}>
114
- <div style="position: relative; width: fit-content; height: fit-content;">
115
- {#if action}
116
- <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} {...attrs} use:action />
117
- {:else}
118
- <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} {...attrs} />
119
- {/if}
120
- <ul bind:this={listElem} class={cls(PARTS.BOTTOM, variant)} id={idList} role="listbox" style={listboxStyle}>
121
- {#each opts as opt, i (opt)}
122
- {@const isSelected = i === selected}
123
- {@const labelStatus = isSelected ? VARIANT.ACTIVE : variant}
124
- {@const onpointerenter = () => selected = i}
125
- <li class={cls(PARTS.LABEL, labelStatus)} aria-selected={isSelected} role="option" style={optionStyle} onpointerdown={apply} {onpointerenter}>
126
- {opt}
127
- </li>
128
- {/each}
129
- </ul>
130
- </div>
131
- </div>
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 visibility = $derived(open ? "visibility: visible;" : "visibility: hidden; z-index: -9999;");
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: MouseEvent) {
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-ev.clientX < menu.width ? ev.clientX-menu.width : ev.clientX;
54
- position.y = window.innerHeight-ev.clientY < menu.height ? (ev.clientY < menu.height ? ev.clientY : ev.clientY-menu.height) : ev.clientY;
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 oncontextmenu={show} onclick={hide} />
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)
@@ -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(() => trigger?.focus());
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<[string, number]>; // Snippet<[variant,index]>
9
- extra?: Snippet<[string, number]>; // Snippet<[variant,index]>
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
- // *** Status *** //
45
- function getEachStatus(index: number): string {
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 = getEachStatus(i)}
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.AUX, stat)}>
65
- {@render aux(i, stat)}
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
- <li class={cls(PARTS.EXTRA, stat)} role="separator">
88
+ <div class={cls(PARTS.EXTRA, stat)} role="separator">
83
89
  {@render extra(i, stat)}
84
- </li>
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<[string, number]>; // Snippet<[variant,index]>
22
- * extra?: Snippet<[string, number]>; // Snippet<[variant,index]>
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
- // *** Status *** //
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
- <span class={cls(PARTS.LABEL, variant)} id={idLabel}>
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
- </span>
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
- aux?: Snippet<[string, string]>; // Snippet<[value,variant]>
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
- aux?: Snippet<[string, string]>; // Snippet<[value,variant]>
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, aux, values = $bindable([]), value = $bindable(""), type = "left", confirm = [], trim = true, unique = true, ariaErrMsgId = $bindable(), events, attributes, action, element = $bindable(), styling, variant = $bindable("") }: TagsInputProps = $props();
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(type, variant)}>
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.AUX, variant)} onclick={remove(i)}>
120
- {#if aux}
121
- {@render aux(value, variant)}
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
- aux?: Snippet<[string, string]>;
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
- * aux?: Snippet<[string, string]>; // Snippet<[value,variant]>
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,value,element]>
9
- left?: Snippet<[string, string, HTMLInputElement | HTMLTextAreaElement | undefined]>; // Snippet<[variant,value,element]>
10
- right?: Snippet<[string, string, HTMLInputElement | HTMLTextAreaElement | undefined]>; // Snippet<[variant,value,element]>
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
- // *** Status *** //
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,value,element]>
34
- * left?: Snippet<[string, string, HTMLInputElement | HTMLTextAreaElement | undefined]>; // Snippet<[variant,value,element]>
35
- * right?: Snippet<[string, string, HTMLInputElement | HTMLTextAreaElement | undefined]>; // Snippet<[variant,value,element]>
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 value
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 value
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 value
30
+ * [key: string]: unknown | Snippet<[string]>; // contents instead of the text
31
31
  * }
32
32
  * ```
33
33
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svseeds",
3
- "version": "0.4.8",
3
+ "version": "0.4.10",
4
4
  "description": "Simple components for Svelte.",
5
5
  "type": "module",
6
6
  "main": "./index.js",