svseeds 0.0.2 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,56 +1,5 @@
1
- # svseeds - the SvSeeds CLI
2
- A CLI to copy SvSeeds components for Svelte.
1
+ # Svelte components as svelte file
3
2
 
4
- ## Quick Start
5
- ```
6
- npx svseeds
7
- ```
3
+ Simple headless Svelte components as Svelte files.
8
4
 
9
- ## Basic Usage
10
- Copy specified SvSeeds files.
11
- ```
12
- npx svseeds [components...]
13
- ```
14
-
15
- ## Main Options
16
- - Specify directory:
17
- ```
18
- npx svseeds -d <directory> [components...]
19
- ```
20
-
21
- - Specify all components:
22
- ```
23
- npx svseeds -a
24
- ```
25
-
26
- - Update copied components:
27
- ```
28
- npx svseeds -u [components...]
29
- ```
30
-
31
- - Remove components:
32
- ```
33
- npx svseeds -r [components...]
34
- ```
35
- (Of course, the components are just a file, you can use `rm` command instead.)
36
-
37
- - Run without interactions:
38
- ```
39
- npx svseeds --no-confirm [components...]
40
- ```
41
-
42
- ## Other Options
43
- - Remove all components:
44
- ```
45
- npx svseeds --uninstall
46
- ```
47
-
48
- - Copy without overwrite
49
- ```
50
- npx svseeds --no-overwrite [components...]
51
- ```
52
-
53
- - Copy without `_style.ts` file
54
- ```
55
- npx svseeds --no-style [components...]
56
- ```
5
+ **WIP**
@@ -0,0 +1,144 @@
1
+ <script module lang="ts">
2
+ export type TextFieldProps = {
3
+ label?: string,
4
+ extra?: string,
5
+ aux?: Snippet<[string, StateName, HTMLInputElement | HTMLTextAreaElement | undefined]>, // Snippet<[value,status,element]>
6
+ left?: Snippet<[string, StateName, HTMLInputElement | HTMLTextAreaElement | undefined]>, // Snippet<[value,status,element]>
7
+ right?: Snippet<[string, StateName, HTMLInputElement | HTMLTextAreaElement | undefined]>, // Snippet<[value,status,element]>
8
+ bottom?: string, // bindable
9
+ value?: string, // bindable, [""]
10
+ type?: "text" | "area" | "email" | "password" | "search" | "tel" | "url" | "number", // bindable, ["text"]
11
+ options?: string[], // bindable
12
+ status?: StateName, // bindable, [STATE.DEFAULT]
13
+ validations?: ((value: string, validity?: ValidityState) => string)[],
14
+ style?: ClassRuleSet | string,
15
+ attributes?: HTMLInputAttributes & HTMLTextareaAttributes,
16
+ action?: Action,
17
+ element?: HTMLInputElement | HTMLTextAreaElement, // bindable
18
+ };
19
+
20
+ const seed = "text-field";
21
+ const preset: ClassRuleSet = {};
22
+
23
+ type TextFieldEvent = Event & { currentTarget: EventTarget & HTMLInputElement & HTMLTextAreaElement; };
24
+ import { type Snippet } from "svelte";
25
+ import { type Action } from "svelte/action";
26
+ import { type HTMLInputAttributes, type HTMLTextareaAttributes } from "svelte/elements";
27
+ import { type ClassRuleSet, type StateName, STATE, AREA, elemId, getClassFn, isUndef, omit } from "./core";
28
+ </script>
29
+
30
+ <script lang="ts">
31
+ let { label, extra, aux, left, right, bottom = $bindable(), value = $bindable(""), type = $bindable("text"), options = $bindable(), status = $bindable(STATE.DEFAULT), validations = [], style = {}, attributes = {}, action, element = $bindable() }: TextFieldProps = $props();
32
+
33
+ // *** Initialize *** //
34
+ const cls = getClassFn(seed, preset, style);
35
+ const id = !isUndef(attributes.id) ? attributes.id : elemId.get(label);
36
+ const idLabel = elemId.get(label?.trim());
37
+ const idDesc = elemId.get(bottom?.trim());
38
+ const idList = elemId.get(Array.isArray(options));
39
+ const idErr = isUndef(idDesc) ? elemId.get(true) : idDesc;
40
+ const attrs = omit({ ...attributes }, "class", "id", "type", "value", "list", "onchange", "oninput", "oninvalid");
41
+ const description = bottom;
42
+
43
+ // *** Status *** //
44
+ let role = $derived(isInvalid() ? "alert" : undefined);
45
+ let invalid = $derived(isInvalid() ? true : undefined);
46
+ let errMsg = $derived(isInvalid() ? idErr : undefined);
47
+ function isInvalid() { return status === STATE.INACTIVE && !isUndef(bottom); }
48
+ function toInvalid(msg?: string) { shiftStatus(STATE.INACTIVE, msg); }
49
+ function toNonInvalid(stat: StateName) { shiftStatus(stat); }
50
+ function shiftStatus(stat: StateName, msg?: string) {
51
+ status = stat;
52
+ bottom = msg ?? description;
53
+ element?.setCustomValidity(msg ?? "");
54
+ }
55
+ function validate(onchange?: boolean) {
56
+ if (!value && onchange) return toNonInvalid(STATE.DEFAULT);
57
+ for (let i = 0; i < validations.length; i++) {
58
+ const msg = validations[i](value, element?.validity);
59
+ if (msg) return toInvalid(msg);
60
+ }
61
+ toNonInvalid(STATE.ACTIVE);
62
+ }
63
+
64
+ // *** Event Handlers *** //
65
+ // const debounceValidate = debounce(300, validate);
66
+ function onchange(ev: Event) {
67
+ attributes.onchange?.(ev as TextFieldEvent);
68
+ validate(true);
69
+ }
70
+ function oninput(ev: Event) {
71
+ attributes.oninput?.(ev as TextFieldEvent);
72
+ if (status === STATE.DEFAULT) return;
73
+ validate(); // debounceValidate();
74
+ }
75
+ function oninvalid(ev: Event) {
76
+ attributes.oninvalid?.(ev as TextFieldEvent);
77
+ validate();
78
+ if (status !== STATE.INACTIVE) toInvalid(element?.validationMessage);
79
+ }
80
+ </script>
81
+
82
+ <!---------------------------------------->
83
+
84
+ <div class={cls(AREA.WHOLE, status)} role="group" aria-labelledby={idLabel}>
85
+ {#if typeof aux === "function"}
86
+ <div class={cls(AREA.TOP, status)}>
87
+ {@render lbl()}
88
+ {#if typeof aux === "function"}
89
+ <span class={cls(AREA.AUX, status)}>{@render aux(value, status, element)}</span>
90
+ {/if}
91
+ </div>
92
+ {:else}
93
+ {@render lbl()}
94
+ {/if}
95
+ {#if typeof left === "function" || typeof right === "function"}
96
+ <div class={cls(AREA.MIDDLE, status)}>
97
+ {#if typeof left === "function"}
98
+ <span class={cls(AREA.LEFT, status)}>{@render left(value, status, element)}</span>
99
+ {/if}
100
+ {@render main()}
101
+ {#if typeof right === "function"}
102
+ <span class={cls(AREA.RIGHT, status)}>{@render right(value, status, element)}</span>
103
+ {/if}
104
+ </div>
105
+ {:else}
106
+ {@render main()}
107
+ {/if}
108
+ {#if bottom?.trim()}
109
+ <output class={cls(AREA.BOTTOM, status)} id={idDesc ?? idErr} {role}>{bottom}</output>
110
+ {/if}
111
+ </div>
112
+
113
+ {#snippet lbl()}
114
+ {#if label?.trim()}
115
+ <label class={cls(AREA.LABEL, status)} for={id} id={idLabel}>
116
+ {label}
117
+ {#if extra?.trim()}
118
+ <span class={cls(AREA.EXTRA, status)}>{extra}</span>
119
+ {/if}
120
+ </label>
121
+ {/if}
122
+ {/snippet}
123
+ {#snippet main()}
124
+ {#if type === "area"}
125
+ {#if typeof action === "function"}
126
+ <textarea bind:value bind:this={element} class={cls(AREA.MAIN, status)} {id} {onchange} {oninput} {oninvalid} {...attrs} use:action aria-describedby={idDesc} aria-controls={idDesc} aria-invalid={invalid} aria-errormessage={errMsg}></textarea>
127
+ {:else}
128
+ <textarea bind:value bind:this={element} class={cls(AREA.MAIN, status)} {id} {onchange} {oninput} {oninvalid} {...attrs} aria-describedby={idDesc} aria-controls={idDesc} aria-invalid={invalid} aria-errormessage={errMsg}></textarea>
129
+ {/if}
130
+ {:else}
131
+ {#if typeof action === "function"}
132
+ <input bind:value bind:this={element} class={cls(AREA.MAIN, status)} list={idList} {id} {type} {onchange} {oninput} {oninvalid} {...attrs} use:action aria-describedby={idDesc} aria-controls={idDesc} aria-invalid={invalid} aria-errormessage={errMsg} />
133
+ {:else}
134
+ <input bind:value bind:this={element} class={cls(AREA.MAIN, status)} list={idList} {id} {type} {onchange} {oninput} {oninvalid} {...attrs} aria-describedby={idDesc} aria-controls={idDesc} aria-invalid={invalid} aria-errormessage={errMsg} />
135
+ {/if}
136
+ {#if Array.isArray(options) && options.length > 0}
137
+ <datalist id={idList}>
138
+ {#each options as option (option)}
139
+ <option value={option}></option>
140
+ {/each}
141
+ </datalist>
142
+ {/if}
143
+ {/if}
144
+ {/snippet}
@@ -0,0 +1,24 @@
1
+ export type TextFieldProps = {
2
+ label?: string;
3
+ extra?: string;
4
+ aux?: Snippet<[string, StateName, HTMLInputElement | HTMLTextAreaElement | undefined]>;
5
+ left?: Snippet<[string, StateName, HTMLInputElement | HTMLTextAreaElement | undefined]>;
6
+ right?: Snippet<[string, StateName, HTMLInputElement | HTMLTextAreaElement | undefined]>;
7
+ bottom?: string;
8
+ value?: string;
9
+ type?: "text" | "area" | "email" | "password" | "search" | "tel" | "url" | "number";
10
+ options?: string[];
11
+ status?: StateName;
12
+ validations?: ((value: string, validity?: ValidityState) => string)[];
13
+ style?: ClassRuleSet | string;
14
+ attributes?: HTMLInputAttributes & HTMLTextareaAttributes;
15
+ action?: Action;
16
+ element?: HTMLInputElement | HTMLTextAreaElement;
17
+ };
18
+ import { type Snippet } from "svelte";
19
+ import { type Action } from "svelte/action";
20
+ import { type HTMLInputAttributes, type HTMLTextareaAttributes } from "svelte/elements";
21
+ import { type ClassRuleSet, type StateName } from "./core";
22
+ declare const TextField: import("svelte").Component<TextFieldProps, {}, "type" | "bottom" | "value" | "options" | "status" | "element">;
23
+ type TextField = ReturnType<typeof TextField>;
24
+ export default TextField;
@@ -0,0 +1,47 @@
1
+ export { type StateName, type AreaName, type ClassRule, type ClassRuleSet, type ThemePreset, CONST, STATE, AREA, elemId, theme, getClassFn, isUndef, omit, debounce, throttle, };
2
+ type valueof<T> = T[keyof T];
3
+ type StateName = valueof<typeof STATE>;
4
+ type AreaName = valueof<typeof AREA>;
5
+ type ClassRule = Partial<Record<StateName | typeof CONST, string>>;
6
+ type ClassRuleSet = Partial<Record<AreaName, ClassRule>>;
7
+ type CssVarSet = Record<string, string>;
8
+ type ThemePreset = Record<string, CssVarSet>;
9
+ declare const CONST = "constant";
10
+ declare const STATE: Readonly<{
11
+ DEFAULT: "default";
12
+ ACTIVE: "active";
13
+ INACTIVE: "inactive";
14
+ }>;
15
+ declare const AREA: Readonly<{
16
+ WHOLE: "whole";
17
+ MIDDLE: "middle";
18
+ MAIN: "main";
19
+ TOP: "top";
20
+ LEFT: "left";
21
+ RIGHT: "right";
22
+ BOTTOM: "bottom";
23
+ LABEL: "label";
24
+ AUX: "aux";
25
+ EXTRA: "extra";
26
+ }>;
27
+ declare class RandomId {
28
+ #private;
29
+ get(v: unknown): string | undefined;
30
+ }
31
+ declare class ThemeSwitcher {
32
+ #private;
33
+ get current(): string;
34
+ constructor();
35
+ setPreset(preset: ThemePreset): ThemeSwitcher;
36
+ toLight(): void;
37
+ toDark(): void;
38
+ switch(theme: string): void;
39
+ }
40
+ declare const elemId: RandomId;
41
+ declare const theme: ThemeSwitcher;
42
+ type ClassFn = (area: AreaName, status: StateName) => string | undefined;
43
+ declare function getClassFn(name: string, preset: ClassRuleSet, style: ClassRuleSet | string): ClassFn;
44
+ declare function isUndef(v: unknown): boolean;
45
+ declare function omit<T extends Record<string, unknown>, K extends keyof T>(obj: T, ...keys: K[]): Omit<T, K>;
46
+ declare function debounce<Args extends unknown[], R>(delay: number, fn: (...args: Args) => R): (...args: Args) => void;
47
+ declare function throttle<Args extends unknown[], R>(interval: number, fn: (...args: Args) => R): (...args: Args) => void;
@@ -0,0 +1,172 @@
1
+ // deno-fmt-ignore
2
+ export { CONST, STATE, AREA, elemId, theme, getClassFn, isUndef, omit, debounce, throttle, };
3
+ const CONST = "constant";
4
+ const STATE = Object.freeze({ DEFAULT: "default", ACTIVE: "active", INACTIVE: "inactive" });
5
+ const AREA = Object.freeze({
6
+ WHOLE: "whole",
7
+ MIDDLE: "middle",
8
+ MAIN: "main",
9
+ TOP: "top",
10
+ LEFT: "left",
11
+ RIGHT: "right",
12
+ BOTTOM: "bottom",
13
+ LABEL: "label",
14
+ AUX: "aux",
15
+ EXTRA: "extra",
16
+ });
17
+ class RandomId {
18
+ static #ALPHABETIC = [...Array.from(Array(25).keys(), (x) => x + 65), ...Array.from(Array(25).keys(), (x) => x + 97)];
19
+ #store = new Set();
20
+ get(v) {
21
+ if (!v)
22
+ return;
23
+ if (this.#store.size > 10000)
24
+ this.#store.clear();
25
+ return this.#add();
26
+ }
27
+ #char() {
28
+ return RandomId.#ALPHABETIC[Math.trunc(Math.random() * RandomId.#ALPHABETIC.length)];
29
+ }
30
+ #gen() {
31
+ return String.fromCharCode(...Array(4).fill(null).map(() => this.#char()), 58);
32
+ }
33
+ #add() {
34
+ let id = this.#gen();
35
+ while (this.#store.has(id)) {
36
+ id = this.#gen();
37
+ }
38
+ this.#store.add(id);
39
+ return id;
40
+ }
41
+ }
42
+ class ThemeSwitcher {
43
+ static #DARK = "dark";
44
+ static #LIGHT = "light";
45
+ #styles = {};
46
+ #current;
47
+ get current() {
48
+ return this.#current;
49
+ }
50
+ constructor() {
51
+ this.#current = ThemeSwitcher.#setInitialTheme();
52
+ }
53
+ setPreset(preset) {
54
+ this.#styles = ThemeSwitcher.#toCSSVarName(preset);
55
+ this.#setColorScheme();
56
+ return this;
57
+ }
58
+ toLight() {
59
+ this.switch(ThemeSwitcher.#LIGHT);
60
+ }
61
+ toDark() {
62
+ this.switch(ThemeSwitcher.#DARK);
63
+ }
64
+ switch(theme) {
65
+ if (!this.#exists(theme))
66
+ return;
67
+ this.#current = theme;
68
+ this.#apply();
69
+ }
70
+ #apply() {
71
+ if (!window)
72
+ return;
73
+ const style = window.document.body.style;
74
+ Object.entries(this.#styles[this.#current])
75
+ .forEach(([name, value]) => style.setProperty(name, value));
76
+ }
77
+ #exists(theme) {
78
+ return Object.keys(this.#styles).includes(theme);
79
+ }
80
+ #setColorScheme() {
81
+ if (!window)
82
+ return;
83
+ const themes = Object.keys(this.#styles).filter((x) => x === ThemeSwitcher.#LIGHT || x === ThemeSwitcher.#DARK);
84
+ window.document.documentElement.style.colorScheme = themes.join(" ");
85
+ }
86
+ static #toCSSVarName(styles) {
87
+ return Object.fromEntries(Object.entries(styles)
88
+ .map(([theme, obj]) => [theme, ThemeSwitcher.#renameProperties(obj)]));
89
+ }
90
+ static #renameProperties(obj) {
91
+ return Object.fromEntries(Object.entries(obj)
92
+ .map(([name, value]) => [`--${name.replaceAll("_", "-")}`, value]));
93
+ }
94
+ static #setInitialTheme() {
95
+ return window?.matchMedia("(prefers-color-scheme: light)").matches ? ThemeSwitcher.#LIGHT : ThemeSwitcher.#DARK;
96
+ }
97
+ }
98
+ const elemId = new RandomId();
99
+ const theme = new ThemeSwitcher();
100
+ function getClassFn(name, preset, style) {
101
+ const rule = getRule(name, preset, style);
102
+ if (typeof rule === "string") {
103
+ return (area, status) => cssClass(rule, area, status);
104
+ }
105
+ else {
106
+ return (area, status) => ruleClass(rule, area, status);
107
+ }
108
+ }
109
+ function getRule(name, preset, style) {
110
+ if (typeof style === "string")
111
+ return style.trim() ? style : name;
112
+ const rule = mergeRule(preset, style);
113
+ return Object.keys(rule).length <= 0 ? name : rule;
114
+ }
115
+ function cssClass(name, area, status) {
116
+ return `${name} ${area}${status === STATE.DEFAULT ? "" : ` ${status}`}`;
117
+ }
118
+ function ruleClass(rule, area, status) {
119
+ const constant = rule[area]?.constant ?? "";
120
+ const dynamic = rule[area]?.[status] ?? rule[area]?.default ?? "";
121
+ return constant === "" && dynamic === "" ? undefined : `${constant}${constant && dynamic ? " " : ""}${dynamic}`;
122
+ }
123
+ function mergeRule(preset, style) {
124
+ const presetKeys = Object.keys(preset);
125
+ if (presetKeys.length <= 0)
126
+ return style;
127
+ const styleKeys = Object.keys(style);
128
+ if (styleKeys.length <= 0)
129
+ return preset;
130
+ const result = {};
131
+ new Set([...presetKeys, ...styleKeys]).forEach((key) => {
132
+ result[key] = { ...preset[key] ?? {}, ...style[key] ?? {} };
133
+ });
134
+ return result;
135
+ }
136
+ function isUndef(v) {
137
+ return v === void 0;
138
+ }
139
+ function omit(obj, ...keys) {
140
+ if (Object.isFrozen(obj) || Object.isSealed(obj))
141
+ return obj;
142
+ keys.forEach((key) => delete obj[key]);
143
+ return obj;
144
+ }
145
+ function debounce(delay, fn) {
146
+ let timer;
147
+ return (...args) => {
148
+ if (timer)
149
+ clearTimeout(timer);
150
+ timer = setTimeout(() => {
151
+ fn.call(null, ...args);
152
+ }, delay);
153
+ };
154
+ }
155
+ function throttle(interval, fn) {
156
+ let timer;
157
+ let last = 0;
158
+ const elapsed = () => Date.now() - last;
159
+ const run = (args) => {
160
+ fn.call(null, ...args);
161
+ last = Date.now();
162
+ };
163
+ return (...args) => {
164
+ if (!last)
165
+ return run(args);
166
+ clearTimeout(timer);
167
+ timer = setTimeout(() => {
168
+ if (elapsed() >= interval)
169
+ run(args);
170
+ }, interval - elapsed());
171
+ };
172
+ }
@@ -0,0 +1,194 @@
1
+ // deno-fmt-ignore
2
+ export {
3
+ type StateName,
4
+ type AreaName,
5
+ type ClassRule,
6
+ type ClassRuleSet,
7
+ type ThemePreset,
8
+ CONST,
9
+ STATE,
10
+ AREA,
11
+ elemId,
12
+ theme,
13
+ getClassFn,
14
+ isUndef,
15
+ omit,
16
+ debounce,
17
+ throttle,
18
+ };
19
+
20
+ type valueof<T> = T[keyof T];
21
+ type StateName = valueof<typeof STATE>;
22
+ type AreaName = valueof<typeof AREA>;
23
+ type ClassRule = Partial<Record<StateName | typeof CONST, string>>;
24
+ type ClassRuleSet = Partial<Record<AreaName, ClassRule>>;
25
+ type CssVarSet = Record<string, string>;
26
+ type ThemePreset = Record<string, CssVarSet>;
27
+
28
+ const CONST = "constant";
29
+ const STATE = Object.freeze({ DEFAULT: "default", ACTIVE: "active", INACTIVE: "inactive" });
30
+ const AREA = Object.freeze({
31
+ WHOLE: "whole",
32
+ MIDDLE: "middle",
33
+ MAIN: "main",
34
+ TOP: "top",
35
+ LEFT: "left",
36
+ RIGHT: "right",
37
+ BOTTOM: "bottom",
38
+ LABEL: "label",
39
+ AUX: "aux",
40
+ EXTRA: "extra",
41
+ });
42
+
43
+ class RandomId {
44
+ static #ALPHABETIC = [...Array.from(Array(25).keys(), (x) => x + 65), ...Array.from(Array(25).keys(), (x) => x + 97)];
45
+ #store = new Set<string>();
46
+
47
+ get(v: unknown): string | undefined {
48
+ if (!v) return;
49
+ if (this.#store.size > 10000) this.#store.clear();
50
+ return this.#add();
51
+ }
52
+ #char(): number {
53
+ return RandomId.#ALPHABETIC[Math.trunc(Math.random() * RandomId.#ALPHABETIC.length)];
54
+ }
55
+ #gen(): string {
56
+ return String.fromCharCode(...Array(4).fill(null).map(() => this.#char()), 58);
57
+ }
58
+ #add(): string {
59
+ let id = this.#gen();
60
+ while (this.#store.has(id)) {
61
+ id = this.#gen();
62
+ }
63
+ this.#store.add(id);
64
+ return id;
65
+ }
66
+ }
67
+ class ThemeSwitcher {
68
+ static #DARK = "dark";
69
+ static #LIGHT = "light";
70
+ #styles: ThemePreset = {};
71
+ #current;
72
+ get current() {
73
+ return this.#current;
74
+ }
75
+
76
+ constructor() {
77
+ this.#current = ThemeSwitcher.#setInitialTheme();
78
+ }
79
+ setPreset(preset: ThemePreset): ThemeSwitcher {
80
+ this.#styles = ThemeSwitcher.#toCSSVarName(preset);
81
+ this.#setColorScheme();
82
+ return this;
83
+ }
84
+ toLight() {
85
+ this.switch(ThemeSwitcher.#LIGHT);
86
+ }
87
+ toDark() {
88
+ this.switch(ThemeSwitcher.#DARK);
89
+ }
90
+ switch(theme: string) {
91
+ if (!this.#exists(theme)) return;
92
+ this.#current = theme;
93
+ this.#apply();
94
+ }
95
+ #apply() {
96
+ if (!window) return;
97
+ const style = window.document.body.style;
98
+ Object.entries(this.#styles[this.#current])
99
+ .forEach(([name, value]) => style.setProperty(name, value));
100
+ }
101
+ #exists(theme: string): boolean {
102
+ return Object.keys(this.#styles).includes(theme);
103
+ }
104
+ #setColorScheme() {
105
+ if (!window) return;
106
+ const themes = Object.keys(this.#styles).filter((x) => x === ThemeSwitcher.#LIGHT || x === ThemeSwitcher.#DARK);
107
+ window.document.documentElement.style.colorScheme = themes.join(" ");
108
+ }
109
+
110
+ static #toCSSVarName(styles: ThemePreset): ThemePreset {
111
+ return Object.fromEntries(
112
+ Object.entries(styles)
113
+ .map(([theme, obj]) => [theme, ThemeSwitcher.#renameProperties(obj)]),
114
+ ) as ThemePreset;
115
+ }
116
+ static #renameProperties(obj: Record<string, string>): CssVarSet {
117
+ return Object.fromEntries(
118
+ Object.entries(obj)
119
+ .map(([name, value]) => [`--${name.replaceAll("_", "-")}`, value]),
120
+ );
121
+ }
122
+ static #setInitialTheme(): string {
123
+ return window?.matchMedia("(prefers-color-scheme: light)").matches ? ThemeSwitcher.#LIGHT : ThemeSwitcher.#DARK;
124
+ }
125
+ }
126
+ const elemId = new RandomId();
127
+ const theme = new ThemeSwitcher();
128
+
129
+ type ClassFn = (area: AreaName, status: StateName) => string | undefined;
130
+ function getClassFn(name: string, preset: ClassRuleSet, style: ClassRuleSet | string): ClassFn {
131
+ const rule = getRule(name, preset, style);
132
+ if (typeof rule === "string") {
133
+ return (area: AreaName, status: StateName) => cssClass(rule, area, status);
134
+ } else {
135
+ return (area: AreaName, status: StateName) => ruleClass(rule, area, status);
136
+ }
137
+ }
138
+ function getRule(name: string, preset: ClassRuleSet, style: ClassRuleSet | string): ClassRuleSet | string {
139
+ if (typeof style === "string") return style.trim() ? style : name;
140
+ const rule = mergeRule(preset, style);
141
+ return Object.keys(rule).length <= 0 ? name : rule;
142
+ }
143
+ function cssClass(name: string, area: AreaName, status: StateName): string {
144
+ return `${name} ${area}${status === STATE.DEFAULT ? "" : ` ${status}`}`;
145
+ }
146
+ function ruleClass(rule: ClassRuleSet, area: AreaName, status: StateName): string | undefined {
147
+ const constant = rule[area]?.constant ?? "";
148
+ const dynamic = rule[area]?.[status] ?? rule[area]?.default ?? "";
149
+ return constant === "" && dynamic === "" ? undefined : `${constant}${constant && dynamic ? " " : ""}${dynamic}`;
150
+ }
151
+ function mergeRule(preset: ClassRuleSet, style: ClassRuleSet): ClassRuleSet {
152
+ const presetKeys = Object.keys(preset) as AreaName[];
153
+ if (presetKeys.length <= 0) return style;
154
+ const styleKeys = Object.keys(style) as AreaName[];
155
+ if (styleKeys.length <= 0) return preset;
156
+ const result: ClassRuleSet = {};
157
+ new Set([...presetKeys, ...styleKeys]).forEach((key) => {
158
+ result[key] = { ...preset[key] ?? {}, ...style[key] ?? {} };
159
+ });
160
+ return result;
161
+ }
162
+ function isUndef(v: unknown): boolean {
163
+ return v === void 0;
164
+ }
165
+ function omit<T extends Record<string, unknown>, K extends keyof T>(obj: T, ...keys: K[]): Omit<T, K> {
166
+ if (Object.isFrozen(obj) || Object.isSealed(obj)) return obj;
167
+ keys.forEach((key) => delete obj[key]);
168
+ return obj;
169
+ }
170
+ function debounce<Args extends unknown[], R>(delay: number, fn: (...args: Args) => R): (...args: Args) => void {
171
+ let timer: number | undefined;
172
+ return (...args: Args) => {
173
+ if (timer) clearTimeout(timer);
174
+ timer = setTimeout(() => {
175
+ fn.call(null, ...args);
176
+ }, delay);
177
+ };
178
+ }
179
+ function throttle<Args extends unknown[], R>(interval: number, fn: (...args: Args) => R): (...args: Args) => void {
180
+ let timer: number | undefined;
181
+ let last: number = 0;
182
+ const elapsed = () => Date.now() - last;
183
+ const run = (args: Args) => {
184
+ fn.call(null, ...args);
185
+ last = Date.now();
186
+ };
187
+ return (...args: Args) => {
188
+ if (!last) return run(args);
189
+ clearTimeout(timer);
190
+ timer = setTimeout(() => {
191
+ if (elapsed() >= interval) run(args);
192
+ }, interval - elapsed());
193
+ };
194
+ }
@@ -0,0 +1 @@
1
+ {"components":{"_TextField.svelte":{"dependencies":[]}}}
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { default as TextField } from "./_svseeds/_TextField.svelte";
package/index.js CHANGED
@@ -1,2 +1 @@
1
- import { main } from "./dist/main.js";
2
- main();
1
+ export { default as TextField } from "./_svseeds/_TextField.svelte";
package/package.json CHANGED
@@ -1,42 +1,38 @@
1
1
  {
2
- "name": "svseeds",
3
- "version": "0.0.2",
4
- "description": "A CLI to copy SvSeeds components.",
5
- "type": "module",
6
- "exports": "./index.js",
7
- "files": [
8
- "dist/**/*",
9
- "index.js"
10
- ],
11
- "keywords": [
12
- "svelte",
13
- "headless",
14
- "components",
15
- "cli"
16
- ],
17
- "author": "scirexs",
18
- "license": "MIT",
19
- "repository": {
20
- "type": "git",
21
- "url": "git+https://github.com/scirexs/svseeds-cli-node.git"
22
- },
23
- "scripts": {
24
- "test": "echo \"Error: no test specified\" && exit 1",
25
- "lint": "eslint .",
26
- "format": "eslint --fix ."
27
- },
28
- "devDependencies": {
29
- "@eslint/js": "^9.21.0",
30
- "@stylistic/eslint-plugin": "^4.2.0",
31
- "@types/node": "^22.13.9",
32
- "eslint": "^9.21.0",
33
- "globals": "^16.0.0",
34
- "typescript": "^5.8.2",
35
- "typescript-eslint": "^8.26.0"
36
- },
37
- "dependencies": {
38
- "@clack/prompts": "^0.10.0",
39
- "commander": "^13.1.0",
40
- "tar": "^7.4.3"
41
- }
2
+ "name": "svseeds",
3
+ "version": "0.0.6",
4
+ "description": "Simple headless Svelte components.",
5
+ "type": "module",
6
+ "main": "./index.js",
7
+ "module": "./index.js",
8
+ "types": "./index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./index.d.ts",
12
+ "svelte": "./index.js"
13
+ }
14
+ },
15
+ "svelte": "./index.js",
16
+ "files": [
17
+ "_svseeds",
18
+ "index.js",
19
+ "index.d.ts",
20
+ "README.md",
21
+ "LICENSE"
22
+ ],
23
+ "keywords": [
24
+ "svelte",
25
+ "headless",
26
+ "components",
27
+ "ui"
28
+ ],
29
+ "author": "scirexs",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/scirexs/svseeds-ui.git"
34
+ },
35
+ "peerDependencies": {
36
+ "svelte": "^5.0.0"
37
+ }
42
38
  }