aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/theme.svelte.ts
blob: 956738b9970c8aa3bb7c44807a723ca296b4cc06 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import { onDestroy, onMount } from 'svelte';
import { themeStorage } from './storage';
import { page } from '$app/state';

export enum Theme {
  Light = 'light',
  Dark = 'dark',
}
const oppositeThemeMappings: Record<Theme, Theme> = {
  [Theme.Light]: Theme.Dark,
  [Theme.Dark]: Theme.Light
};
export class CTheme {
  public rawTheme = $state(null as null | Theme);
  public theme = $derived(this.rawTheme ?? Theme.Dark);
  public opposite = $derived(oppositeThemeMappings[this.theme]);
  /**
   * Marks the page a component renders on as fully compatible/tested with theming.
   * 
   * Call during component init.
   */
  public themeCompatible() {
    onMount(() => {
      const t =
        page.url.searchParams.get('theme') ?? themeStorage.getItem('theme');
      if (theme.isThemeValid(t)) theme.set(t);
    });
    $effect(() => {
      document.documentElement.setAttribute('data-blog-theme', theme.theme);
      page.url.searchParams.delete('theme');
    });
    onDestroy(() => {
      if (typeof document !== 'undefined')
        document.documentElement.removeAttribute('data-blog-theme');
    });
  }
  public isThemeValid(theme: string | null): theme is Theme {
    return Object.values(Theme).includes(theme as unknown as Theme);
  }
  public set(theme: Theme) {
    themeStorage.setItem('theme', theme);
    this.theme = theme;
  }
  public flip() {
    this.set(this.opposite);
  }
};
export const theme = new CTheme();
export default theme;