Skip to content

Commit d75d284

Browse files
committed
next: mode watcher store -> state
1 parent bf7b42a commit d75d284

29 files changed

Lines changed: 625 additions & 673 deletions

packages/mode-watcher/package.json

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@
3838
"devDependencies": {
3939
"@playwright/test": "^1.28.1",
4040
"@sveltejs/adapter-auto": "^3.0.0",
41-
"@sveltejs/kit": "^2.0.0",
42-
"@sveltejs/package": "^2.0.0",
41+
"@sveltejs/kit": "^2.20.7",
42+
"@sveltejs/package": "^2.3.11",
4343
"@sveltejs/vite-plugin-svelte": "^3.0.0",
4444
"@svitejs/changesets-changelog-github-compact": "^1.1.0",
4545
"@testing-library/dom": "^10.3.1",
@@ -52,16 +52,20 @@
5252
"postcss": "^8.4.24",
5353
"postcss-load-config": "^4.0.1",
5454
"publint": "^0.1.9",
55-
"svelte": "^4.0.5",
56-
"svelte-check": "^3.4.3",
55+
"svelte": "^5.27.0",
56+
"svelte-check": "^4.1.6",
5757
"tailwindcss": "^3.3.2",
58-
"tslib": "^2.4.1",
59-
"typescript": "^5.0.0",
58+
"tslib": "^2.8.1",
59+
"typescript": "^5.8.3",
6060
"vite": "^5.0.0",
6161
"vitest": "^1.0.0",
6262
"vitest-localstorage-mock": "^0.1.2"
6363
},
6464
"svelte": "./dist/index.js",
6565
"types": "./dist/index.d.ts",
66-
"type": "module"
66+
"type": "module",
67+
"dependencies": {
68+
"runed": "^0.25.0",
69+
"svelte-toolbelt": "^0.7.1"
70+
}
6771
}

packages/mode-watcher/src/lib/components/mode-watcher-full.svelte

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
<script lang="ts">
2-
import { setInitialMode } from "./mode.js";
3-
import type { ThemeColors } from "./types.js";
2+
import { setInitialMode } from "../mode.js";
3+
import type { ThemeColors } from "../types.js";
44
5-
export let trueNonce: string = "";
6-
export let initConfig: Parameters<typeof setInitialMode>[0];
7-
export let themeColors: ThemeColors = undefined;
5+
let {
6+
trueNonce = "",
7+
initConfig,
8+
themeColors,
9+
}: {
10+
trueNonce: string;
11+
initConfig: Parameters<typeof setInitialMode>[0];
12+
themeColors: ThemeColors;
13+
} = $props();
814
</script>
915

