diff options
Diffstat (limited to 'src/utils/hooks/use-prism-theme/use-prism-theme.ts')
| -rw-r--r-- | src/utils/hooks/use-prism-theme/use-prism-theme.ts | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/src/utils/hooks/use-prism-theme/use-prism-theme.ts b/src/utils/hooks/use-prism-theme/use-prism-theme.ts new file mode 100644 index 0000000..4caec6c --- /dev/null +++ b/src/utils/hooks/use-prism-theme/use-prism-theme.ts @@ -0,0 +1,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 }; +}; |
