All files / hooks use-theme.ts

100% Statements 17/17
100% Branches 4/4
100% Functions 6/6
100% Lines 17/17

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68        3x 6x 6x   6x 4x 3x   3x 2x     3x   3x 3x     1x       6x 5x     6x     3x   3x                                                                
import { useEffect, useState } from 'react';
import { useLocalStorage } from './use-storage';
 
export function createThemeHook<T extends string[]>(options: createThemeHook.Options<T>) {
  const hook = () => {
    const [mode, setMode] = useLocalStorage<T[number] | 'auto'>('theme', 'auto');
    const [theme, setTheme] = useState<T[number]>(createThemeHook.getTheme(mode, options));
 
    useEffect(() => {
      if (mode === 'auto') {
        const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
 
        const listener = (event: MediaQueryListEvent) => {
          setTheme(event.matches ? options.dark : options.light);
        };
 
        mediaQuery.addEventListener('change', listener);
 
        return () => {
          mediaQuery.removeEventListener('change', listener);
        };
      } else {
        setTheme(mode);
      }
    }, [mode, setTheme]);
 
    useEffect(() => {
      document.body.setAttribute('data-theme', theme);
    }, [theme]);
 
    return [mode, setMode, theme] as const;
  };
 
  hook.$modes = 'auto' as T[number] | 'auto';
 
  return hook;
}
 
/* c8 ignore start */
export namespace createThemeHook {
  /* c8 ignore end */
 
  export type Options<T extends string[]> = {
    /**
     * A list of the available themes.
     */
    themes: T;
 
    /**
     * This theme will be used when light mode is detected.
     */
    light: T[number];
 
    /**
     * This theme will be used when dark mode is detected.
     */
    dark: T[number];
  };
 
  export function getTheme<T extends string[]>(mode: T[number] | 'auto', options: Options<T>): T[number] {
    if (mode === 'auto') {
      return window.matchMedia('(prefers-color-scheme: dark)').matches ? options.dark : options.light;
    }
 
    return mode;
  }
}