1016
<svelte:head>
@@ -14,7 +20,7 @@
1420
<!-- but that snippet does not run in vitest -->
1521
<meta name="theme-color" content={themeColors.dark} />
1622
{/if}
17-
<!-- eslint-disable-next-line svelte/no-at-html-tags, prefer-template -->
23+
<!-- eslint-disable-next-line svelte/no-at-html-tags, prefer-template, svelte/no-unused-svelte-ignore --><!-- svelte-ignore hydration_html_changed -->
1824
{@html `<script${trueNonce ? ` nonce=${trueNonce}` : ""}>(` +
1925
setInitialMode.toString() +
2026
`)(` +

packages/mode-watcher/src/lib/components/mode-watcher-lite.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
2-
import type { ThemeColors } from "./types.js";
2+
import type { ThemeColors } from "../types.js";
33
4-
export let themeColors: ThemeColors = undefined;
4+
let { themeColors }: { themeColors: ThemeColors } = $props();
55
</script>
66

77
{#if themeColors}
Lines changed: 56 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,82 @@
11
<script lang="ts">
22
import { onMount } from "svelte";
3+
import ModeWatcherLite from "./mode-watcher-lite.svelte";
4+
import ModeWatcherFull from "./mode-watcher-full.svelte";
35
import {
4-
defineConfig,
5-
disableTransitions as disableTransitionsStore,
6-
mode,
7-
setMode,
8-
setTheme,
6+
darkClassNames,
7+
disableTransitions,
8+
lightClassNames,
9+
modeStorageKey,
910
systemPrefersMode,
10-
theme,
11-
themeColors as themeColorsStore,
12-
} from "./mode.js";
11+
themeColors,
12+
themeStorageKey,
13+
} from "$lib/states.svelte.js";
14+
import type { ModeWatcherProps } from "$lib/types.js";
15+
import { isValidMode } from "$lib/modes.js";
16+
import { defineConfig, setMode, setTheme } from "$lib/mode.js";
1317
14-
import type { Mode, ModeWatcherProps, ThemeColors } from "./types.js";
15-
import {
16-
darkClassNames as darkClassNamesStore,
17-
isValidMode,
18-
lightClassNames as lightClassNamesStore,
19-
modeStorageKey as modeStorageKeyStore,
20-
themeStorageKey as themeStorageKeyStore,
21-
} from "./stores.js";
22-
import ModeWatcherLite from "./mode-watcher-lite.svelte";
23-
import ModeWatcherFull from "./mode-watcher-full.svelte";
18+
let {
19+
track = true,
20+
defaultMode = "system",
21+
themeColors: themeColorsProp,
22+
disableTransitions: disableTransitionsProp = true,
23+
darkClassNames: darkClassNamesProp = ["dark"],
24+
lightClassNames: lightClassNamesProp = [],
25+
defaultTheme = "",
26+
nonce = "",
27+
themeStorageKey: themeStorageKeyProp = "mode-watcher-theme",
28+
modeStorageKey: modeStorageKeyProp = "mode-watcher-mode",
29+
disableHeadScriptInjection = false,
30+
}: ModeWatcherProps = $props();
2431
25-
type $$Props = ModeWatcherProps;
32+
$effect.pre(() => {
33+
disableTransitions.current = disableTransitionsProp;
34+
});
2635
27-
export let track = true;
28-
export let defaultMode: Mode = "system";
29-
export let themeColors: ThemeColors = undefined;
30-
export let disableTransitions = true;
31-
export let darkClassNames: string[] = ["dark"];
32-
export let lightClassNames: string[] = [];
33-
export let defaultTheme: string = "";
34-
export let nonce: string = "";
35-
export let themeStorageKey: string = "mode-watcher-theme";
36-
export let modeStorageKey: string = "mode-watcher-mode";
37-
export let disableHeadScriptInjection = false;
36+
$effect.pre(() => {
37+
themeColors.current = themeColorsProp;
38+
});
3839
39-
$: disableTransitionsStore.set(disableTransitions);
40-
$: themeColorsStore.set(themeColors);
41-
$: darkClassNamesStore.set(darkClassNames);
42-
$: lightClassNamesStore.set(lightClassNames);
43-
$: modeStorageKeyStore.set(modeStorageKey);
44-
$: themeStorageKeyStore.set(themeStorageKey);
40+
$effect.pre(() => {
41+
darkClassNames.current = darkClassNamesProp;
42+
});
43+
44+
$effect.pre(() => {
45+
lightClassNames.current = lightClassNamesProp;
46+
});
47+
48+
$effect.pre(() => {
49+
modeStorageKey.current = modeStorageKeyProp;
50+
});
51+
52+
$effect.pre(() => {
53+
themeStorageKey.current = themeStorageKeyProp;
54+
});
4555
4656
onMount(() => {
47-
const modeUnsubscribe = mode.subscribe(() => {});
48-
const themeUnsubscribe = theme.subscribe(() => {});
4957
systemPrefersMode.tracking(track);
5058
systemPrefersMode.query();
51-
const localStorageMode = localStorage.getItem($modeStorageKeyStore);
59+
const localStorageMode = localStorage.getItem(modeStorageKey.current);
5260
setMode(isValidMode(localStorageMode) ? localStorageMode : defaultMode);
53-
const localStorageTheme = localStorage.getItem($themeStorageKeyStore);
61+
const localStorageTheme = localStorage.getItem(themeStorageKey.current);
5462
setTheme(localStorageTheme || defaultTheme);
55-
56-
return () => {
57-
modeUnsubscribe();
58-
themeUnsubscribe();
59-
};
6063
});
6164
6265
const initConfig = defineConfig({
6366
defaultMode,
64-
themeColors,
65-
darkClassNames,
66-
lightClassNames,
67+
themeColors: themeColorsProp,
68+
darkClassNames: darkClassNamesProp,
69+
lightClassNames: lightClassNamesProp,
6770
defaultTheme,
68-
modeStorageKey,
69-
themeStorageKey,
71+
modeStorageKey: modeStorageKeyProp,
72+
themeStorageKey: themeStorageKeyProp,
7073
});
7174
72-
$: trueNonce = typeof window === "undefined" ? nonce : "";
75+
const trueNonce = $derived(typeof window === "undefined" ? nonce : "");
7376
</script>
7477

7578
{#if disableHeadScriptInjection}
76-
<ModeWatcherLite {themeColors} />
79+
<ModeWatcherLite themeColors={themeColors.current} />
7780
{:else}
78-
<ModeWatcherFull {trueNonce} {initConfig} {themeColors} />
81+
<ModeWatcherFull {trueNonce} {initConfig} themeColors={themeColors.current} />
7982
{/if}

packages/mode-watcher/src/lib/components/types.ts

Whitespace-only changes.

packages/mode-watcher/src/lib/index.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
import {
22
generateSetInitialModeExpression,
3-
mode,
4-
modeStorageKey,
53
resetMode,
64
setMode,
75
setTheme,
8-
systemPrefersMode,
9-
theme,
10-
themeStorageKey,
116
toggleMode,
12-
userPrefersMode,
137
} from "./mode.js";
8+
import {
9+
mode,
10+
modeStorageKey,
11+
themeStorageKey,
12+
theme,
13+
userPrefersMode,
14+
systemPrefersMode,
15+
} from "./states.svelte.js";
1416

1517
export {
1618
generateSetInitialModeExpression,
@@ -26,4 +28,4 @@ export {
2628
themeStorageKey,
2729
};
2830

29-
export { default as ModeWatcher } from "./mode-watcher.svelte";
31+
export { default as ModeWatcher } from "./components/mode-watcher.svelte";
Lines changed: 50 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,24 @@
1-
import { get } from "svelte/store";
2-
import {
3-
derivedMode,
4-
derivedTheme,
5-
disableTransitions,
6-
modeStorageKey,
7-
systemPrefersMode,
8-
themeColors,
9-
themeStorageKey,
10-
theme as themeStore,
11-
userPrefersMode,
12-
} from "./stores.js";
1+
import { derivedMode, customTheme, userPrefersMode } from "./states.svelte.js";
132
import type { Mode, ThemeColors } from "./types.js";
143

154
/** Toggle between light and dark mode */
165
export function toggleMode(): void {
17-
userPrefersMode.set(get(derivedMode) === "dark" ? "light" : "dark");
6+
userPrefersMode.current = derivedMode.current === "dark" ? "light" : "dark";
187
}
198

209
/** Set the mode to light or dark */
2110
export function setMode(mode: Mode): void {
22-
userPrefersMode.set(mode);
11+
userPrefersMode.current = mode;
2312
}
2413

2514
/** Reset the mode to operating system preference */
2615
export function resetMode(): void {
27-
userPrefersMode.set("system");
16+
userPrefersMode.current = "system";
2817
}
2918

3019
/** Set the theme to a custom value */
31-
export function setTheme(theme: string): void {
32-
themeStore.set(theme);
20+
export function setTheme(newTheme: string): void {
21+
customTheme.current = newTheme;
3322
}
3423

3524
export function defineConfig(config: SetInitialModeArgs) {
@@ -46,63 +35,58 @@ type SetInitialModeArgs = {
4635
themeStorageKey?: string;
4736
};
4837

38+
// @ts-expect-error - this is fine
39+
// prettier-ignore
40+
export function setInitialMode(a){const{defaultMode:b="system",themeColors:c,darkClassNames:d=["dark"],lightClassNames:e=[],defaultTheme:f="",modeStorageKey:g="mode-watcher-mode",themeStorageKey:h="mode-watcher-theme"}=a||{};const k=document.documentElement,l=localStorage.getItem(g)??b,m=localStorage.getItem(h)??f,n=l=="light"||(l=="system"&&matchMedia("(prefers-color-scheme: light)").matches),p=(q,r)=>q.length&&k.classList[r](...q);if(n){p(d,"remove");p(e,"add")}else{p(e,"remove");p(d,"add")}k.style.colorScheme=n?"light":"dark";let s;if(c&&(s=document.querySelector('meta[name="theme-color"]')))s.setAttribute("content",n?c.light:c.dark);if(m){k.setAttribute("data-theme",m);localStorage.setItem(h,m)}localStorage.setItem(g,l)}
41+
42+
// The below code is minified above to reduce client bundle size when stringifying this into a
43+
// script tag.
4944
/** Used to set the mode on initial page load to prevent FOUC */
50-
export function setInitialMode({
51-
defaultMode = "system",
52-
themeColors,
53-
darkClassNames = ["dark"],
54-
lightClassNames = [],
55-
defaultTheme = "",
56-
modeStorageKey = "mode-watcher-mode",
57-
themeStorageKey = "mode-watcher-theme",
58-
}: SetInitialModeArgs) {
59-
const rootEl = document.documentElement;
60-
const mode = localStorage.getItem(modeStorageKey) || defaultMode;
61-
const theme = localStorage.getItem(themeStorageKey) || defaultTheme;
62-
const light =
63-
mode === "light" ||
64-
(mode === "system" && window.matchMedia("(prefers-color-scheme: light)").matches);
65-
if (light) {
66-
if (darkClassNames.length) rootEl.classList.remove(...darkClassNames);
67-
if (lightClassNames.length) rootEl.classList.add(...lightClassNames);
68-
} else {
69-
if (lightClassNames.length) rootEl.classList.remove(...lightClassNames);
70-
if (darkClassNames.length) rootEl.classList.add(...darkClassNames);
71-
}
72-
rootEl.style.colorScheme = light ? "light" : "dark";
45+
// export function setInitialMode({
46+
// defaultMode = "system",
47+
// themeColors,
48+
// darkClassNames = ["dark"],
49+
// lightClassNames = [],
50+
// defaultTheme = "",
51+
// modeStorageKey = "mode-watcher-mode",
52+
// themeStorageKey = "mode-watcher-theme",
53+
// }: SetInitialModeArgs) {
54+
// const rootEl = document.documentElement;
55+
// const mode = localStorage.getItem(modeStorageKey) ?? defaultMode;
56+
// const theme = localStorage.getItem(themeStorageKey) ?? defaultTheme;
57+
// const light =
58+
// mode === "light" ||
59+
// (mode === "system" && window.matchMedia("(prefers-color-scheme: light)").matches);
60+
// if (light) {
61+
// if (darkClassNames.length) rootEl.classList.remove(...darkClassNames);
62+
// if (lightClassNames.length) rootEl.classList.add(...lightClassNames);
63+
// } else {
64+
// if (lightClassNames.length) rootEl.classList.remove(...lightClassNames);
65+
// if (darkClassNames.length) rootEl.classList.add(...darkClassNames);
66+
// }
67+
// rootEl.style.colorScheme = light ? "light" : "dark";
7368

74-
if (themeColors) {
75-
const themeMetaEl = document.querySelector('meta[name="theme-color"]');
76-
if (themeMetaEl) {
77-
themeMetaEl.setAttribute(
78-
"content",
79-
mode === "light" ? themeColors.light : themeColors.dark
80-
);
81-
}
82-
}
69+
// if (themeColors) {
70+
// const themeMetaEl = document.querySelector('meta[name="theme-color"]');
71+
// if (themeMetaEl) {
72+
// themeMetaEl.setAttribute(
73+
// "content",
74+
// mode === "light" ? themeColors.light : themeColors.dark
75+
// );
76+
// }
77+
// }
8378

84-
if (theme) {
85-
rootEl.setAttribute("data-theme", theme);
86-
localStorage.setItem(themeStorageKey, theme);
87-
}
79+
// if (theme) {
80+
// rootEl.setAttribute("data-theme", theme);
81+
// localStorage.setItem(themeStorageKey, theme);
82+
// }
8883

89-
localStorage.setItem(modeStorageKey, mode);
90-
}
84+
// localStorage.setItem(modeStorageKey, mode);
85+
// }
9186

9287
/**
9388
* A type-safe way to generate the source expression used to set the initial mode and avoid FOUC.
9489
*/
9590
export function generateSetInitialModeExpression(config: SetInitialModeArgs = {}): string {
9691
return `(${setInitialMode.toString()})(${JSON.stringify(config)});`;
9792
}
98-
99-
export {
100-
modeStorageKey,
101-
themeStorageKey,
102-
derivedTheme as theme,
103-
userPrefersMode,
104-
systemPrefersMode,
105-
derivedMode as mode,
106-
themeColors,
107-
disableTransitions,
108-
};

0 commit comments

Comments
 (0)