svseeds 0.0.7 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +2 -2
  2. package/_svseeds/Accordion.svelte +91 -0
  3. package/_svseeds/Accordion.svelte.d.ts +19 -0
  4. package/_svseeds/DarkToggle.svelte +58 -0
  5. package/_svseeds/DarkToggle.svelte.d.ts +15 -0
  6. package/_svseeds/TagsInput.svelte +136 -0
  7. package/_svseeds/TagsInput.svelte.d.ts +31 -0
  8. package/_svseeds/_Badge.svelte +58 -0
  9. package/_svseeds/_Badge.svelte.d.ts +18 -0
  10. package/_svseeds/_Button.svelte +64 -0
  11. package/_svseeds/_Button.svelte.d.ts +22 -0
  12. package/_svseeds/_CheckField.svelte +142 -0
  13. package/_svseeds/_CheckField.svelte.d.ts +26 -0
  14. package/_svseeds/_ColorPicker.svelte +74 -0
  15. package/_svseeds/_ColorPicker.svelte.d.ts +19 -0
  16. package/_svseeds/_ComboBox.svelte +115 -0
  17. package/_svseeds/_ComboBox.svelte.d.ts +19 -0
  18. package/_svseeds/_ContextMenu.svelte +52 -0
  19. package/_svseeds/_ContextMenu.svelte.d.ts +15 -0
  20. package/_svseeds/_Disclosure.svelte +119 -0
  21. package/_svseeds/_Disclosure.svelte.d.ts +20 -0
  22. package/_svseeds/_HotkeyCapture.svelte +94 -0
  23. package/_svseeds/_HotkeyCapture.svelte.d.ts +15 -0
  24. package/_svseeds/_Modal.svelte +67 -0
  25. package/_svseeds/_Modal.svelte.d.ts +17 -0
  26. package/_svseeds/_ProgressTracker.svelte +71 -0
  27. package/_svseeds/_ProgressTracker.svelte.d.ts +17 -0
  28. package/_svseeds/_SelectField.svelte +147 -0
  29. package/_svseeds/_SelectField.svelte.d.ts +27 -0
  30. package/_svseeds/_Slider.svelte +69 -0
  31. package/_svseeds/_Slider.svelte.d.ts +28 -0
  32. package/_svseeds/_Sortable.svelte +746 -0
  33. package/_svseeds/_Sortable.svelte.d.ts +62 -0
  34. package/_svseeds/_Tabs.svelte +91 -0
  35. package/_svseeds/_Tabs.svelte.d.ts +15 -0
  36. package/_svseeds/_TextField.svelte +84 -69
  37. package/_svseeds/_TextField.svelte.d.ts +12 -8
  38. package/_svseeds/_Toggle.svelte +104 -0
  39. package/_svseeds/_Toggle.svelte.d.ts +22 -0
  40. package/_svseeds/_TogglesField.svelte +148 -0
  41. package/_svseeds/_TogglesField.svelte.d.ts +26 -0
  42. package/_svseeds/_Tooltip.svelte +267 -0
  43. package/_svseeds/_Tooltip.svelte.d.ts +31 -0
  44. package/_svseeds/core.d.ts +7 -10
  45. package/_svseeds/core.js +1 -1
  46. package/index.d.ts +22 -1
  47. package/index.js +21 -0
  48. package/package.json +3 -1
  49. package/_svseeds/core.ts +0 -195
  50. package/_svseeds/dep.json +0 -1
