svseeds 0.0.2 → 0.0.7

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,9 @@
1
- # svseeds - the SvSeeds CLI
2
- A CLI to copy SvSeeds components for Svelte.
1
+ # SvSeeds - Simple Components of Svelte
3
2
 
4
- ## Quick Start
5
- ```
6
- npx svseeds
7
- ```
3
+ [![npm version](https://img.shields.io/npm/v/svseeds)](https://www.npmjs.com/package/svseeds)
4
+ [![JSR](https://jsr.io/badges/@svseeds/ui)](https://jsr.io/@svseeds/ui)
5
+ [![license](https://img.shields.io/npm/l/svseeds)](LICENSE.md)
8
6
 
9
- ## Basic Usage
10
- Copy specified SvSeeds files.
11
- ```
12
- npx svseeds [components...]
13
- ```
7
+ Simple headless components for Svelte.
14
8
 
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
- ```
9
+ **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 @@
1
+ var __assign=this&&this.__assign||function(){__assign=Object.assign||function(e){for(var t,r=1,i=arguments.length;r<i;r++){t=arguments[r];for(var a in t)if(Object.prototype.hasOwnProperty.call(t,a))e[a]=t[a]}return e};return __assign.apply(this,arguments)};var __classPrivateFieldGet=this&&this.__classPrivateFieldGet||function(e,t,r,i){if(r==="a"&&!i)throw new TypeError("Private accessor was defined without a getter");if(typeof t==="function"?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return r==="m"?i:r==="a"?i.call(e):i?i.value:t.get(e)};var __classPrivateFieldSet=this&&this.__classPrivateFieldSet||function(e,t,r,i,a){if(i==="m")throw new TypeError("Private method is not writable");if(i==="a"&&!a)throw new TypeError("Private accessor was defined without a setter");if(typeof t==="function"?e!==t||!a:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return i==="a"?a.call(e,r):a?a.value=r:t.set(e,r),r};var __spreadArray=this&&this.__spreadArray||function(e,t,r){if(r||arguments.length===2)for(var i=0,a=t.length,n;i<a;i++){if(n||!(i in t)){if(!n)n=Array.prototype.slice.call(t,0,i);n[i]=t[i]}}return e.concat(n||Array.prototype.slice.call(t))};export{CONST,STATE,AREA,elemId,theme,getClassFn,isUndef,omit,debounce,throttle};var CONST="constant";var STATE=Object.freeze({DEFAULT:"default",ACTIVE:"active",INACTIVE:"inactive"});var AREA=Object.freeze({WHOLE:"whole",MIDDLE:"middle",MAIN:"main",TOP:"top",LEFT:"left",RIGHT:"right",BOTTOM:"bottom",LABEL:"label",AUX:"aux",EXTRA:"extra"});var RandomId=function(){function e(){t.add(this);a.set(this,new Set)}e.prototype.get=function(e){if(!e)return;if(__classPrivateFieldGet(this,a,"f").size>1e4)__classPrivateFieldGet(this,a,"f").clear();return __classPrivateFieldGet(this,t,"m",l).call(this)};var t,r,i,a,n,s,l;r=e,a=new WeakMap,t=new WeakSet,n=function e(){return __classPrivateFieldGet(r,r,"f",i)[Math.trunc(Math.random()*__classPrivateFieldGet(r,r,"f",i).length)]},s=function e(){var r=this;return String.fromCharCode.apply(String,__spreadArray(__spreadArray([],Array(4).fill(null).map((function(){return __classPrivateFieldGet(r,t,"m",n).call(r)})),false),[58],false))},l=function e(){var r=__classPrivateFieldGet(this,t,"m",s).call(this);while(__classPrivateFieldGet(this,a,"f").has(r)){r=__classPrivateFieldGet(this,t,"m",s).call(this)}__classPrivateFieldGet(this,a,"f").add(r);return r};i={value:__spreadArray(__spreadArray([],Array.from(Array(25).keys(),(function(e){return e+65})),true),Array.from(Array(25).keys(),(function(e){return e+97})),true)};return e}();var ThemeSwitcher=function(){function e(){t.add(this);n.set(this,{});s.set(this,void 0);__classPrivateFieldSet(this,s,__classPrivateFieldGet(r,r,"m",d).call(r),"f")}Object.defineProperty(e.prototype,"current",{get:function(){return __classPrivateFieldGet(this,s,"f")},enumerable:false,configurable:true});e.prototype.setPreset=function(e){__classPrivateFieldSet(this,n,__classPrivateFieldGet(r,r,"m",u).call(r,e),"f");__classPrivateFieldGet(this,t,"m",o).call(this);return this};e.prototype.toLight=function(){this.switch(__classPrivateFieldGet(r,r,"f",a))};e.prototype.toDark=function(){this.switch(__classPrivateFieldGet(r,r,"f",i))};e.prototype.switch=function(e){if(!__classPrivateFieldGet(this,t,"m",c).call(this,e))return;__classPrivateFieldSet(this,s,e,"f");__classPrivateFieldGet(this,t,"m",l).call(this)};var t,r,i,a,n,s,l,c,o,u,f,d;r=e,n=new WeakMap,s=new WeakMap,t=new WeakSet,l=function e(){if(typeof window==="undefined")return;var t=window.document.body.style;Object.entries(__classPrivateFieldGet(this,n,"f")[__classPrivateFieldGet(this,s,"f")]).forEach((function(e){var r=e[0],i=e[1];return t.setProperty(r,i)}))},c=function e(t){return Object.keys(__classPrivateFieldGet(this,n,"f")).includes(t)},o=function e(){if(typeof window==="undefined")return;var t=Object.keys(__classPrivateFieldGet(this,n,"f")).filter((function(e){return e===__classPrivateFieldGet(r,r,"f",a)||e===__classPrivateFieldGet(r,r,"f",i)}));window.document.documentElement.style.colorScheme=t.join(" ")},u=function e(t){return Object.fromEntries(Object.entries(t).map((function(e){var t=e[0],i=e[1];return[t,__classPrivateFieldGet(r,r,"m",f).call(r,i)]})))},f=function e(t){return Object.fromEntries(Object.entries(t).map((function(e){var t=e[0],r=e[1];return["--".concat(t.replaceAll("_","-")),r]})))},d=function e(){if(typeof window==="undefined")return __classPrivateFieldGet(r,r,"f",a);return window.matchMedia("(prefers-color-scheme: light)").matches?__classPrivateFieldGet(r,r,"f",a):__classPrivateFieldGet(r,r,"f",i)};i={value:"dark"};a={value:"light"};return e}();var elemId=new RandomId;var theme=new ThemeSwitcher;function getClassFn(e,t,r){var i=getRule(e,t,r);if(typeof i==="string"){return function(e,t){return cssClass(i,e,t)}}else{return function(e,t){return ruleClass(i,e,t)}}}function getRule(e,t,r){if(typeof r==="string")return r.trim()?r:e;var i=mergeRule(t,r);return Object.keys(i).length<=0?e:i}function cssClass(e,t,r){return"".concat(e," ").concat(t).concat(r===STATE.DEFAULT?"":" ".concat(r))}function ruleClass(e,t,r){var i,a,n,s,l,c;var o=(a=(i=e[t])===null||i===void 0?void 0:i.constant)!==null&&a!==void 0?a:"";var u=(c=(s=(n=e[t])===null||n===void 0?void 0:n[r])!==null&&s!==void 0?s:(l=e[t])===null||l===void 0?void 0:l.default)!==null&&c!==void 0?c:"";return o===""&&u===""?undefined:"".concat(o).concat(o&&u?" ":"").concat(u)}function mergeRule(e,t){var r=Object.keys(e);if(r.length<=0)return t;var i=Object.keys(t);if(i.length<=0)return e;var a={};new Set(__spreadArray(__spreadArray([],r,true),i,true)).forEach((function(r){var i,n;a[r]=__assign(__assign({},(i=e[r])!==null&&i!==void 0?i:{}),(n=t[r])!==null&&n!==void 0?n:{})}));return a}function isUndef(e){return e===void 0}function omit(e){var t=[];for(var r=1;r<arguments.length;r++){t[r-1]=arguments[r]}if(Object.isFrozen(e)||Object.isSealed(e))return e;t.forEach((function(t){return delete e[t]}));return e}function debounce(e,t){var r;return function(){var i=[];for(var a=0;a<arguments.length;a++){i[a]=arguments[a]}if(r)clearTimeout(r);r=setTimeout((function(){t.call.apply(t,__spreadArray([null],i,false))}),e)}}function throttle(e,t){var r;var i=0;var a=function(){return Date.now()-i};var n=function(e){t.call.apply(t,__spreadArray([null],e,false));i=Date.now()};return function(){var t=[];for(var s=0;s<arguments.length;s++){t[s]=arguments[s]}if(!i)return n(t);clearTimeout(r);r=setTimeout((function(){if(a()>=e)n(t)}),e-a())}}
@@ -0,0 +1,195 @@
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 (typeof window === "undefined") 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 (typeof window === "undefined") 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
+ if (typeof window === "undefined") return ThemeSwitcher.#LIGHT;
124
+ return window.matchMedia("(prefers-color-scheme: light)").matches ? ThemeSwitcher.#LIGHT : ThemeSwitcher.#DARK;
125
+ }
126
+ }
127
+ const elemId = new RandomId();
128
+ const theme = new ThemeSwitcher();
129
+
130
+ type ClassFn = (area: AreaName, status: StateName) => string | undefined;
131
+ function getClassFn(name: string, preset: ClassRuleSet, style: ClassRuleSet | string): ClassFn {
132
+ const rule = getRule(name, preset, style);
133
+ if (typeof rule === "string") {
134
+ return (area: AreaName, status: StateName) => cssClass(rule, area, status);
135
+ } else {
136
+ return (area: AreaName, status: StateName) => ruleClass(rule, area, status);
137
+ }
138
+ }
139
+ function getRule(name: string, preset: ClassRuleSet, style: ClassRuleSet | string): ClassRuleSet | string {
140
+ if (typeof style === "string") return style.trim() ? style : name;
141
+ const rule = mergeRule(preset, style);
142
+ return Object.keys(rule).length <= 0 ? name : rule;
143
+ }
144
+ function cssClass(name: string, area: AreaName, status: StateName): string {
145
+ return `${name} ${area}${status === STATE.DEFAULT ? "" : ` ${status}`}`;
146
+ }
147
+ function ruleClass(rule: ClassRuleSet, area: AreaName, status: StateName): string | undefined {
148
+ const constant = rule[area]?.constant ?? "";
149
+ const dynamic = rule[area]?.[status] ?? rule[area]?.default ?? "";
150
+ return constant === "" && dynamic === "" ? undefined : `${constant}${constant && dynamic ? " " : ""}${dynamic}`;
151
+ }
152
+ function mergeRule(preset: ClassRuleSet, style: ClassRuleSet): ClassRuleSet {
153
+ const presetKeys = Object.keys(preset) as AreaName[];
154
+ if (presetKeys.length <= 0) return style;
155
+ const styleKeys = Object.keys(style) as AreaName[];
156
+ if (styleKeys.length <= 0) return preset;
157
+ const result: ClassRuleSet = {};
158
+ new Set([...presetKeys, ...styleKeys]).forEach((key) => {
159
+ result[key] = { ...preset[key] ?? {}, ...style[key] ?? {} };
160
+ });
161
+ return result;
162
+ }
163
+ function isUndef(v: unknown): boolean {
164
+ return v === void 0;
165
+ }
166
+ function omit<T extends Record<string, unknown>, K extends keyof T>(obj: T, ...keys: K[]): Omit<T, K> {
167
+ if (Object.isFrozen(obj) || Object.isSealed(obj)) return obj;
168
+ keys.forEach((key) => delete obj[key]);
169
+ return obj;
170
+ }
171
+ function debounce<Args extends unknown[], R>(delay: number, fn: (...args: Args) => R): (...args: Args) => void {
172
+ let timer: number | undefined;
173
+ return (...args: Args) => {
174
+ if (timer) clearTimeout(timer);
175
+ timer = setTimeout(() => {
176
+ fn.call(null, ...args);
177
+ }, delay);
178
+ };
179
+ }
180
+ function throttle<Args extends unknown[], R>(interval: number, fn: (...args: Args) => R): (...args: Args) => void {
181
+ let timer: number | undefined;
182
+ let last: number = 0;
183
+ const elapsed = () => Date.now() - last;
184
+ const run = (args: Args) => {
185
+ fn.call(null, ...args);
186
+ last = Date.now();
187
+ };
188
+ return (...args: Args) => {
189
+ if (!last) return run(args);
190
+ clearTimeout(timer);
191
+ timer = setTimeout(() => {
192
+ if (elapsed() >= interval) run(args);
193
+ }, interval - elapsed());
194
+ };
195
+ }
@@ -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.7",
4
+ "description": "Simple headless components for Svelte.",
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
  }