Skip to content

Commit

Permalink
cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
huntabyte committed Jan 22, 2024
1 parent 0fa1017 commit cff725a
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 45 deletions.
2 changes: 1 addition & 1 deletion src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
setMode,
toggleMode,
resetMode
} from './mode';
} from './mode.js';

export {
setMode,
Expand Down
33 changes: 17 additions & 16 deletions src/lib/mode-watcher.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
mode,
themeColors as themeColorsStore
} from './mode.js';
import type { Mode, ThemeColors } from './types.js';
export let track = true;
export let defaultMode: 'light' | 'dark' | 'system' = 'system';
export let defaultMode: Mode = 'system';
// TODO: how can I pass this prop to stores in stores.ts BEFORE they are initialized??
export let themeColors: { dark: string; light: string } | undefined = undefined;
export let themeColors: ThemeColors = undefined;
themeColorsStore.set(themeColors);
Expand All @@ -26,23 +27,23 @@
};
});
function setInitialMode(
defaultMode: 'light' | 'dark' | 'system',
themeColors?: { dark: string; light: string }
) {
const elem = document.documentElement,
mode = localStorage.getItem('mode') || defaultMode,
light =
mode === 'light' ||
(mode === 'system' && window.matchMedia('(prefers-color-scheme: light)').matches);
function setInitialMode(defaultMode: Mode, themeColors?: ThemeColors) {
const rootEl = document.documentElement;
const mode = localStorage.getItem('mode') || defaultMode;
const light =
mode === 'light' ||
(mode === 'system' && window.matchMedia('(prefers-color-scheme: light)').matches);
elem.classList[light ? 'remove' : 'add']('dark');
elem.style.colorScheme = light ? 'light' : 'dark';
rootEl.classList[light ? 'remove' : 'add']('dark');
rootEl.style.colorScheme = light ? 'light' : 'dark';
if (themeColors) {
const te = document.querySelector('meta[name="theme-color"]');
if (te) {
te.setAttribute('content', mode === 'light' ? themeColors.light : themeColors.dark);
const themeMetaEl = document.querySelector('meta[name="theme-color"]');
if (themeMetaEl) {
themeMetaEl.setAttribute(
'content',
mode === 'light' ? themeColors.light : themeColors.dark
);
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/lib/mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import {
derivedMode,
themeColors
} from './stores.js';
import type { Mode } from './types.js';

/** Toggle between light and dark mode */
export function toggleMode(): void {
userPrefersMode.set(get(derivedMode) === 'dark' ? 'light' : 'dark');
}

/** Set the mode to light or dark */
export function setMode(mode: 'dark' | 'light' | 'system'): void {
export function setMode(mode: Mode): void {
userPrefersMode.set(mode);
}

Expand Down
65 changes: 38 additions & 27 deletions src/lib/stores.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { writable, derived } from 'svelte/store';
import { withoutTransition } from './without-transition.js';
import type { ThemeColors } from './types.js';
import type { Mode, ThemeColors } from './types.js';

// saves having to branch for server vs client
const noopStorage = {
Expand All @@ -13,10 +13,13 @@ const noopStorage = {
// whether we are running on server vs client
const isBrowser = typeof document !== 'undefined';

// the modes that are supported, used for validation & type derivation
export const modes = ['dark', 'light', 'system'] as const;

/**
* The key used to store the mode in localStorage.
*/
export const localStorageKey = 'mode';
export const localStorageKey = 'mode-watcher-mode';
/**
* Writable store that represents the user's preferred mode (`"dark"`, `"light"` or `"system"`)
*/
Expand All @@ -39,21 +42,26 @@ function createUserPrefersMode() {
const defaultValue = 'system';

const storage = isBrowser ? localStorage : noopStorage;
let value = (storage.getItem(localStorageKey) as 'dark' | 'light' | 'system') || defaultValue;
const initialValue = storage.getItem(localStorageKey);

let value = isValidMode(initialValue) ? initialValue : defaultValue;

const { subscribe, set: _set } = writable(value, () => {
if (isBrowser) {
const handler = (e: StorageEvent) => {
if (e.key === localStorageKey) {
_set((value = (e.newValue as 'dark' | 'light' | 'system') || defaultValue));
}
};
addEventListener('storage', handler);
return () => removeEventListener('storage', handler);
}
if (!isBrowser) return;
const handler = (e: StorageEvent) => {
if (e.key !== localStorageKey) return;
const newValue = e.newValue;
if (isValidMode(newValue)) {
_set((value = newValue));
} else {
_set((value = defaultValue));
}
};
addEventListener('storage', handler);
return () => removeEventListener('storage', handler);
});

function set(v: 'dark' | 'light' | 'system') {
function set(v: Mode) {
_set((value = v));
storage.setItem(localStorageKey, value);
}
Expand All @@ -69,26 +77,25 @@ function createSystemMode() {
let track = true;

const { subscribe, set } = writable<'dark' | 'light' | undefined>(defaultValue, () => {
if (isBrowser) {
const handler = (e: MediaQueryListEvent) => {
if (track) {
set(e.matches ? 'light' : 'dark');
}
};
const mediaQueryState = window.matchMedia('(prefers-color-scheme: light)');
mediaQueryState.addEventListener('change', handler);
return () => mediaQueryState.removeEventListener('change', handler);
}
if (!isBrowser) return;

const handler = (e: MediaQueryListEvent) => {
if (!track) return;
set(e.matches ? 'light' : 'dark');
};

const mediaQueryState = window.matchMedia('(prefers-color-scheme: light)');
mediaQueryState.addEventListener('change', handler);
return () => mediaQueryState.removeEventListener('change', handler);
});

/**
* Query system preferences and update the store.
*/
function query() {
if (isBrowser) {
const mediaQueryState = window.matchMedia('(prefers-color-scheme: light)');
set(mediaQueryState.matches ? 'light' : 'dark');
}
if (!isBrowser) return;
const mediaQueryState = window.matchMedia('(prefers-color-scheme: light)');
set(mediaQueryState.matches ? 'light' : 'dark');
}

/**
Expand Down Expand Up @@ -139,3 +146,7 @@ function createDerivedMode() {
subscribe
};
}

function isValidMode(value: unknown): value is Mode {
return modes.includes(value as Mode);
}
3 changes: 3 additions & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
import type { modes } from './stores';

export type Mode = typeof modes[number];
export type ThemeColors = { dark: string; light: string } | undefined;

0 comments on commit cff725a

Please sign in to comment.