@gottheflag/nova 0.1.0-beta.1 → 0.1.0-beta.2
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 +3 -1
- package/dist/adapters/index.d.ts +15 -0
- package/dist/adapters/index.js.map +1 -1
- package/dist/core/index.d.ts +19 -0
- package/dist/core/index.js.map +1 -1
- package/package.json +57 -57
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Theme controller
|
|
2
2
|
|
|
3
|
+
[](https://badge.socket.dev/npm/package/@gottheflag/nova/0.1.0-beta.1)
|
|
4
|
+
|
|
3
5
|
A controller for managing the theming for applications.
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
@@ -25,4 +27,4 @@ const ctl = new Controller(document, {
|
|
|
25
27
|
});
|
|
26
28
|
|
|
27
29
|
ctl.use(Button); // use adapters
|
|
28
|
-
```
|
|
30
|
+
```
|
package/dist/adapters/index.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { Adapter, Name } from '../core/index.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Copyright (c) 2026 GTF
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
3
8
|
/**
|
|
4
9
|
* @example
|
|
5
10
|
* <button value="<prefix>light">Light</button>
|
|
@@ -9,6 +14,11 @@ declare const Button: Adapter & {
|
|
|
9
14
|
registry: Map<Element, Name[]>;
|
|
10
15
|
};
|
|
11
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Copyright (c) 2026 GTF
|
|
19
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
20
|
+
*/
|
|
21
|
+
|
|
12
22
|
interface Options$1 {
|
|
13
23
|
prefix?: string;
|
|
14
24
|
}
|
|
@@ -17,6 +27,11 @@ declare const Radio: Adapter<Options$1> & {
|
|
|
17
27
|
prefix?: string;
|
|
18
28
|
};
|
|
19
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Copyright (c) 2026 GTF
|
|
32
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
33
|
+
*/
|
|
34
|
+
|
|
20
35
|
interface Options {
|
|
21
36
|
prefix?: string;
|
|
22
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/button.ts","../../src/adapters/radio.ts","../../src/adapters/select.ts"],"names":["PREFIX","parseThemeName","btn","value","name","slice","length","Button","registry","Map","discover","ctl","root","querySelectorAll","forEach","detected","theme","push","set","bind","addEventListener","get","includes","prefix","Radio","setup","_ctl","options","radio","e","target","currentTarget","sync","active","checked","Select","sel","Array","from","option","selectedIndex","selected"],"mappings":";;;AAIA,IAAMA,MAAAA,GAAS,QAAA;AAEf,SAASC,eAAeC,GAAAA,EAAsB;AAC1C,EAAA,MAAMC,QAAQD,GAAAA,CAAIC,KAAAA;AAClB,EAAA,MAAMC,IAAAA,GAAOD,KAAAA,CAAME,KAAAA,CAAML,MAAAA,CAAOM,MAAM,CAAA;AAEtC,EAAA,OAAOF,OAAOA,IAAAA,GAAOD,KAAAA;AACzB;AALSF,MAAAA,CAAAA,cAAAA,EAAAA,gBAAAA,CAAAA;AAYF,IAAMM,MAAAA,GAET;EACAH,IAAAA,EAAM,QAAA;AAENI,EAAAA,QAAAA,sBAAcC,GAAAA,EAAAA;AAEdC,EAAAA,QAAAA,CAASC,GAAAA,EAAe;AACpB,IAAA,MAAMC,OAAOD,GAAAA,CAAIC,IAAAA;AAEjBA,IAAAA,IAAAA,CAAKC,iBAAoC,CAAA,eAAA,EAAkBb,MAAAA,IAAU,CAAA,CAAEc,OAAAA,CAAQZ,CAAAA,GAAAA,KAAAA;AAC3E,MAAA,MAAMa,WAAmB,EAAA;AAEzB,MAAA,MAAMC,KAAAA,GAAQf,eAAeC,GAAAA,CAAAA;AAC7B,MAAA,IAAIc,KAAAA,EAAOD,QAAAA,CAASE,IAAAA,CAAKD,KAAAA,CAAAA;AAEzB,MAAA,IAAA,CAAKR,QAAAA,CAASU,GAAAA,CAAIhB,GAAAA,EAAKa,QAAAA,CAAAA;IAC3B,CAAA,CAAA;AACJ,EAAA,CAAA;AAEAI,EAAAA,IAAAA,CAAKR,GAAAA,EAAe;AAChB,IAAA,MAAMC,OAAOD,GAAAA,CAAIC,IAAAA;AAEjBA,IAAAA,IAAAA,CAAKC,iBAAoC,CAAA,eAAA,EAAkBb,MAAAA,IAAU,CAAA,CAAEc,OAAAA,CAAQZ,CAAAA,GAAAA,KAAAA;AAC3E,MAAA,MAAMc,KAAAA,GAAQf,eAAeC,GAAAA,CAAAA;AAE7BA,MAAAA,GAAAA,CAAIkB,gBAAAA,CAAiB,SAAS,MAAA;AAC1B,QAAA,IAAI,KAAKZ,QAAAA,CAASa,GAAAA,CAAInB,GAAAA,CAAAA,EAAMoB,QAAAA,CAASN,KAAAA,CAAAA,EAAQ;AACzCL,UAAAA,GAAAA,CAAIO,IAAIF,KAAAA,CAAAA;AACZ,QAAA;MACJ,CAAA,CAAA;IACJ,CAAA,CAAA;AACJ,EAAA;AACJ;;;AC/CA,IAAMhB,OAAAA,GAAS,QAAA;AAEf,SAASC,eAAAA,CAAeC,KAAuBqB,MAAAA,EAAc;AACzD,EAAA,MAAMpB,QAAQD,GAAAA,CAAIC,KAAAA;AAClB,EAAA,MAAMC,IAAAA,GAAOD,KAAAA,CAAME,KAAAA,CAAMkB,MAAAA,CAAOjB,MAAM,CAAA;AAEtC,EAAA,OAAOF,OAAOA,IAAAA,GAAOD,KAAAA;AACzB;AALSF,MAAAA,CAAAA,eAAAA,EAAAA,gBAAAA,CAAAA;AAWF,IAAMuB,KAAAA,GAGT;EACApB,IAAAA,EAAM,OAAA;AAENI,EAAAA,QAAAA,sBAAcC,GAAAA,EAAAA;AAEdgB,EAAAA,KAAAA,CAAMC,MAAkBC,OAAAA,EAAO;AAC3B,IAAA,IAAA,CAAKJ,MAAAA,GAASI,SAASJ,MAAAA,IAAUvB,OAAAA;AACrC,EAAA,CAAA;AAEAU,EAAAA,QAAAA,CAASC,GAAAA,EAAe;AACpB,IAAA,MAAMC,OAAOD,GAAAA,CAAIC,IAAAA;AAEjBA,IAAAA,IAAAA,CAAKC,iBAAmC,CAAA,4BAAA,EAA+Bb,OAAAA,IAAU,CAAA,CAAEc,OAAAA,CAAQc,CAAAA,KAAAA,KAAAA;AACvF,MAAA,MAAMb,WAAmB,EAAA;AAEzB,MAAA,MAAMC,KAAAA,GAAQf,eAAAA,CAAe2B,KAAAA,EAAO,IAAA,CAAKL,MAAM,CAAA;AAC/C,MAAA,IAAIP,KAAAA,EAAOD,QAAAA,CAASE,IAAAA,CAAKD,KAAAA,CAAAA;AAEzB,MAAA,IAAA,CAAKR,QAAAA,CAASU,GAAAA,CAAIU,KAAAA,EAAOb,QAAAA,CAAAA;IAC7B,CAAA,CAAA;AACJ,EAAA,CAAA;AAEAI,EAAAA,IAAAA,CAAKR,GAAAA,EAAe;AAChB,IAAA,MAAMC,OAAOD,GAAAA,CAAIC,IAAAA;AAEjBA,IAAAA,IAAAA,CAAKC,iBAAmC,CAAA,4BAAA,EAA+Bb,OAAAA,IAAU,CAAA,CAAEc,OAAAA,CAAQc,CAAAA,KAAAA,KAAAA;AACvFA,MAAAA,KAAAA,CAAMR,gBAAAA,CAAiB,QAAA,EAAUS,CAAAA,CAAAA,KAAAA;AAC7B,QAAA,MAAMC,SAASD,CAAAA,CAAEE,aAAAA;AACjB,QAAA,MAAMf,KAAAA,GAAQf,eAAAA,CAAe6B,MAAAA,EAAQ,IAAA,CAAKP,MAAM,CAAA;AAEhD,QAAA,IAAIP,KAAAA,EAAOL,GAAAA,CAAIO,GAAAA,CAAIF,KAAAA,CAAAA;MACvB,CAAA,CAAA;IACJ,CAAA,CAAA;AACJ,EAAA,CAAA;AAEAgB,EAAAA,IAAAA,CAAKrB,GAAAA,EAAe;AAChB,IAAA,MAAMC,OAAOD,GAAAA,CAAIC,IAAAA;AACjB,IAAA,MAAMqB,MAAAA,GAAStB,IAAIsB,MAAAA,EAAM;AAEzBrB,IAAAA,IAAAA,CAAKC,iBAAmC,CAAA,4BAAA,EAA+Bb,OAAAA,IAAU,CAAA,CAAEc,OAAAA,CAAQc,CAAAA,KAAAA,KAAAA;AACvF,MAAA,MAAMZ,KAAAA,GAAQf,eAAAA,CAAe2B,KAAAA,EAAO,IAAA,CAAKL,MAAM,CAAA;AAC/CK,MAAAA,KAAAA,CAAMM,UAAUlB,KAAAA,KAAUiB,MAAAA;IAC9B,CAAA,CAAA;AACJ,EAAA;AACJ;;;AC5DA,IAAMjC,OAAAA,GAAS,QAAA;AAEf,SAASC,eAAAA,CAAeC,KAAwBqB,MAAAA,EAAc;AAC1D,EAAA,MAAMpB,QAAQD,GAAAA,CAAIC,KAAAA;AAClB,EAAA,MAAMC,IAAAA,GAAOD,KAAAA,CAAME,KAAAA,CAAMkB,MAAAA,CAAOjB,MAAM,CAAA;AAEtC,EAAA,OAAOF,OAAOA,IAAAA,GAAOD,KAAAA;AACzB;AALSF,MAAAA,CAAAA,eAAAA,EAAAA,gBAAAA,CAAAA;AAqBF,IAAMkC,MAAAA,GAGT;EACA/B,IAAAA,EAAM,QAAA;AAENI,EAAAA,QAAAA,sBAAcC,GAAAA,EAAAA;AAEdgB,EAAAA,KAAAA,CAAMC,MAAkBC,OAAAA,EAAO;AAC3B,IAAA,IAAA,CAAKJ,MAAAA,GAASI,SAASJ,MAAAA,IAAUvB,OAAAA;AACrC,EAAA,CAAA;AAEAU,EAAAA,QAAAA,CAASC,GAAAA,EAAe;AACpB,IAAA,MAAMC,OAAOD,GAAAA,CAAIC,IAAAA;AAEjBA,IAAAA,IAAAA,CAAKC,gBAAAA,CAAoC,6BAA6B,IAAA,CAAKU,MAAM,KAAK,CAAA,CAAET,OAAAA,CAAQsB,CAAAA,GAAAA,KAAAA;AAC5F,MAAA,MAAMrB,WAAmB,EAAA;AAEzBsB,MAAAA,KAAAA,CAAMC,KAAKF,GAAAA,CAAIT,OAAO,CAAA,CAAEb,OAAAA,CAAQyB,CAAAA,MAAAA,KAAAA;AAC5B,QAAA,MAAMvB,KAAAA,GAAQf,eAAAA,CAAesC,MAAAA,EAAQ,IAAA,CAAKhB,MAAM,CAAA;AAChD,QAAA,IAAIP,KAAAA,EAAOD,QAAAA,CAASE,IAAAA,CAAKD,KAAAA,CAAAA;MAC7B,CAAA,CAAA;AAEA,MAAA,IAAA,CAAKR,QAAAA,CAASU,GAAAA,CAAIkB,GAAAA,EAAKrB,QAAAA,CAAAA;IAC3B,CAAA,CAAA;AACJ,EAAA,CAAA;AAEAI,EAAAA,IAAAA,CAAKR,GAAAA,EAAe;AAChB,IAAA,MAAMC,OAAOD,GAAAA,CAAIC,IAAAA;AAEjBA,IAAAA,IAAAA,CAAKC,gBAAAA,CAAoC,6BAA6B,IAAA,CAAKU,MAAM,KAAK,CAAA,CAAET,OAAAA,CAAQsB,CAAAA,GAAAA,KAAAA;AAC5FA,MAAAA,GAAAA,CAAIhB,gBAAAA,CAAiB,QAAA,EAAUS,CAAAA,CAAAA,KAAAA;AAC3B,QAAA,MAAMC,SAASD,CAAAA,CAAEE,aAAAA;AACjB,QAAA,MAAMQ,MAAAA,GAAST,MAAAA,CAAOH,OAAAA,CAASG,MAAAA,CAAOU,aAAa,CAAA;AAEnD,QAAA,MAAMxB,KAAAA,GAAQf,eAAAA,CAAesC,MAAAA,EAA6B,IAAA,CAAKhB,MAAM,CAAA;AACrE,QAAA,IAAIP,KAAAA,EAAOL,GAAAA,CAAIO,GAAAA,CAAIF,KAAAA,CAAAA;MACvB,CAAA,CAAA;IACJ,CAAA,CAAA;AACJ,EAAA,CAAA;AAEAgB,EAAAA,IAAAA,CAAKrB,GAAAA,EAAe;AAChB,IAAA,MAAMC,OAAOD,GAAAA,CAAIC,IAAAA;AACjB,IAAA,MAAMqB,MAAAA,GAAStB,IAAIsB,MAAAA,EAAM;AAEzBrB,IAAAA,IAAAA,CAAKC,gBAAAA,CAAoC,6BAA6B,IAAA,CAAKU,MAAM,KAAK,CAAA,CAAET,OAAAA,CAAQsB,CAAAA,GAAAA,KAAAA;AAC5FC,MAAAA,KAAAA,CAAMC,KAAKF,GAAAA,CAAIT,OAAO,CAAA,CAAEb,OAAAA,CAAQyB,CAAAA,MAAAA,KAAAA;AAC5B,QAAA,MAAMvB,KAAAA,GAAQf,eAAAA,CAAesC,MAAAA,EAAQ,IAAA,CAAKhB,MAAM,CAAA;AAChDgB,QAAAA,MAAAA,CAAOE,WAAWzB,KAAAA,KAAUiB,MAAAA;MAChC,CAAA,CAAA;IACJ,CAAA,CAAA;AACJ,EAAA;AACJ","file":"index.js","sourcesContent":["import { Controller } from \"../core/controller.js\";\r\nimport type { Adapter } from \"../core/adapter.js\";\r\nimport { State, type Name } from \"../core/types.js\";\r\n\r\nconst PREFIX = \"theme:\";\r\n\r\nfunction parseThemeName(btn: HTMLButtonElement): State {\r\n const value = btn.value;\r\n const name = value.slice(PREFIX.length) as State;\r\n\r\n return name ? name : value as State;\r\n}\r\n\r\n/**\r\n * @example\r\n * <button value=\"<prefix>light\">Light</button>\r\n * <button value=\"<prefix>dark\">Dark</button>\r\n */\r\nexport const Button: Adapter & {\r\n registry: Map<Element, Name[]>;\r\n} = {\r\n name: \"button\",\r\n\r\n registry: new Map<Element, Name[]>(),\r\n\r\n discover(ctl: Controller) {\r\n const root = ctl.root;\r\n\r\n root.querySelectorAll<HTMLButtonElement>(`button[value^=\"${PREFIX}\"]`).forEach(btn => {\r\n const detected: Name[] = [];\r\n\r\n const theme = parseThemeName(btn);\r\n if (theme) detected.push(theme);\r\n\r\n this.registry.set(btn, detected);\r\n });\r\n },\r\n\r\n bind(ctl: Controller) {\r\n const root = ctl.root;\r\n\r\n root.querySelectorAll<HTMLButtonElement>(`button[value^=\"${PREFIX}\"]`).forEach(btn => {\r\n const theme = parseThemeName(btn);\r\n\r\n btn.addEventListener(\"click\", () => {\r\n if (this.registry.get(btn)?.includes(theme)) {\r\n ctl.set(theme);\r\n }\r\n });\r\n });\r\n },\r\n};\r\n","import type { Adapter } from '../core/adapter.js';\r\nimport { Controller } from '../core/controller.js';\r\nimport { Name, State } from '../core/types.js';\r\n\r\nconst PREFIX = \"theme:\";\r\n\r\nfunction parseThemeName(btn: HTMLInputElement, prefix: string): State {\r\n const value = btn.value;\r\n const name = value.slice(prefix.length) as State;\r\n\r\n return name ? name : value as State;\r\n}\r\n\r\ninterface Options {\r\n prefix?: string;\r\n};\r\n\r\nexport const Radio: Adapter<Options> & {\r\n registry: Map<Element, Name[]>;\r\n prefix?: string;\r\n} = {\r\n name: 'radio',\r\n\r\n registry: new Map<Element, Name[]>(),\r\n\r\n setup(_ctl: Controller, options) {\r\n this.prefix = options?.prefix ?? PREFIX;\r\n },\r\n\r\n discover(ctl: Controller) {\r\n const root = ctl.root;\r\n\r\n root.querySelectorAll<HTMLInputElement>(`input[type=\"radio\"][value^=\"${PREFIX}\"]`).forEach(radio => {\r\n const detected: Name[] = [];\r\n\r\n const theme = parseThemeName(radio, this.prefix!);\r\n if (theme) detected.push(theme);\r\n\r\n this.registry.set(radio, detected);\r\n });\r\n },\r\n\r\n bind(ctl: Controller) {\r\n const root = ctl.root;\r\n\r\n root.querySelectorAll<HTMLInputElement>(`input[type=\"radio\"][value^=\"${PREFIX}\"]`).forEach(radio => { \r\n radio.addEventListener('change', e => {\r\n const target = e.currentTarget as HTMLInputElement;\r\n const theme = parseThemeName(target, this.prefix!);\r\n \r\n if (theme) ctl.set(theme);\r\n });\r\n });\r\n },\r\n\r\n sync(ctl: Controller) {\r\n const root = ctl.root;\r\n const active = ctl.active();\r\n\r\n root.querySelectorAll<HTMLInputElement>(`input[type=\"radio\"][value^=\"${PREFIX}\"]`).forEach(radio => {\r\n const theme = parseThemeName(radio, this.prefix!);\r\n radio.checked = theme === active;\r\n });\r\n }\r\n};\r\n","import type { Adapter } from \"../core/adapter.js\";\r\nimport { Controller } from \"../core/controller.js\";\r\nimport { State, type Name } from \"../core/types.js\";\r\n\r\nconst PREFIX = \"theme:\";\r\n\r\nfunction parseThemeName(btn: HTMLOptionElement, prefix: string): State {\r\n const value = btn.value;\r\n const name = value.slice(prefix.length) as State;\r\n\r\n return name ? name : value as State;\r\n}\r\n\r\ninterface Options {\r\n prefix?: string;\r\n};\r\n\r\n/**\r\n * @example\r\n * ```html\r\n * <select>\r\n * <option value=\"theme:light\">Light</option>\r\n * <option value=\"theme:system\">System</option>\r\n * <option value=\"theme:dark\">Dark</option>\r\n * </select>\r\n * ```\r\n */\r\nexport const Select: Adapter<Options> & {\r\n registry: Map<Element, Name[]>;\r\n prefix?: string;\r\n} = {\r\n name: \"select\",\r\n\r\n registry: new Map<Element, Name[]>(),\r\n\r\n setup(_ctl: Controller, options) {\r\n this.prefix = options?.prefix ?? PREFIX;\r\n },\r\n\r\n discover(ctl: Controller) {\r\n const root = ctl.root;\r\n\r\n root.querySelectorAll<HTMLSelectElement>(`select:has(option[value^=\"${this.prefix}\"])`).forEach(sel => {\r\n const detected: Name[] = [];\r\n\r\n Array.from(sel.options).forEach(option => {\r\n const theme = parseThemeName(option, this.prefix!);\r\n if (theme) detected.push(theme);\r\n });\r\n\r\n this.registry.set(sel, detected);\r\n });\r\n },\r\n\r\n bind(ctl: Controller) {\r\n const root = ctl.root;\r\n\r\n root.querySelectorAll<HTMLSelectElement>(`select:has(option[value^=\"${this.prefix}\"])`).forEach(sel => {\r\n sel.addEventListener(\"change\", e => {\r\n const target = e.currentTarget as HTMLSelectElement;\r\n const option = target.options[ target.selectedIndex ];\r\n\r\n const theme = parseThemeName(option as HTMLOptionElement, this.prefix!);\r\n if (theme) ctl.set(theme);\r\n });\r\n });\r\n },\r\n\r\n sync(ctl: Controller) {\r\n const root = ctl.root;\r\n const active = ctl.active();\r\n\r\n root.querySelectorAll<HTMLSelectElement>(`select:has(option[value^=\"${this.prefix}\"])`).forEach(sel => {\r\n Array.from(sel.options).forEach(option => {\r\n const theme = parseThemeName(option, this.prefix!);\r\n option.selected = theme === active;\r\n });\r\n });\r\n }\r\n};\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/button.ts","../../src/adapters/radio.ts","../../src/adapters/select.ts"],"names":["PREFIX","parseThemeName","btn","value","name","slice","length","Button","registry","Map","discover","ctl","root","querySelectorAll","forEach","detected","theme","push","set","bind","addEventListener","get","includes","prefix","Radio","setup","_ctl","options","radio","e","target","currentTarget","sync","active","checked","Select","sel","Array","from","option","selectedIndex","selected"],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * Copyright (c) 2026 GTF\r\n * SPDX-License-Identifier: Apache-2.0\r\n */\r\n\r\nimport { Controller } from \"../core/controller.js\";\r\nimport type { Adapter } from \"../core/adapter.js\";\r\nimport { State, type Name } from \"../core/types.js\";\r\n\r\nconst PREFIX = \"theme:\";\r\n\r\nfunction parseThemeName(btn: HTMLButtonElement): State {\r\n const value = btn.value;\r\n const name = value.slice(PREFIX.length) as State;\r\n\r\n return name ? name : value as State;\r\n}\r\n\r\n/**\r\n * @example\r\n * <button value=\"<prefix>light\">Light</button>\r\n * <button value=\"<prefix>dark\">Dark</button>\r\n */\r\nexport const Button: Adapter & {\r\n registry: Map<Element, Name[]>;\r\n} = {\r\n name: \"button\",\r\n\r\n registry: new Map<Element, Name[]>(),\r\n\r\n discover(ctl: Controller) {\r\n const root = ctl.root;\r\n\r\n root.querySelectorAll<HTMLButtonElement>(`button[value^=\"${PREFIX}\"]`).forEach(btn => {\r\n const detected: Name[] = [];\r\n\r\n const theme = parseThemeName(btn);\r\n if (theme) detected.push(theme);\r\n\r\n this.registry.set(btn, detected);\r\n });\r\n },\r\n\r\n bind(ctl: Controller) {\r\n const root = ctl.root;\r\n\r\n root.querySelectorAll<HTMLButtonElement>(`button[value^=\"${PREFIX}\"]`).forEach(btn => {\r\n const theme = parseThemeName(btn);\r\n\r\n btn.addEventListener(\"click\", () => {\r\n if (this.registry.get(btn)?.includes(theme)) {\r\n ctl.set(theme);\r\n }\r\n });\r\n });\r\n },\r\n};\r\n","/**\r\n * Copyright (c) 2026 GTF\r\n * SPDX-License-Identifier: Apache-2.0\r\n */\r\n\r\nimport type { Adapter } from '../core/adapter.js';\r\nimport { Controller } from '../core/controller.js';\r\nimport { Name, State } from '../core/types.js';\r\n\r\nconst PREFIX = \"theme:\";\r\n\r\nfunction parseThemeName(btn: HTMLInputElement, prefix: string): State {\r\n const value = btn.value;\r\n const name = value.slice(prefix.length) as State;\r\n\r\n return name ? name : value as State;\r\n}\r\n\r\ninterface Options {\r\n prefix?: string;\r\n};\r\n\r\nexport const Radio: Adapter<Options> & {\r\n registry: Map<Element, Name[]>;\r\n prefix?: string;\r\n} = {\r\n name: 'radio',\r\n\r\n registry: new Map<Element, Name[]>(),\r\n\r\n setup(_ctl: Controller, options) {\r\n this.prefix = options?.prefix ?? PREFIX;\r\n },\r\n\r\n discover(ctl: Controller) {\r\n const root = ctl.root;\r\n\r\n root.querySelectorAll<HTMLInputElement>(`input[type=\"radio\"][value^=\"${PREFIX}\"]`).forEach(radio => {\r\n const detected: Name[] = [];\r\n\r\n const theme = parseThemeName(radio, this.prefix!);\r\n if (theme) detected.push(theme);\r\n\r\n this.registry.set(radio, detected);\r\n });\r\n },\r\n\r\n bind(ctl: Controller) {\r\n const root = ctl.root;\r\n\r\n root.querySelectorAll<HTMLInputElement>(`input[type=\"radio\"][value^=\"${PREFIX}\"]`).forEach(radio => { \r\n radio.addEventListener('change', e => {\r\n const target = e.currentTarget as HTMLInputElement;\r\n const theme = parseThemeName(target, this.prefix!);\r\n \r\n if (theme) ctl.set(theme);\r\n });\r\n });\r\n },\r\n\r\n sync(ctl: Controller) {\r\n const root = ctl.root;\r\n const active = ctl.active();\r\n\r\n root.querySelectorAll<HTMLInputElement>(`input[type=\"radio\"][value^=\"${PREFIX}\"]`).forEach(radio => {\r\n const theme = parseThemeName(radio, this.prefix!);\r\n radio.checked = theme === active;\r\n });\r\n }\r\n};\r\n","/**\r\n * Copyright (c) 2026 GTF\r\n * SPDX-License-Identifier: Apache-2.0\r\n */\r\n\r\nimport type { Adapter } from \"../core/adapter.js\";\r\nimport { Controller } from \"../core/controller.js\";\r\nimport { State, type Name } from \"../core/types.js\";\r\n\r\nconst PREFIX = \"theme:\";\r\n\r\nfunction parseThemeName(btn: HTMLOptionElement, prefix: string): State {\r\n const value = btn.value;\r\n const name = value.slice(prefix.length) as State;\r\n\r\n return name ? name : value as State;\r\n}\r\n\r\ninterface Options {\r\n prefix?: string;\r\n};\r\n\r\n/**\r\n * @example\r\n * ```html\r\n * <select>\r\n * <option value=\"theme:light\">Light</option>\r\n * <option value=\"theme:system\">System</option>\r\n * <option value=\"theme:dark\">Dark</option>\r\n * </select>\r\n * ```\r\n */\r\nexport const Select: Adapter<Options> & {\r\n registry: Map<Element, Name[]>;\r\n prefix?: string;\r\n} = {\r\n name: \"select\",\r\n\r\n registry: new Map<Element, Name[]>(),\r\n\r\n setup(_ctl: Controller, options) {\r\n this.prefix = options?.prefix ?? PREFIX;\r\n },\r\n\r\n discover(ctl: Controller) {\r\n const root = ctl.root;\r\n\r\n root.querySelectorAll<HTMLSelectElement>(`select:has(option[value^=\"${this.prefix}\"])`).forEach(sel => {\r\n const detected: Name[] = [];\r\n\r\n Array.from(sel.options).forEach(option => {\r\n const theme = parseThemeName(option, this.prefix!);\r\n if (theme) detected.push(theme);\r\n });\r\n\r\n this.registry.set(sel, detected);\r\n });\r\n },\r\n\r\n bind(ctl: Controller) {\r\n const root = ctl.root;\r\n\r\n root.querySelectorAll<HTMLSelectElement>(`select:has(option[value^=\"${this.prefix}\"])`).forEach(sel => {\r\n sel.addEventListener(\"change\", e => {\r\n const target = e.currentTarget as HTMLSelectElement;\r\n const option = target.options[ target.selectedIndex ];\r\n\r\n const theme = parseThemeName(option as HTMLOptionElement, this.prefix!);\r\n if (theme) ctl.set(theme);\r\n });\r\n });\r\n },\r\n\r\n sync(ctl: Controller) {\r\n const root = ctl.root;\r\n const active = ctl.active();\r\n\r\n root.querySelectorAll<HTMLSelectElement>(`select:has(option[value^=\"${this.prefix}\"])`).forEach(sel => {\r\n Array.from(sel.options).forEach(option => {\r\n const theme = parseThemeName(option, this.prefix!);\r\n option.selected = theme === active;\r\n });\r\n });\r\n }\r\n};\r\n"]}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 GTF
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
1
5
|
/**
|
|
2
6
|
* Effective theme name (e.g. `day`, `night`, `mint`).
|
|
3
7
|
*/
|
|
@@ -10,6 +14,11 @@ type Name = string;
|
|
|
10
14
|
*/
|
|
11
15
|
type State = "system" | "light" | "dark";
|
|
12
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Copyright (c) 2026 GTF
|
|
19
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
20
|
+
*/
|
|
21
|
+
|
|
13
22
|
/**
|
|
14
23
|
* Raw configuration, passed by the user.
|
|
15
24
|
*/
|
|
@@ -37,6 +46,11 @@ interface ResolvedConfig {
|
|
|
37
46
|
initial?: Name;
|
|
38
47
|
}
|
|
39
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Copyright (c) 2026 GTF
|
|
51
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
52
|
+
*/
|
|
53
|
+
|
|
40
54
|
/**
|
|
41
55
|
* Theme controller logic.
|
|
42
56
|
*/
|
|
@@ -119,6 +133,11 @@ declare class Controller {
|
|
|
119
133
|
private write;
|
|
120
134
|
}
|
|
121
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Copyright (c) 2026 GTF
|
|
138
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
139
|
+
*/
|
|
140
|
+
|
|
122
141
|
interface Adapter<Options = undefined> {
|
|
123
142
|
name: string;
|
|
124
143
|
/**
|
package/dist/core/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/system.ts","../../src/core/utils.ts","../../src/core/controller.ts"],"names":["System","mql","handler","ctl","prefers","matchMedia","matches","config","dark","light","prefersDark","prefersLight","start","onChange","e","addEventListener","stop","removeEventListener","undefined","resolveStateOf","state","system","resolveRoot","root","HTMLElement","Document","documentElement","ShadowRoot","host","document","Controller","adapters","_root","cfg","attribute","storage","key","initial","syncSystemListener","active","resolved","theme","use","adapter","options","includes","console","warn","name","push","setup","discover","bind","sync","err","set","write","apply","a","localStorage","getItem","_state","setAttribute","setItem"],"mappings":"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","file":"index.js","sourcesContent":["import { Controller } from \"./controller.js\";\r\nimport { Name, State } from \"./types.js\";\r\n\r\n/**\r\n * System theme proxy.\r\n * \r\n * Used to detect system theme preference and listen for changes.\r\n */\r\nexport class System {\r\n\t/**\r\n\t * Media query list instance.\r\n\t */\r\n\tprivate mql?: MediaQueryList;\r\n\t/**\r\n\t * Handles the media query list listener.\r\n\t */\r\n\tprivate handler?: (e: MediaQueryListEvent) => void;\r\n\t/**\r\n\t * Controller instance.\r\n\t */\r\n\r\n\tconstructor(private ctl: Controller) { }\r\n\r\n\t/**\r\n\t * Get the effective theme by system preference.\r\n\t */\r\n\tget prefers(): Name {\r\n\t\treturn matchMedia?.(\"(prefers-color-scheme: dark)\").matches\r\n\t\t\t? this.ctl.config.dark\r\n\t\t\t: this.ctl.config.light;\r\n\t}\r\n\r\n\t/**\r\n\t * Check if the effective theme is dark.\r\n\t */\r\n\tget prefersDark(): boolean {\r\n\t\treturn this.prefers === this.ctl.config.dark;\r\n\t}\r\n\r\n\t/**\r\n\t * Check if the effective theme is light.\r\n\t */\r\n\tget prefersLight(): boolean {\r\n\t\treturn this.prefers === this.ctl.config.light;\r\n\t}\r\n\r\n\t/**\r\n\t * Start listening for system theme changes.\r\n\t * \r\n\t * @param onChange Callback to be called when the system theme changes.\r\n\t */\r\n\tstart(onChange: (state: Exclude<State, \"system\">) => void) {\r\n\t\tif (this.handler) return;\r\n\r\n\t\tthis.mql = matchMedia(\"(prefers-color-scheme: dark)\");\r\n\t\tthis.handler = e => onChange(e.matches ? \"dark\" : \"light\");\r\n\r\n\t\tthis.mql.addEventListener?.(\"change\", this.handler);\r\n\t}\r\n\r\n\t/**\r\n\t * Stop listening for system theme changes.\r\n\t */\r\n\tstop() {\r\n\t\tif (!this.mql || !this.handler) return;\r\n\r\n\t\tthis.mql.removeEventListener?.(\"change\", this.handler);\r\n\t\t\r\n\t\tthis.mql = this.handler = undefined;\r\n\t}\r\n}","import { ResolvedConfig } from \"./config.js\";\r\nimport { System } from \"./system.js\";\r\nimport { State, type Name } from \"./types.js\";\r\n\r\n/**\r\n * Resolve a theme state to an effective theme name.\r\n * \r\n * @see {@link State}\r\n * \r\n * @param theme Theme state\r\n * @param system System instance.\r\n * @param config Resolved set of configuration.\r\n * @returns \r\n */\r\nexport function resolveStateOf(state: State, system: System, config: ResolvedConfig): Name | null {\r\n\tif (state === config.system) {\r\n\t\treturn system.prefers;\r\n\t}\r\n\r\n\tif (state === config.light || state === config.dark) {\r\n\t\treturn state;\r\n\t}\r\n\r\n\treturn typeof state === \"string\" ? state : null;\r\n}\r\n\r\nexport function resolveRoot(root: unknown): HTMLElement {\r\n\tif (root instanceof HTMLElement) {\r\n\t\treturn root\r\n\t} else if (root instanceof Document) {\r\n\t\treturn root.documentElement\r\n\t} else if (root instanceof ShadowRoot && root.host instanceof HTMLElement) {\r\n\t\treturn root.host\r\n\t};\r\n\r\n\treturn document.documentElement;\r\n}","import { Adapter } from \"./adapter.js\";\r\nimport { RawConfig, ResolvedConfig } from \"./config.js\";\r\nimport { System } from \"./system.js\";\r\nimport { Name, State } from \"./types.js\";\r\nimport { resolveRoot, resolveStateOf } from \"./utils.js\";\r\n\r\n/**\r\n * Theme controller logic.\r\n */\r\nexport class Controller {\r\n\t/**\r\n\t * Resolved configuration.\r\n\t */\r\n\tconfig: ResolvedConfig;\r\n\t/**\r\n\t * System theme proxy.\r\n\t */\r\n\tprivate system: System;\r\n\r\n\tprivate adapters: Adapter<any>[] = [];\r\n\r\n\tconstructor(\r\n\t\tprivate _root: Document | ParentNode = document,\r\n\t\tcfg: RawConfig = {}\r\n\t) {\r\n\t\tthis.config = {\r\n\t\t\tattribute: cfg.attribute ?? \"data-theme\",\r\n\t\t\tstorage: {\r\n\t\t\t\tkey: cfg.storage?.key ?? \"theme\",\r\n\t\t\t},\r\n\t\t\tsystem: cfg.system ?? \"system\",\r\n\t\t\tlight: cfg.light ?? \"light\",\r\n\t\t\tdark: cfg.dark ?? \"dark\",\r\n\t\t\tinitial: cfg.initial\r\n\t\t};\r\n\r\n\t\tthis.system = new System(this);\r\n\r\n\t\tthis.syncSystemListener();\r\n\t}\r\n\r\n\t/**\r\n\t * The root element to apply theme to.\r\n\t */\r\n\tget root(): HTMLElement {\r\n\t\treturn resolveRoot(this._root);\r\n\t}\r\n\r\n\t/**\r\n\t * @param resolved resolve state and effective themes to their values.\r\n\t * \r\n\t * @example\r\n\t * \r\n\t * system = config.system;\r\n\t * light = config.light;\r\n\t * dark = config.dark;\r\n\t * \r\n\t * resolved = true;\r\n\t * [\r\n\t * \t\t\"system\": light | dark,\r\n\t * \t\t\"light\": light,\r\n\t * \t\t\"dark\": dark\r\n\t * ]\r\n\t * resolved = false;\r\n\t * [\r\n\t * \t\t\"system\": system,\r\n\t * \t\t\"light\": light,\r\n\t * \t\t\"dark\": dark\r\n\t * ]\r\n\t */\r\n\tactive(resolved: boolean = false): State | Name | null {\r\n\t\tconst state = this.state;\r\n\r\n\t\tif (!state) {\r\n\t\t\treturn this.config.initial || null;\r\n\t\t}\r\n\r\n\t\tconst theme = resolveStateOf(state, this.system, this.config);\r\n\r\n\t\tif (!theme) return null;\r\n\r\n\r\n\t\treturn resolved ? theme : state;\r\n\t}\r\n\r\n\t/**\r\n\t * Register an adapter.\r\n\t * \r\n\t * @description\r\n\t * Adapters are toys used to change themes.\r\n\t * They define where themes lives and how they work with the controller.\r\n\t * \r\n\t * @param adapter Adapter instance\r\n\t * @param options Adapter options\r\n\t */\r\n\tuse<T extends Adapter<any>>(adapter: T, options?: T extends Adapter<infer O> ? O : never) {\r\n\t\tif (this.adapters.includes(adapter)) {\r\n\t\t\tconsole.warn(`Adapter <${adapter.name}> is already installed.`);\r\n\t\t\treturn this;\r\n\t\t}\r\n\r\n\t\tthis.adapters.push(adapter);\r\n\r\n\t\ttry {\r\n\t\t\tadapter.setup?.(this, options as any);\r\n\t\t\tadapter.discover?.(this);\r\n\t\t\tadapter.bind?.(this);\r\n\t\t\tadapter.sync?.(this);\r\n\t\t} catch (err) {\r\n\t\t\tconsole.warn(`Adapter <${adapter.name}> failed to install.`, err);\r\n\t\t}\r\n\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * Set the effective theme from a theme state.\r\n\t * \r\n\t * @see {@link State}\r\n\t * @param state Theme State\r\n\t */\r\n\tset(state: State) {\r\n\t\tconst theme = resolveStateOf(state, this.system, this.config);\r\n\t\tif (!theme) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tthis.write(state);\r\n\r\n\t\tthis.syncSystemListener();\r\n\r\n\t\tthis.apply(theme);\r\n\r\n\t\tfor (const a of this.adapters) {\r\n\t\t\ta.sync?.(this);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Read the stored theme state from local storage.\r\n\t */\r\n\tget state(): State | null {\r\n\t\ttry {\r\n\t\t\treturn localStorage.getItem(this.config.storage.key) as State | null;\r\n\t\t} catch { return null; }\r\n\t}\r\n\r\n\t/**\r\n\t * Sync the system listener.\r\n\t */\r\n\tprivate syncSystemListener() {\r\n\t\tconst state = this.active();\r\n\r\n\t\tif (state === this.config.system) {\r\n\t\t\tthis.system.start(_state => {\r\n\t\t\t\tconst theme = resolveStateOf(\r\n\t\t\t\t\t\"system\",\r\n\t\t\t\t\tthis.system,\r\n\t\t\t\t\tthis.config\r\n\t\t\t\t);\r\n\t\t\t\tif (!theme) return;\r\n\r\n\t\t\t\tthis.apply(theme);\r\n\r\n\t\t\t\tfor (const a of this.adapters) {\r\n\t\t\t\t\ta.sync?.(this);\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t} else {\r\n\t\t\tthis.system.stop();\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Applies the effective theme name to the root.\r\n\t * \r\n\t * @param theme Effective theme name (e.g. `<dark>`, `<light>`, `mint`)\r\n\t */\r\n\tprivate apply(theme: Name) {\r\n\t\tthis.root.setAttribute(this.config.attribute, theme);\r\n\t}\r\n\r\n\t/**\r\n\t * Write theme state to local storage.\r\n\t * \r\n\t * @param theme Theme state\r\n\t */\r\n\tprivate write(theme: State) {\r\n\t\ttry {\r\n\t\t\tlocalStorage.setItem(this.config.storage.key, theme);\r\n\t\t} catch {\r\n\t\t\tconsole.warn(`Failed to write theme state to local storage.`);\r\n\t\t}\r\n\t}\r\n}"]}
|
|
1
|
+
{"version":3,"sources":["../../src/core/system.ts","../../src/core/utils.ts","../../src/core/controller.ts"],"names":["System","mql","handler","ctl","prefers","matchMedia","matches","config","dark","light","prefersDark","prefersLight","start","onChange","e","addEventListener","stop","removeEventListener","undefined","resolveStateOf","state","system","resolveRoot","root","HTMLElement","Document","documentElement","ShadowRoot","host","document","Controller","adapters","_root","cfg","attribute","storage","key","initial","syncSystemListener","active","resolved","theme","use","adapter","options","includes","console","warn","name","push","setup","discover","bind","sync","err","set","write","apply","a","localStorage","getItem","_state","setAttribute","setItem"],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * Copyright (c) 2026 GTF\r\n * SPDX-License-Identifier: Apache-2.0\r\n */\r\n\r\nimport { Controller } from \"./controller.js\";\r\nimport { Name, State } from \"./types.js\";\r\n\r\n/**\r\n * System theme proxy.\r\n * \r\n * Used to detect system theme preference and listen for changes.\r\n */\r\nexport class System {\r\n\t/**\r\n\t * Media query list instance.\r\n\t */\r\n\tprivate mql?: MediaQueryList;\r\n\t/**\r\n\t * Handles the media query list listener.\r\n\t */\r\n\tprivate handler?: (e: MediaQueryListEvent) => void;\r\n\t/**\r\n\t * Controller instance.\r\n\t */\r\n\r\n\tconstructor(private ctl: Controller) { }\r\n\r\n\t/**\r\n\t * Get the effective theme by system preference.\r\n\t */\r\n\tget prefers(): Name {\r\n\t\treturn matchMedia?.(\"(prefers-color-scheme: dark)\").matches\r\n\t\t\t? this.ctl.config.dark\r\n\t\t\t: this.ctl.config.light;\r\n\t}\r\n\r\n\t/**\r\n\t * Check if the effective theme is dark.\r\n\t */\r\n\tget prefersDark(): boolean {\r\n\t\treturn this.prefers === this.ctl.config.dark;\r\n\t}\r\n\r\n\t/**\r\n\t * Check if the effective theme is light.\r\n\t */\r\n\tget prefersLight(): boolean {\r\n\t\treturn this.prefers === this.ctl.config.light;\r\n\t}\r\n\r\n\t/**\r\n\t * Start listening for system theme changes.\r\n\t * \r\n\t * @param onChange Callback to be called when the system theme changes.\r\n\t */\r\n\tstart(onChange: (state: Exclude<State, \"system\">) => void) {\r\n\t\tif (this.handler) return;\r\n\r\n\t\tthis.mql = matchMedia(\"(prefers-color-scheme: dark)\");\r\n\t\tthis.handler = e => onChange(e.matches ? \"dark\" : \"light\");\r\n\r\n\t\tthis.mql.addEventListener?.(\"change\", this.handler);\r\n\t}\r\n\r\n\t/**\r\n\t * Stop listening for system theme changes.\r\n\t */\r\n\tstop() {\r\n\t\tif (!this.mql || !this.handler) return;\r\n\r\n\t\tthis.mql.removeEventListener?.(\"change\", this.handler);\r\n\t\t\r\n\t\tthis.mql = this.handler = undefined;\r\n\t}\r\n}","/**\r\n * Copyright (c) 2026 GTF\r\n * SPDX-License-Identifier: Apache-2.0\r\n */\r\n\r\nimport { ResolvedConfig } from \"./config.js\";\r\nimport { System } from \"./system.js\";\r\nimport { State, type Name } from \"./types.js\";\r\n\r\n/**\r\n * Resolve a theme state to an effective theme name.\r\n * \r\n * @see {@link State}\r\n * \r\n * @param theme Theme state\r\n * @param system System instance.\r\n * @param config Resolved set of configuration.\r\n * @returns \r\n */\r\nexport function resolveStateOf(state: State, system: System, config: ResolvedConfig): Name | null {\r\n\tif (state === config.system) {\r\n\t\treturn system.prefers;\r\n\t}\r\n\r\n\tif (state === config.light || state === config.dark) {\r\n\t\treturn state;\r\n\t}\r\n\r\n\treturn typeof state === \"string\" ? state : null;\r\n}\r\n\r\nexport function resolveRoot(root: unknown): HTMLElement {\r\n\tif (root instanceof HTMLElement) {\r\n\t\treturn root\r\n\t} else if (root instanceof Document) {\r\n\t\treturn root.documentElement\r\n\t} else if (root instanceof ShadowRoot && root.host instanceof HTMLElement) {\r\n\t\treturn root.host\r\n\t};\r\n\r\n\treturn document.documentElement;\r\n}","/**\r\n * Copyright (c) 2026 GTF\r\n * SPDX-License-Identifier: Apache-2.0\r\n */\r\n\r\nimport { Adapter } from \"./adapter.js\";\r\nimport { RawConfig, ResolvedConfig } from \"./config.js\";\r\nimport { System } from \"./system.js\";\r\nimport { Name, State } from \"./types.js\";\r\nimport { resolveRoot, resolveStateOf } from \"./utils.js\";\r\n\r\n/**\r\n * Theme controller logic.\r\n */\r\nexport class Controller {\r\n\t/**\r\n\t * Resolved configuration.\r\n\t */\r\n\tconfig: ResolvedConfig;\r\n\t/**\r\n\t * System theme proxy.\r\n\t */\r\n\tprivate system: System;\r\n\r\n\tprivate adapters: Adapter<any>[] = [];\r\n\r\n\tconstructor(\r\n\t\tprivate _root: Document | ParentNode = document,\r\n\t\tcfg: RawConfig = {}\r\n\t) {\r\n\t\tthis.config = {\r\n\t\t\tattribute: cfg.attribute ?? \"data-theme\",\r\n\t\t\tstorage: {\r\n\t\t\t\tkey: cfg.storage?.key ?? \"theme\",\r\n\t\t\t},\r\n\t\t\tsystem: cfg.system ?? \"system\",\r\n\t\t\tlight: cfg.light ?? \"light\",\r\n\t\t\tdark: cfg.dark ?? \"dark\",\r\n\t\t\tinitial: cfg.initial\r\n\t\t};\r\n\r\n\t\tthis.system = new System(this);\r\n\r\n\t\tthis.syncSystemListener();\r\n\t}\r\n\r\n\t/**\r\n\t * The root element to apply theme to.\r\n\t */\r\n\tget root(): HTMLElement {\r\n\t\treturn resolveRoot(this._root);\r\n\t}\r\n\r\n\t/**\r\n\t * @param resolved resolve state and effective themes to their values.\r\n\t * \r\n\t * @example\r\n\t * \r\n\t * system = config.system;\r\n\t * light = config.light;\r\n\t * dark = config.dark;\r\n\t * \r\n\t * resolved = true;\r\n\t * [\r\n\t * \t\t\"system\": light | dark,\r\n\t * \t\t\"light\": light,\r\n\t * \t\t\"dark\": dark\r\n\t * ]\r\n\t * resolved = false;\r\n\t * [\r\n\t * \t\t\"system\": system,\r\n\t * \t\t\"light\": light,\r\n\t * \t\t\"dark\": dark\r\n\t * ]\r\n\t */\r\n\tactive(resolved: boolean = false): State | Name | null {\r\n\t\tconst state = this.state;\r\n\r\n\t\tif (!state) {\r\n\t\t\treturn this.config.initial || null;\r\n\t\t}\r\n\r\n\t\tconst theme = resolveStateOf(state, this.system, this.config);\r\n\r\n\t\tif (!theme) return null;\r\n\r\n\r\n\t\treturn resolved ? theme : state;\r\n\t}\r\n\r\n\t/**\r\n\t * Register an adapter.\r\n\t * \r\n\t * @description\r\n\t * Adapters are toys used to change themes.\r\n\t * They define where themes lives and how they work with the controller.\r\n\t * \r\n\t * @param adapter Adapter instance\r\n\t * @param options Adapter options\r\n\t */\r\n\tuse<T extends Adapter<any>>(adapter: T, options?: T extends Adapter<infer O> ? O : never) {\r\n\t\tif (this.adapters.includes(adapter)) {\r\n\t\t\tconsole.warn(`Adapter <${adapter.name}> is already installed.`);\r\n\t\t\treturn this;\r\n\t\t}\r\n\r\n\t\tthis.adapters.push(adapter);\r\n\r\n\t\ttry {\r\n\t\t\tadapter.setup?.(this, options as any);\r\n\t\t\tadapter.discover?.(this);\r\n\t\t\tadapter.bind?.(this);\r\n\t\t\tadapter.sync?.(this);\r\n\t\t} catch (err) {\r\n\t\t\tconsole.warn(`Adapter <${adapter.name}> failed to install.`, err);\r\n\t\t}\r\n\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * Set the effective theme from a theme state.\r\n\t * \r\n\t * @see {@link State}\r\n\t * @param state Theme State\r\n\t */\r\n\tset(state: State) {\r\n\t\tconst theme = resolveStateOf(state, this.system, this.config);\r\n\t\tif (!theme) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tthis.write(state);\r\n\r\n\t\tthis.syncSystemListener();\r\n\r\n\t\tthis.apply(theme);\r\n\r\n\t\tfor (const a of this.adapters) {\r\n\t\t\ta.sync?.(this);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Read the stored theme state from local storage.\r\n\t */\r\n\tget state(): State | null {\r\n\t\ttry {\r\n\t\t\treturn localStorage.getItem(this.config.storage.key) as State | null;\r\n\t\t} catch { return null; }\r\n\t}\r\n\r\n\t/**\r\n\t * Sync the system listener.\r\n\t */\r\n\tprivate syncSystemListener() {\r\n\t\tconst state = this.active();\r\n\r\n\t\tif (state === this.config.system) {\r\n\t\t\tthis.system.start(_state => {\r\n\t\t\t\tconst theme = resolveStateOf(\r\n\t\t\t\t\t\"system\",\r\n\t\t\t\t\tthis.system,\r\n\t\t\t\t\tthis.config\r\n\t\t\t\t);\r\n\t\t\t\tif (!theme) return;\r\n\r\n\t\t\t\tthis.apply(theme);\r\n\r\n\t\t\t\tfor (const a of this.adapters) {\r\n\t\t\t\t\ta.sync?.(this);\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t} else {\r\n\t\t\tthis.system.stop();\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Applies the effective theme name to the root.\r\n\t * \r\n\t * @param theme Effective theme name (e.g. `<dark>`, `<light>`, `mint`)\r\n\t */\r\n\tprivate apply(theme: Name) {\r\n\t\tthis.root.setAttribute(this.config.attribute, theme);\r\n\t}\r\n\r\n\t/**\r\n\t * Write theme state to local storage.\r\n\t * \r\n\t * @param theme Theme state\r\n\t */\r\n\tprivate write(theme: State) {\r\n\t\ttry {\r\n\t\t\tlocalStorage.setItem(this.config.storage.key, theme);\r\n\t\t} catch {\r\n\t\t\tconsole.warn(`Failed to write theme state to local storage.`);\r\n\t\t}\r\n\t}\r\n}"]}
|
package/package.json
CHANGED
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@gottheflag/nova",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"license": "
|
|
6
|
-
"author": {
|
|
7
|
-
"name": "GTF",
|
|
8
|
-
"email": "dev@gottheflag.sa",
|
|
9
|
-
"url": "https://gottheflag.sa"
|
|
10
|
-
},
|
|
11
|
-
"description": "Theme state controller.",
|
|
12
|
-
"files": [
|
|
13
|
-
"dist"
|
|
14
|
-
],
|
|
15
|
-
"exports": {
|
|
16
|
-
".": {
|
|
17
|
-
"types": "./dist/core/index.d.ts",
|
|
18
|
-
"import": "./dist/core/index.js"
|
|
19
|
-
},
|
|
20
|
-
"./adapters": {
|
|
21
|
-
"types": "./dist/adapters/index.d.ts",
|
|
22
|
-
"import": "./dist/adapters/index.js"
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
"scripts": {
|
|
26
|
-
"dev": "concurrently -k -n TS,VITE -c blue,green \"tsc -p tsconfig.build.json --watch\" \"vite example\"",
|
|
27
|
-
"build": "tsup",
|
|
28
|
-
"typecheck": "tsc --noEmit",
|
|
29
|
-
"prepare": "pnpm run build"
|
|
30
|
-
},
|
|
31
|
-
"keywords": [
|
|
32
|
-
"theme",
|
|
33
|
-
"light",
|
|
34
|
-
"dark"
|
|
35
|
-
],
|
|
36
|
-
"publishConfig": {
|
|
37
|
-
"access": "public"
|
|
38
|
-
},
|
|
39
|
-
"repository": {
|
|
40
|
-
"type": "git",
|
|
41
|
-
"url": "https://github.com/gottheflag/nova"
|
|
42
|
-
},
|
|
43
|
-
"bugs": {
|
|
44
|
-
"url": "https://github.com/gottheflag/nova/issues"
|
|
45
|
-
},
|
|
46
|
-
"homepage": "https://github.com/gottheflag/nova#readme",
|
|
47
|
-
"devDependencies": {
|
|
48
|
-
"@swc/core": "^1.15.11",
|
|
49
|
-
"@types/node": "^25",
|
|
50
|
-
"concurrently": "^9.2.1",
|
|
51
|
-
"terser": "^5.46.0",
|
|
52
|
-
"tsup": "^8.5",
|
|
53
|
-
"tsx": "^4.21",
|
|
54
|
-
"typescript": "^5.9",
|
|
55
|
-
"vite": "^7.3.1"
|
|
56
|
-
}
|
|
57
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@gottheflag/nova",
|
|
3
|
+
"version": "0.1.0-beta.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "GTF",
|
|
8
|
+
"email": "dev@gottheflag.sa",
|
|
9
|
+
"url": "https://gottheflag.sa"
|
|
10
|
+
},
|
|
11
|
+
"description": "Theme state controller.",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/core/index.d.ts",
|
|
18
|
+
"import": "./dist/core/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./adapters": {
|
|
21
|
+
"types": "./dist/adapters/index.d.ts",
|
|
22
|
+
"import": "./dist/adapters/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"dev": "concurrently -k -n TS,VITE -c blue,green \"tsc -p tsconfig.build.json --watch\" \"vite example\"",
|
|
27
|
+
"build": "tsup",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"prepare": "pnpm run build"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"theme",
|
|
33
|
+
"light",
|
|
34
|
+
"dark"
|
|
35
|
+
],
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/gottheflag/nova"
|
|
42
|
+
},
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/gottheflag/nova/issues"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/gottheflag/nova#readme",
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@swc/core": "^1.15.11",
|
|
49
|
+
"@types/node": "^25",
|
|
50
|
+
"concurrently": "^9.2.1",
|
|
51
|
+
"terser": "^5.46.0",
|
|
52
|
+
"tsup": "^8.5",
|
|
53
|
+
"tsx": "^4.21",
|
|
54
|
+
"typescript": "^5.9",
|
|
55
|
+
"vite": "^7.3.1"
|
|
56
|
+
}
|
|
57
|
+
}
|