aboutsummaryrefslogtreecommitdiffstats
path: root/src/utils/hooks/use-prism-theme/use-prism-theme.ts
blob: 4caec6ca0cb6b4b5d0d32c1a316eeff6fa4fef46 (plain)
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
import { useCallback, useContext, useEffect } from 'react';
import { themeValidator as isValidTheme } from '../../helpers';
import { PrismThemeContext } from '../../providers/prism-theme-provider';

export const usePrismTheme = () => {
  const { attribute, resolvedTheme, setTheme, theme } =
    useContext(PrismThemeContext);
  const currentTheme = theme === 'system' ? resolvedTheme : theme;

  const handleMutations: MutationCallback = useCallback(
    (mutations) => {
      for (const mutation of mutations) {
        if (mutation.target.nodeName.toLowerCase() !== 'pre') return;

        const newTheme = (mutation.target as HTMLPreElement).getAttribute(
          attribute
        );

        if (isValidTheme(newTheme) && newTheme !== theme) setTheme(newTheme);
      }
    },
    [attribute, setTheme, theme]
  );

  useEffect(() => {
    if (typeof window === 'undefined') return undefined;

    const preElements = document.getElementsByTagName('pre');
    const observer = new MutationObserver(handleMutations);

    for (const preEl of Array.from(preElements)) {
      if (preEl.className.includes('language-')) {
        preEl.setAttribute(attribute, theme);
        observer.observe(preEl, {
          attributes: true,
          attributeFilter: [attribute],
        });
      }
    }

    return () => {
      observer.disconnect();
    };
  }, [attribute, handleMutations, theme]);

  const toggleTheme = useCallback(() => {
    setTheme(() => {
      if (currentTheme === 'dark') return 'light';
      return 'dark';
    });
  }, [currentTheme, setTheme]);

  return { currentTheme, resolvedTheme, setTheme, theme, toggleTheme };
};