github.com/grafana/pyroscope@v1.18.0/public/app/hooks/colorMode.hook.ts (about) 1 import { useCallback, useEffect } from 'react'; 2 import { useAppSelector, useAppDispatch } from '@pyroscope/redux/hooks'; 3 import { setColorMode, selectAppColorMode } from '@pyroscope/redux/reducers/ui'; 4 5 /** Obtain the current color mode (light|dark) and ensure all representations of it are in sync 6 * 7 * Some elements of style rely on the `<body>` tag's `data-theme` attribute 8 * (accessibile via document.body.dataset.theme) 9 * while others must consult with the color mode set in the redux store. 10 * 11 * This hook ensures that any change in the redux store is reflected in the body theme, 12 * and vice versa. 13 */ 14 const useColorMode = () => { 15 const dispatch = useAppDispatch(); 16 const colorMode = useAppSelector(selectAppColorMode); 17 const { body } = document; 18 19 /** This callback updates the color mode in the redux store. */ 20 const changeColorMode = useCallback( 21 (newColorMode: 'light' | 'dark') => dispatch(setColorMode(newColorMode)), 22 [dispatch] 23 ); 24 25 // This effect sets up an observer for changes in the `<body>` `data-theme` attr 26 // which updates the redux value to keep them in sync. 27 useEffect(() => { 28 const observer = new MutationObserver((mutationsList) => { 29 const mutation = mutationsList[0]; 30 31 if (mutation.type === 'attributes' && body.dataset.theme !== colorMode) { 32 changeColorMode( 33 body.dataset.theme === 'light' || body.dataset.theme === 'dark' 34 ? body.dataset.theme 35 : 'dark' 36 ); 37 } 38 }); 39 40 if (body) { 41 observer.observe(body, { 42 attributes: true, 43 attributeFilter: ['data-theme'], 44 childList: false, 45 subtree: false, 46 }); 47 } 48 49 return () => { 50 observer.disconnect(); 51 }; 52 }, [body, changeColorMode, colorMode]); 53 54 // This effect will update the `<body>` tag's `data-theme` attribute 55 // to ensure that it matches the color mode from the redux store. 56 useEffect(() => { 57 // sync redux value with DOM body attr 58 if (body.dataset.theme !== colorMode) { 59 body.dataset.theme = colorMode; 60 } 61 }, [colorMode, body]); 62 63 return { 64 colorMode, 65 changeColorMode, 66 }; 67 }; 68 69 export default useColorMode;