summaryrefslogtreecommitdiffstats
path: root/src/utils
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-02-01 15:15:55 +0100
committerArmand Philippot <git@armandphilippot.com>2022-02-01 15:15:55 +0100
commit1e370817560c905a0a3520e245c317f308b6a5e7 (patch)
treef5a1e2c3b8ddbff2cb4d94eff82830388158e4c9 /src/utils
parent8f8a3957b5d3b33bafaa0a6afe4187f75d6dd2b7 (diff)
chore: add a new settings to handle prism theme from toolbar
Diffstat (limited to 'src/utils')
-rw-r--r--src/utils/providers/prism.tsx162
1 files changed, 162 insertions, 0 deletions
diff --git a/src/utils/providers/prism.tsx b/src/utils/providers/prism.tsx
new file mode 100644
index 0000000..840f9d8
--- /dev/null
+++ b/src/utils/providers/prism.tsx
@@ -0,0 +1,162 @@
+import { LocalStorage } from '@services/local-storage';
+import {
+ createContext,
+ FC,
+ useCallback,
+ useContext,
+ useEffect,
+ useState,
+} from 'react';
+
+export type PrismTheme = 'dark' | 'light' | 'system';
+export type ResolvedPrismTheme = 'dark' | 'light';
+
+export type UsePrismThemeProps = {
+ themes: PrismTheme[];
+ theme?: PrismTheme;
+ setTheme: (theme: PrismTheme) => void;
+ resolvedTheme?: ResolvedPrismTheme;
+ codeBlocks?: NodeListOf<HTMLPreElement>;
+ setCodeBlocks: (codeBlocks: NodeListOf<HTMLPreElement>) => void;
+};
+
+export type PrismThemeProviderProps = {
+ attribute?: string;
+ storageKey?: string;
+ themes?: PrismTheme[];
+};
+
+export const PrismThemeContext = createContext<UsePrismThemeProps>({
+ themes: ['dark', 'light', 'system'],
+ setTheme: (_) => {},
+ setCodeBlocks: (_) => {},
+});
+
+export const usePrismTheme = () => useContext(PrismThemeContext);
+
+const prefersDarkScheme = () => {
+ if (typeof window === 'undefined') return;
+
+ return (
+ window.matchMedia &&
+ window.matchMedia('(prefers-color-scheme: dark)').matches
+ );
+};
+
+const isValidTheme = (theme: string): boolean => {
+ return theme === 'dark' || theme === 'light' || theme === 'system';
+};
+
+const getTheme = (key: string): PrismTheme | undefined => {
+ if (typeof window === 'undefined') return undefined;
+ const storageValue = LocalStorage.get(key);
+
+ return storageValue && isValidTheme(storageValue)
+ ? (storageValue as PrismTheme)
+ : undefined;
+};
+
+export const PrismThemeProvider: FC<PrismThemeProviderProps> = ({
+ attribute = 'data-prismjs-color-scheme',
+ storageKey = 'prismjs-color-scheme',
+ themes = ['dark', 'light', 'system'],
+ children,
+}) => {
+ const getThemeFromSystem = useCallback(() => {
+ return prefersDarkScheme() ? 'dark' : 'light';
+ }, []);
+
+ const [prismTheme, setPrismTheme] = useState<PrismTheme>(
+ getTheme(storageKey) || 'system'
+ );
+
+ const updateTheme = (theme: PrismTheme) => {
+ setPrismTheme(theme);
+ };
+
+ useEffect(() => {
+ LocalStorage.set(storageKey, prismTheme);
+ }, [prismTheme, storageKey]);
+
+ const [resolvedTheme, setResolvedTheme] = useState<ResolvedPrismTheme>();
+
+ useEffect(() => {
+ if (prismTheme === 'dark' || prismTheme === 'light') {
+ setResolvedTheme(prismTheme);
+ } else {
+ setResolvedTheme(getThemeFromSystem());
+ }
+ }, [prismTheme, getThemeFromSystem]);
+
+ const updateResolvedTheme = useCallback(() => {
+ setResolvedTheme(getThemeFromSystem());
+ }, [getThemeFromSystem]);
+
+ useEffect(() => {
+ window
+ .matchMedia('(prefers-color-scheme: dark)')
+ .addEventListener('change', updateResolvedTheme);
+
+ return () =>
+ window
+ .matchMedia('(prefers-color-scheme: dark)')
+ .removeEventListener('change', updateResolvedTheme);
+ }, [updateResolvedTheme]);
+
+ const [preTags, setPreTags] = useState<NodeListOf<HTMLPreElement>>();
+
+ const updatePreTags = useCallback((tags: NodeListOf<HTMLPreElement>) => {
+ setPreTags(tags);
+ }, []);
+
+ const updatePreTagsAttribute = useCallback(() => {
+ preTags?.forEach((pre) => {
+ pre.setAttribute(attribute, prismTheme);
+ });
+ }, [attribute, preTags, prismTheme]);
+
+ useEffect(() => {
+ updatePreTagsAttribute();
+ }, [updatePreTagsAttribute, prismTheme]);
+
+ const listenAttributeChange = useCallback(
+ (pre: HTMLPreElement) => {
+ var observer = new MutationObserver(function (mutations) {
+ mutations.forEach((record) => {
+ var mutatedPre = record.target as HTMLPreElement;
+ var newTheme = mutatedPre.getAttribute(attribute) as PrismTheme;
+ console.log('here');
+ setPrismTheme(newTheme);
+ });
+ });
+ observer.observe(pre, {
+ attributes: true,
+ attributeFilter: [attribute],
+ });
+ },
+ [attribute]
+ );
+
+ useEffect(() => {
+ if (!preTags) return;
+
+ preTags.forEach((pre) => {
+ listenAttributeChange(pre);
+ });
+ }, [preTags, listenAttributeChange]);
+
+ return (
+ <PrismThemeContext.Provider
+ value={{
+ themes,
+ theme: prismTheme,
+ setTheme: updateTheme,
+ codeBlocks: preTags,
+ setCodeBlocks: updatePreTags,
+ resolvedTheme,
+ }}
+ >
+ {children}
+ </PrismThemeContext.Provider>
+ );
+};