useHandleAppTheme.ts 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. import { atom, useAtom } from "jotai";
  2. import { useEffect, useLayoutEffect, useState } from "react";
  3. import { THEME } from "../packages/excalidraw";
  4. import { EVENT } from "../packages/excalidraw/constants";
  5. import type { Theme } from "../packages/excalidraw/element/types";
  6. import { CODES, KEYS } from "../packages/excalidraw/keys";
  7. import { STORAGE_KEYS } from "./app_constants";
  8. export const appThemeAtom = atom<Theme | "system">(
  9. (localStorage.getItem(STORAGE_KEYS.LOCAL_STORAGE_THEME) as
  10. | Theme
  11. | "system"
  12. | null) || THEME.LIGHT,
  13. );
  14. const getDarkThemeMediaQuery = (): MediaQueryList | undefined =>
  15. window.matchMedia?.("(prefers-color-scheme: dark)");
  16. export const useHandleAppTheme = () => {
  17. const [appTheme, setAppTheme] = useAtom(appThemeAtom);
  18. const [editorTheme, setEditorTheme] = useState<Theme>(THEME.LIGHT);
  19. useEffect(() => {
  20. const mediaQuery = getDarkThemeMediaQuery();
  21. const handleChange = (e: MediaQueryListEvent) => {
  22. setEditorTheme(e.matches ? THEME.DARK : THEME.LIGHT);
  23. };
  24. if (appTheme === "system") {
  25. mediaQuery?.addEventListener("change", handleChange);
  26. }
  27. const handleKeydown = (event: KeyboardEvent) => {
  28. if (
  29. !event[KEYS.CTRL_OR_CMD] &&
  30. event.altKey &&
  31. event.shiftKey &&
  32. event.code === CODES.D
  33. ) {
  34. event.preventDefault();
  35. event.stopImmediatePropagation();
  36. setAppTheme(editorTheme === THEME.DARK ? THEME.LIGHT : THEME.DARK);
  37. }
  38. };
  39. document.addEventListener(EVENT.KEYDOWN, handleKeydown, { capture: true });
  40. return () => {
  41. mediaQuery?.removeEventListener("change", handleChange);
  42. document.removeEventListener(EVENT.KEYDOWN, handleKeydown, {
  43. capture: true,
  44. });
  45. };
  46. }, [appTheme, editorTheme, setAppTheme]);
  47. useLayoutEffect(() => {
  48. localStorage.setItem(STORAGE_KEYS.LOCAL_STORAGE_THEME, appTheme);
  49. if (appTheme === "system") {
  50. setEditorTheme(
  51. getDarkThemeMediaQuery()?.matches ? THEME.DARK : THEME.LIGHT,
  52. );
  53. } else {
  54. setEditorTheme(appTheme);
  55. }
  56. }, [appTheme]);
  57. return { editorTheme };
  58. };