@@ -0,0 +1,26 @@
1
+ export type CheckFieldProps = {
2
+ options: SvelteMap<string, string> | Map<string, string>;
3
+ label?: string;
4
+ extra?: string;
5
+ aux?: Snippet<[string, string[], HTMLInputElement[] | undefined]>;
6
+ bottom?: string;
7
+ values?: string[];
8
+ multiple?: boolean;
9
+ descFirst?: boolean;
10
+ validations?: ((values: string[], validities?: ValidityState[]) => string)[];
11
+ status?: string;
12
+ style?: ClassRuleSet | string;
13
+ attributes?: HTMLInputAttributes;
14
+ action?: Action;
15
+ elements?: HTMLInputElement[];
16
+ };
17
+ export type CheckFieldReqdProps = "options";
18
+ export type CheckFieldBindProps = "bottom" | "values" | "status" | "elements";
19
+ import { type Snippet } from "svelte";
20
+ import { type Action } from "svelte/action";
21
+ import { type SvelteMap } from "svelte/reactivity";
22
+ import { type HTMLInputAttributes } from "svelte/elements";
23
+ import { type ClassRuleSet } from "./core";
24
+ declare const CheckField: import("svelte").Component<CheckFieldProps, {}, "bottom" | "elements" | "status" | "values">;
25
+ type CheckField = ReturnType<typeof CheckField>;
26
+ export default CheckField;
@@ -0,0 +1,74 @@
1
+ <script module lang="ts">
2
+ export type ColorPickerProps = {
3
+ value?: string, // bindable <"#000">
4
+ alpha?: number, // bindable <1>
5
+ status?: string, // bindable <STATE.DEFAULT>
6
+ style?: ClassRuleSet | string,
7
+ attributes?: HTMLInputAttributes;
8
+ action?: Action,
9
+ element?: HTMLInputElement, // bindable
10
+ };
11
+ export type ColorPickerReqdProps = never;
12
+ export type ColorPickerBindProps = "value" | "alpha" | "status" | "element";
13
+ export function getHex(rgb: RgbColor): string {
14
+ if (!isValidRgb(rgb)) return "#000";
15
+ return "#" + (1 << 24 | rgb[0] << 16 | rgb[1] << 8 | rgb[2]).toString(16).slice(1);
16
+ }
17
+
18
+ type RgbColor = [number, number, number];
19
+ const svs = "svs-color-picker";
20
+ const preset: ClassRuleSet = {};
21
+
22
+ function isValidHex(hex: string): boolean {
23
+ return Boolean(formatHex(hex));
24
+ }
25
+ function castHexToRgb(hex: string): RgbColor {
26
+ const rgb = formatHex(hex)?.filter((x) => x.length === 2);
27
+ return rgb ? rgb.map((x) => Number.parseInt(x, 16)) as RgbColor : [0, 0, 0];
28
+ }
29
+ function formatHex(hex: string): RegExpExecArray | null {
30
+ if (hex.startsWith("#")) hex = hex.substring(1);
31
+ hex = hex.replace(/^([a-f\d])([a-f\d])([a-f\d])$/i, (_, r, g, b) => r+r + g+g + b+b);
32
+ return /^([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
33
+ }
34
+ function isValidRgb(rgb: RgbColor): boolean {
35
+ return rgb.every((x) => x >= 0 && x <= 255);
36
+ }
37
+ function correctAlpha(alpha: number): number {
38
+ if (alpha <= 0) return 0;
39
+ if (alpha >= 1) return 1;
40
+ return alpha;
41
+ }
42
+
43
+ import { type Action } from "svelte/action";
44
+ import { type HTMLInputAttributes } from "svelte/elements";
45
+ import { type ClassRuleSet, STATE, AREA, fnClass, omit } from "./core";
46
+ </script>
47
+
48
+ <script lang="ts">
49
+ let { value = $bindable("#000"), alpha = $bindable(1), status = $bindable(""), style, attributes, action, element = $bindable() }: ColorPickerProps = $props();
50
+
51
+ // *** Initialize *** //
52
+ if (!status) status = STATE.DEFAULT;
53
+ const cls = fnClass(svs, preset, style);
54
+ const attrs = omit(attributes, "type");
55
+ if (!isValidHex(value)) value = "#000";
56
+
57
+ // *** Bind Handlers *** //
58
+ let rgb = $derived(castHexToRgb(value));
59
+ let alp = $derived(correctAlpha(alpha));
60
+ </script>
61
+
62
+ <!---------------------------------------->
63
+
64
+ <label class={cls(AREA.WHOLE, status)}>
65
+ <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;">
66
+ <div class={cls(AREA.MAIN, status)} style={`background-color: rgba(${rgb[0]},${rgb[1]},${rgb[2]},${alp})`}>
67
+ {#if action}
68
+ <input bind:value bind:this={element} type="color" style="visibility: hidden;" {...attrs} use:action />
69
+ {:else}
70
+ <input bind:value bind:this={element} type="color" style="visibility: hidden;" {...attrs} />
71
+ {/if}
72
+ </div>
73
+ </div>
74
+ </label>
@@ -0,0 +1,19 @@
1
+ export type ColorPickerProps = {
2
+ value?: string;
3
+ alpha?: number;
4
+ status?: string;
5
+ style?: ClassRuleSet | string;
6
+ attributes?: HTMLInputAttributes;
7
+ action?: Action;
8
+ element?: HTMLInputElement;
9
+ };
10
+ export type ColorPickerReqdProps = never;
11
+ export type ColorPickerBindProps = "value" | "alpha" | "status" | "element";
12
+ export declare function getHex(rgb: RgbColor): string;
13
+ type RgbColor = [number, number, number];
14
+ import { type Action } from "svelte/action";
15
+ import { type HTMLInputAttributes } from "svelte/elements";
16
+ import { type ClassRuleSet } from "./core";
17
+ declare const ColorPicker: import("svelte").Component<ColorPickerProps, {}, "alpha" | "value" | "status" | "element">;
18
+ type ColorPicker = ReturnType<typeof ColorPicker>;
19
+ export default ColorPicker;
@@ -0,0 +1,115 @@
1
+ <script module lang="ts">
2
+ export type ComboBoxProps = {
3
+ options: SvelteSet<string> | Set<string>,
4
+ value?: string, // bindable
5
+ expanded?: boolean, // bindable
6
+ status?: string, // bindable <STATE.DEFAULT>
7
+ style?: ClassRuleSet | string,
8
+ attributes?: HTMLInputAttributes,
9
+ action?: Action,
10
+ element?: HTMLInputElement, // bindable
11
+ };
12
+ export type ComboBoxReqdProps = "options";
13
+ export type ComboBoxBindProps = "value" | "expanded" | "status" | "element";
14
+
15
+ const svs = "svs-combo-box";
16
+ const preset: ClassRuleSet = {};
17
+ const NA = -1;
18
+ const optionStyle = "cursor: default; user-select: none;";
19
+
20
+ import { untrack } from "svelte";
21
+ import { type Action } from "svelte/action";
22
+ import { type SvelteSet } from "svelte/reactivity";
23
+ import { type HTMLInputAttributes } from "svelte/elements";
24
+ import { type ClassRuleSet, STATE, AREA, elemId, fnClass, omit } from "./core";
25
+ </script>
26
+
27
+ <script lang="ts">
28
+ let { options, value = $bindable(""), expanded = $bindable(false), status = $bindable(""), style, attributes, action, element = $bindable() }: ComboBoxProps = $props();
29
+
30
+ // *** Initialize *** //
31
+ if (!status) status = STATE.DEFAULT;
32
+ const cls = fnClass(svs, preset, style);
33
+ const idList = elemId.get(true);
34
+ const attrs = omit(attributes, "class", "type", "value", "list", "role", "aria-haspopup", "aria-autocomplete", "aria-controls", "aria-expanded");
35
+ let selected = $state(NA);
36
+ let overflow = $state({x: false, y: false});
37
+ let listElem: HTMLUListElement | undefined = $state();
38
+
39
+ // *** Bind Handlers *** //
40
+ let listboxStyle = $derived(`position: absolute;visibility: ${expanded ? "visible" : "hidden"};${overflow.x ? "right: 0%;" : ""}${overflow.y ? "bottom: 100%;" : ""}`);
41
+ let opts = $derived([...options.keys()]);
42
+ $effect(() => { options;
43
+ untrack(() => observeOverflow());
44
+ });
45
+ function observeOverflow() {
46
+ if (!listElem || !window) return;
47
+ const rect = listElem.getBoundingClientRect();
48
+ overflow.x = window.innerWidth < rect.right;
49
+ overflow.y = window.innerHeight < rect.bottom;
50
+ }
51
+
52
+ // *** Event Handlers *** //
53
+ function open(activate: boolean = false, bottom: boolean = false) {
54
+ if (expanded) return;
55
+ selected = opts.indexOf(value);
56
+ if (activate && selected === NA) selected = bottom ? opts.length-- : 0;
57
+ expanded = true;
58
+ }
59
+ function apply() {
60
+ if (!expanded) return;
61
+ value = opts[selected];
62
+ if (element) element.selectionStart = value.length;
63
+ close();
64
+ }
65
+ function close() {
66
+ expanded = false;
67
+ selected = NA;
68
+ }
69
+ function onkeydown(ev: KeyboardEvent) {
70
+ if (ev.isComposing) return;
71
+ if (ev.ctrlKey || ev.shiftKey || ev.metaKey) return;
72
+ switch (ev.key) {
73
+ case "Escape": if (!ev.altKey && expanded) close(); break;
74
+ case "Enter": if (!ev.altKey && expanded && selected > NA) apply(); break;
75
+ case "ArrowDown": caseArrowDown(ev.altKey); break;
76
+ case "ArrowUp": caseArrowUp(ev.altKey); break;
77
+ }
78
+ }
79
+ function caseArrowDown(alt: boolean) {
80
+ if (alt && !expanded) return open();
81
+ if (alt && expanded) return;
82
+ if (!alt && !expanded) return open(true);
83
+ if (selected + 1 < opts.length) selected++;
84
+ }
85
+ function caseArrowUp(alt: boolean) {
86
+ if (alt && !expanded) return;
87
+ if (alt && expanded) return close();
88
+ if (!alt && !expanded) return open(true, true);
89
+ if (selected - 1 >= 0) selected--;
90
+ }
91
+ </script>
92
+
93
+ <!---------------------------------------->
94
+
95
+ {#if options.size}
96
+ <div class={cls(AREA.WHOLE, status)}>
97
+ <div style="position: relative; width: fit-content; height: fit-content;">
98
+ {#if action}
99
+ <input bind:value bind:this={element} class={cls(AREA.MAIN, status)} type="text" role="combobox" aria-haspopup="listbox" aria-autocomplete="none" aria-controls={idList} aria-expanded={expanded} onfocus={() => open()} onblur={close} {onkeydown} {...attrs} use:action />
100
+ {:else}
101
+ <input bind:value bind:this={element} class={cls(AREA.MAIN, status)} type="text" role="combobox" aria-haspopup="listbox" aria-autocomplete="none" aria-controls={idList} aria-expanded={expanded} onfocus={() => open()} onblur={close} {onkeydown} {...attrs} />
102
+ {/if}
103
+ <ul bind:this={listElem} class={cls(AREA.BOTTOM, status)} id={idList} role="listbox" style={listboxStyle}>
104
+ {#each opts as opt, i (opt)}
105
+ {@const isSelected = i === selected}
106
+ {@const labelStatus = isSelected ? STATE.ACTIVE : status}
107
+ {@const onpointerenter = () => selected = i}
108
+ <li class={cls(AREA.LABEL, labelStatus)} aria-selected={isSelected} role="option" style={optionStyle} onpointerdown={apply} {onpointerenter}>
109
+ {opt}
110
+ </li>
111
+ {/each}
112
+ </ul>
113
+ </div>
114
+ </div>
115
+ {/if}
@@ -0,0 +1,19 @@
1
+ export type ComboBoxProps = {
2
+ options: SvelteSet<string> | Set<string>;
3
+ value?: string;
4
+ expanded?: boolean;
5
+ status?: string;
6
+ style?: ClassRuleSet | string;
7
+ attributes?: HTMLInputAttributes;
8
+ action?: Action;
9
+ element?: HTMLInputElement;
10
+ };
11
+ export type ComboBoxReqdProps = "options";
12
+ export type ComboBoxBindProps = "value" | "expanded" | "status" | "element";
13
+ import { type Action } from "svelte/action";
14
+ import { type SvelteSet } from "svelte/reactivity";
15
+ import { type HTMLInputAttributes } from "svelte/elements";
16
+ import { type ClassRuleSet } from "./core";
17
+ declare const ComboBox: import("svelte").Component<ComboBoxProps, {}, "expanded" | "value" | "status" | "element">;
18
+ type ComboBox = ReturnType<typeof ComboBox>;
19
+ export default ComboBox;
@@ -0,0 +1,52 @@
1
+ <script module lang="ts">
2
+ export type ContextMenuProps = {
3
+ children: Snippet,
4
+ open?: boolean, // bindable <false>; to observe state, not to control
5
+ lock?: boolean, // bindable <false>
6
+ status?: string, // bindable <STATE.DEFAULT>
7
+ style?: ClassRuleSet | string,
8
+ element?: HTMLElement, // bindable
9
+ };
10
+ export type ContextMenuReqdProps = "children";
11
+ export type ContextMenuBindProps = "open" | "lock" | "status" | "element";
12
+
13
+ const svs = "svs-context-menu";
14
+ const preset: ClassRuleSet = {};
15
+
16
+ import { type Snippet } from "svelte";
17
+ import { type ClassRuleSet, STATE, AREA, fnClass } from "./core";
18
+ </script>
19
+
20
+ <!---------------------------------------->
21
+
22
+ <script lang="ts">
23
+ let { children, open = $bindable(false), lock = $bindable(false), status = $bindable(""), style, element = $bindable() }: ContextMenuProps = $props();
24
+
25
+ // *** Initialize *** //
26
+ if (!status) status = STATE.DEFAULT;
27
+ const cls = fnClass(svs, preset, style);
28
+ let position = $state({ x: 0, y: 0 });
29
+
30
+ // *** Bind Handlers *** //
31
+ let visibility = $derived(open ? "visibility: open;" : "visibility: hidden; z-index: -9999;");
32
+ let dynStyle = $derived(`position: fixed; left:${position.x}px; top:${position.y}px; ${visibility}`);
33
+
34
+ // *** Event Handlers *** //
35
+ function show(ev: MouseEvent) {
36
+ if (lock) return;
37
+ ev.preventDefault();
38
+
39
+ const menu = { width: element?.offsetWidth ?? 0, height: element?.offsetHeight ?? 0 };
40
+ position.x = window.innerWidth-ev.clientX < menu.width ? ev.clientX-menu.width : ev.clientX;
41
+ position.y = window.innerHeight-ev.clientY < menu.height ? (ev.clientY < menu.height ? ev.clientY : ev.clientY-menu.height) : ev.clientY;
42
+ open = true;
43
+ }
44
+ function hide() { if (!lock) open = false; }
45
+ </script>
46
+
47
+ <!---------------------------------------->
48
+ <svelte:document oncontextmenu={show} onclick={hide} />
49
+
50
+ <nav class={cls(AREA.WHOLE, status)} style={dynStyle} bind:this={element}>
51
+ {@render children()}
52
+ </nav>
@@ -0,0 +1,15 @@
1
+ export type ContextMenuProps = {
2
+ children: Snippet;
3
+ open?: boolean;
4
+ lock?: boolean;
5
+ status?: string;
6
+ style?: ClassRuleSet | string;
7
+ element?: HTMLElement;
8
+ };
9
+ export type ContextMenuReqdProps = "children";
10
+ export type ContextMenuBindProps = "open" | "lock" | "status" | "element";
11
+ import { type Snippet } from "svelte";
12
+ import { type ClassRuleSet } from "./core";
13
+ declare const ContextMenu: import("svelte").Component<ContextMenuProps, {}, "open" | "status" | "element" | "lock">;
14
+ type ContextMenu = ReturnType<typeof ContextMenu>;
15
+ export default ContextMenu;
@@ -0,0 +1,119 @@
1
+ <script module lang="ts">
2
+ export type DisclosureProps = {
3
+ label: string | Snippet<[string]>, // Snippet<[status]>
4
+ children: Snippet,
5
+ open?: boolean, // bindable <false>
6
+ duration?: number, // <400>
7
+ status?: string, // bindable <STATE.DEFAULT>
8
+ style?: ClassRuleSet | string,
9
+ attributes?: HTMLDetailsAttributes;
10
+ action?: Action,
11
+ element?: HTMLDetailsElement, // bindable
12
+ };
13
+ export type DisclosureReqdProps = "label" | "children";
14
+ export type DisclosureBindProps = "open" | "status" | "elementent";
15
+
16
+ type DisclosureTarget = { currentTarget: EventTarget & HTMLDetailsElement };
17
+ const svs = "svs-disclosure";
18
+ const preset: ClassRuleSet = {};
19
+
20
+ const sleep = (msec: number) => new Promise(resolve => setTimeout(resolve, msec));
21
+ class ToggleGurad {
22
+ #active = false;
23
+ get active(): boolean {
24
+ return this.#active;
25
+ }
26
+ activate(duration: number) {
27
+ this.#active = true;
28
+ sleep(duration).then(() => this.#active = false);
29
+ }
30
+ }
31
+
32
+ import { type Snippet, untrack } from "svelte";
33
+ import { type Action } from "svelte/action";
34
+ import { type HTMLDetailsAttributes } from "svelte/elements";
35
+ import { slide } from "svelte/transition";
36
+ import { type ClassRuleSet, STATE, AREA, fnClass, isNeutral, omit } from "./core";
37
+ </script>
38
+
39
+ <script lang="ts">
40
+ let { label, children, open = $bindable(false), duration = 400, status = $bindable(""), style, attributes, action, element = $bindable() }: DisclosureProps = $props();
41
+
42
+ // *** Initialize *** //
43
+ if (!status) status = STATE.DEFAULT;
44
+ const cls = fnClass(svs, preset, style);
45
+ const attrs = omit(attributes, "class", "open", "ontoggle");
46
+ const guard = new ToggleGurad();
47
+ let hidden = $state(!open);
48
+ let neutral = isNeutral(status) ? status : STATE.DEFAULT;
49
+ const initOpen = () => { if (element) element.open = open };
50
+ $effect(() => untrack(() => initOpen()));
51
+
52
+ // *** Bind Handlers *** //
53
+ $effect(() => { neutral = isNeutral(status) ? status : neutral });
54
+ $effect.pre(() => {
55
+ open;
56
+ untrack(() => toggleOpen());
57
+ });
58
+ function toggleOpen() {
59
+ guard.activate(duration);
60
+ if (open) {
61
+ toggle(true);
62
+ } else {
63
+ sleep(duration).then(() => toggle(false));
64
+ }
65
+ }
66
+ function toggle(bool: boolean) {
67
+ if (!element) return;
68
+ element.open = bool;
69
+ hidden = !element.open;
70
+ status = bool ? STATE.ACTIVE : neutral;
71
+ }
72
+
73
+ // *** Event Handlers *** //
74
+ function onclick(ev: Event) {
75
+ ev.preventDefault();
76
+ if (guard.active) return;
77
+ if (!element?.open) hidden = false;
78
+ open = !open;
79
+ }
80
+ function ontoggle(ev: Event & DisclosureTarget) {
81
+ attributes?.ontoggle?.(ev);
82
+ if (element?.open && !open) {
83
+ hidden = false;
84
+ open = true;
85
+ }
86
+ }
87
+ </script>
88
+
89
+ <!---------------------------------------->
90
+
91
+ {#if action}
92
+ <details bind:this={element} class={cls(AREA.WHOLE, status)} {ontoggle} {...attrs} use:action>
93
+ {@render inner()}
94
+ </details>
95
+ {:else}
96
+ <details bind:this={element} class={cls(AREA.WHOLE, status)} {ontoggle} {...attrs}>
97
+ {@render inner()}
98
+ </details>
99
+ {/if}
100
+
101
+ {#snippet inner()}
102
+ <summary class={cls(AREA.LABEL, status)} {onclick}>
103
+ {#if typeof label === "string"}
104
+ {label}
105
+ {:else if typeof label === "function"}
106
+ {@render label(status)}
107
+ {/if}
108
+ </summary>
109
+ {#if open}
110
+ <div class={cls(AREA.MAIN, status)} transition:slide={{ duration }}>
111
+ {@render children()}
112
+ </div>
113
+ {/if}
114
+ {#if hidden}
115
+ <div class={cls(AREA.MAIN, status)}>
116
+ {@render children()}
117
+ </div>
118
+ {/if}
119
+ {/snippet}
@@ -0,0 +1,20 @@
1
+ export type DisclosureProps = {
2
+ label: string | Snippet<[string]>;
3
+ children: Snippet;
4
+ open?: boolean;
5
+ duration?: number;
6
+ status?: string;
7
+ style?: ClassRuleSet | string;
8
+ attributes?: HTMLDetailsAttributes;
9
+ action?: Action;
10
+ element?: HTMLDetailsElement;
11
+ };
12
+ export type DisclosureReqdProps = "label" | "children";
13
+ export type DisclosureBindProps = "open" | "status" | "elementent";
14
+ import { type Snippet } from "svelte";
15
+ import { type Action } from "svelte/action";
16
+ import { type HTMLDetailsAttributes } from "svelte/elements";
17
+ import { type ClassRuleSet } from "./core";
18
+ declare const Disclosure: import("svelte").Component<DisclosureProps, {}, "open" | "status" | "element">;
19
+ type Disclosure = ReturnType<typeof Disclosure>;
20
+ export default Disclosure;
@@ -0,0 +1,94 @@
1
+ <script module lang="ts">
2
+ export type HotkeyCaptureProps = {
3
+ value?: string, // bindable
4
+ placeholder?: string,
5
+ active?: boolean, // bindable, <false>
6
+ disabled?: boolean, // bindable, <false>
7
+ status?: string, // bindable <STATE.DEFAULT>
8
+ style?: ClassRuleSet | string,
9
+ element?: HTMLInputElement, // bindable
10
+ };
11
+ export type HotkeyCaptureReqdProps = never;
12
+ export type HotkeyCaptureBindProps = "value" | "active" | "disable" | "status" | "element";
13
+
14
+ const svs = "svs-hotkey-capture";
15
+ const preset: ClassRuleSet = {};
16
+ const KEY_MODIFIER = new Set(["Control", "Alt", "Shift", "Meta"]);
17
+ const LABEL_SPACE = "SPACE";
18
+ const LABEL_POINTER = ["BTN_MAIN","BTN_WHEEL","BTN_SUB","BTN_BACK","BTN_FORWARD"] as const;
19
+ const LABEL_WHEEL = ["WHEELUP", "WHEELDOWN"] as const;
20
+ function getModifierLabel(ev: KeyboardEvent | PointerEvent | WheelEvent): string {
21
+ return `${ev.ctrlKey ? "Ctrl " : ""}${ev.altKey ? "Alt " : ""}${ev.shiftKey ? "Shift " : ""}${ev.metaKey ? "Meta " : ""}`;
22
+ }
23
+
24
+ import { untrack } from "svelte";
25
+ import { type ClassRuleSet, STATE, AREA, fnClass, isNeutral } from "./core";
26
+ </script>
27
+
28
+ <script lang="ts">
29
+ let { value = $bindable(""), placeholder, active = $bindable(false), disabled = $bindable(false), status = $bindable(""), style, element = $bindable() }: HotkeyCaptureProps = $props();
30
+
31
+ // *** Initialize *** //
32
+ if (!status) status = STATE.DEFAULT;
33
+ const cls = fnClass(svs, preset, style);
34
+ let neutral = isNeutral(status) ? status : STATE.DEFAULT;
35
+
36
+ // *** Bind Handlers *** //
37
+ $effect(() => { neutral = isNeutral(status) ? status : neutral });
38
+ $effect.pre(() => {
39
+ disabled;
40
+ untrack(() => shiftStatus());
41
+ });
42
+ $effect.pre(() => {
43
+ active;
44
+ untrack(() => toggle());
45
+ });
46
+ function toggle() {
47
+ if (disabled) return;
48
+ shiftStatus();
49
+ if (active) {
50
+ element?.focus();
51
+ } else {
52
+ element?.blur();
53
+ }
54
+ }
55
+ function shiftStatus() {
56
+ if (disabled) return status = STATE.INACTIVE;
57
+ status = active ? STATE.ACTIVE : neutral;
58
+ }
59
+
60
+ // *** Event Handlers *** //
61
+ const ignore = () => disabled || window?.document.activeElement !== element;
62
+ function prep(ev: KeyboardEvent | PointerEvent | MouseEvent | WheelEvent) {
63
+ ev.preventDefault();
64
+ ev.stopPropagation();
65
+ }
66
+ function onkeydown(ev: KeyboardEvent) {
67
+ if (ev.repeat) return;
68
+ if (ignore()) return;
69
+ prep(ev);
70
+ if (KEY_MODIFIER.has(ev.key)) return;
71
+ value = getModifierLabel(ev) + (ev.key === " " ? LABEL_SPACE : ev.key.toUpperCase());
72
+ }
73
+ function onpointerdown(ev: PointerEvent) {
74
+ if (ignore()) return;
75
+ prep(ev);
76
+ value = getModifierLabel(ev) + LABEL_POINTER[ev.button];
77
+ }
78
+ function onwheel(ev: WheelEvent) {
79
+ if (ignore()) return;
80
+ prep(ev);
81
+ value = getModifierLabel(ev) + LABEL_WHEEL[ev.deltaY < 0 ? 0 : 1];
82
+ }
83
+ function oncontextmenu(ev: MouseEvent) { prep(ev); }
84
+ function onfocus() {
85
+ active = true;
86
+ }
87
+ function onblur() {
88
+ active = false;
89
+ }
90
+ </script>
91
+
92
+ <!---------------------------------------->
93
+
94
+ <input bind:value bind:this={element} class={cls(AREA.MAIN, status)} type="text" readonly={true} {placeholder} {disabled} {onkeydown} {onpointerdown} {onwheel} {oncontextmenu} {onfocus} {onblur} />
@@ -0,0 +1,15 @@
1
+ export type HotkeyCaptureProps = {
2
+ value?: string;
3
+ placeholder?: string;
4
+ active?: boolean;
5
+ disabled?: boolean;
6
+ status?: string;
7
+ style?: ClassRuleSet | string;
8
+ element?: HTMLInputElement;
9
+ };
10
+ export type HotkeyCaptureReqdProps = never;
11
+ export type HotkeyCaptureBindProps = "value" | "active" | "disable" | "status" | "element";
12
+ import { type ClassRuleSet } from "./core";
13
+ declare const HotkeyCapture: import("svelte").Component<HotkeyCaptureProps, {}, "active" | "value" | "disabled" | "status" | "element">;
14
+ type HotkeyCapture = ReturnType<typeof HotkeyCapture>;
15
+ export default HotkeyCapture;
@@ -0,0 +1,67 @@
1
+ <script module lang="ts">
2
+ export type ModalProps = {
3
+ children: Snippet,
4
+ label?: string, // for aria-label
5
+ open?: boolean, // bindable <false>
6
+ closable?: boolean, // <true>
7
+ trigger?: HTMLElement, // bindable
8
+ status?: string, // bindable <STATE.DEFAULT>
9
+ style?: ClassRuleSet | string,
10
+ element?: HTMLDialogElement, // bindable
11
+ };
12
+ export type ModalReqdProps = "children";
13
+ export type ModalBindProps = "open" | "status" | "element";
14
+
15
+ const svs = "svs-modal";
16
+ const preset: ClassRuleSet = {};
17
+
18
+ import { type Snippet, untrack } from "svelte";
19
+ import { type ClassRuleSet, STATE, AREA, fnClass } from "./core";
20
+ </script>
21
+
22
+ <script lang="ts">
23
+ let { children, label, open = $bindable(false), closable = true, trigger = $bindable(), status = $bindable(""), style, element = $bindable() }: ModalProps = $props();
24
+
25
+ // *** Initialize *** //
26
+ if (!status) status = STATE.DEFAULT;
27
+ const cls = fnClass(svs, preset, style);
28
+
29
+ // *** Bind Handlers *** //
30
+ $effect(() => {
31
+ open;
32
+ untrack(() => trigger?.focus());
33
+ });
34
+ $effect.pre(() => {
35
+ open;
36
+ untrack(() => toggle());
37
+ });
38
+ function toggle() {
39
+ if (open) {
40
+ element?.showModal();
41
+ } else {
42
+ element?.close();
43
+ }
44
+ }
45
+
46
+ // *** Event Handlers *** //
47
+ const onclick = closable ? (ev: MouseEvent) => { if (ev.target === element) open = false; } : undefined;
48
+ const onkeydown = closable ? (ev: KeyboardEvent) => { if (ev.key === "Escape") ev.preventDefault(); } : undefined;
49
+ function onclose() {
50
+ open = false;
51
+ }
52
+ </script>
53
+
54
+ <!---------------------------------------->
55
+
56
+ <!-- svelte-ignore a11y_autofocus -->
57
+ <dialog bind:this={element} class={cls(AREA.WHOLE, status)} aria-label={label} {onclick} {onkeydown} {onclose} autofocus={true}>
58
+ <div class={cls(AREA.MAIN, status)}>
59
+ {@render children()}
60
+ </div>
61
+ </dialog>
62
+
63
+ <style>
64
+ :global(html:has(dialog[open])) {
65
+ overflow: hidden;
66
+ }
67
+ </style>
@@ -0,0 +1,17 @@
1
+ export type ModalProps = {
2
+ children: Snippet;
3
+ label?: string;
4
+ open?: boolean;
5
+ closable?: boolean;
6
+ trigger?: HTMLElement;
7
+ status?: string;
8
+ style?: ClassRuleSet | string;
9
+ element?: HTMLDialogElement;
10
+ };
11
+ export type ModalReqdProps = "children";
12
+ export type ModalBindProps = "open" | "status" | "element";
13
+ import { type Snippet } from "svelte";
14
+ import { type ClassRuleSet } from "./core";
15
+ declare const Modal: import("svelte").Component<ModalProps, {}, "open" | "status" | "element" | "trigger">;
16
+ type Modal = ReturnType<typeof Modal>;
17
+ export default Modal;