{"version":3,"file":"dark-mode.cjs","sources":["../src/dark-mode.ts"],"sourcesContent":["import { Button, ButtonProps } from \"@blueprintjs/core\";\nimport h from \"@macrostrat/hyper\";\nimport React, {\n  ReactNode,\n  createContext,\n  useCallback,\n  useContext,\n  useEffect,\n  useState,\n} from \"react\";\nimport { useStoredState } from \"./util/local-storage\";\n\ntype DarkModeState = {\n  isEnabled: boolean;\n  isAutoset: boolean;\n  isSystemPreferred: boolean | null;\n};\n\ntype DarkModeUpdater = (enabled?: boolean) => void;\n\nfunction systemPreferredDarkMode() {\n  if (typeof window === \"undefined\") return null;\n  const matcher = window.matchMedia(\"(prefers-color-scheme: dark)\");\n  return matcher?.matches;\n}\n\nfunction systemDarkMode(): DarkModeState {\n  const isSystemPreferred = systemPreferredDarkMode();\n  return {\n    isSystemPreferred,\n    isEnabled: isSystemPreferred ?? false,\n    isAutoset: true,\n  };\n}\n\nfunction setDarkReaderMeta(enabled: boolean = true) {\n  // Ensure that Dark Reader doesn't apply to this page\n  const meta = document.querySelector(\"meta[name=darkreader-lock]\");\n  if (enabled && meta == null) {\n    const meta = document.createElement(\"meta\");\n    meta.name = \"darkreader-lock\";\n    document.head.append(meta);\n  }\n  if (!enabled && meta != null) meta.remove();\n}\n\nconst ValueContext = createContext<DarkModeState>({\n  isSystemPreferred: null,\n  isEnabled: false,\n  isAutoset: false,\n});\nconst UpdaterContext = createContext<DarkModeUpdater | null>(null);\n\ntype DarkModeProps = {\n  children?: ReactNode;\n  isEnabled?: boolean;\n  /** Override with system dark mode setting on page load */\n  followSystem?: boolean;\n  bodyClasses?: string[];\n  disabledBodyClasses?: string[];\n  // Deprecated\n  addBodyClasses?: boolean;\n};\n\nfunction applySystemDarkMode(\n  initialValue: DarkModeState,\n  forceFollowSystem: boolean,\n  systemPreferred: boolean | null,\n): DarkModeState {\n  const _systemPreferred = systemPreferred ?? systemPreferredDarkMode();\n  if (initialValue.isAutoset || forceFollowSystem) {\n    return {\n      isSystemPreferred: _systemPreferred,\n      isEnabled: _systemPreferred ?? false,\n      isAutoset: true,\n    };\n  } else {\n    return {\n      ...initialValue,\n      isSystemPreferred: _systemPreferred,\n    };\n  }\n}\n\nfunction _DarkModeProvider(props: DarkModeProps) {\n  const {\n    addBodyClasses = true,\n    isEnabled,\n    followSystem = false,\n    bodyClasses = [\"dark-mode\", \"bp6-dark\"],\n    disabledBodyClasses = [\"bp6-light\", \"light-mode\"],\n    children,\n  } = props;\n  const [storedValue, updateValue, resetState] = useStoredState(\n    \"ui-dark-mode\",\n    systemDarkMode(),\n  );\n  // Guards so that we don't error on an invalid stored value\n  const value = {\n    isSystemPreferred: storedValue?.isSystemPreferred ?? null,\n    isEnabled: storedValue?.isEnabled ?? false,\n    isAutoset: storedValue?.isAutoset ?? false,\n  };\n\n  useEffect(() => {\n    // Update value if isEnabled is provided\n    if (isEnabled && followSystem) {\n      console.warn(\n        \"DarkModeProvider: followSystem and isEnabled are mutually exclusive. Ignoring followSystem\",\n      );\n    }\n    if (isEnabled == null) {\n      updateValue(applySystemDarkMode(value, followSystem, null));\n    } else {\n      updateValue({\n        isAutoset: false,\n        isEnabled,\n        isSystemPreferred: systemPreferredDarkMode(),\n      });\n    }\n  }, [isEnabled, followSystem]);\n\n  // Manage dark mode body classes\n  useEffect(() => {\n    // addBodyClasses will eventually be replaced\n    const _addBodyClasses = addBodyClasses ?? bodyClasses != null;\n    if (!_addBodyClasses) return;\n    setDarkReaderMeta(value.isEnabled);\n    if (value.isEnabled) {\n      document.body.classList.remove(...disabledBodyClasses);\n      document.body.classList.add(...bodyClasses);\n    } else {\n      document.body.classList.remove(...bodyClasses);\n      document.body.classList.add(...disabledBodyClasses);\n    }\n  }, [storedValue, bodyClasses, addBodyClasses]);\n\n  const update: DarkModeUpdater = useCallback(\n    (enabled: boolean | null) => {\n      if (enabled == null) return resetState();\n      updateValue({\n        isAutoset: false,\n        isEnabled: enabled ?? isEnabled ?? false,\n        isSystemPreferred: value.isSystemPreferred,\n      });\n    },\n    [isEnabled],\n  );\n\n  const onSystemChange = useCallback(\n    (e) => updateValue(applySystemDarkMode(value, followSystem, e.matches)),\n    [value, followSystem],\n  );\n\n  useEffect(() => {\n    if (typeof window === \"undefined\") return;\n\n    const matcher = window.matchMedia(\"(prefers-color-scheme: dark)\");\n    matcher.addEventListener(\"change\", onSystemChange);\n    return () => {\n      matcher.removeEventListener(\"change\", onSystemChange);\n    };\n  }, [onSystemChange]);\n\n  return h(\n    ValueContext.Provider,\n    { value },\n    h(UpdaterContext.Provider, { value: update }, children),\n  );\n}\n\nfunction DarkModeProvider(props: DarkModeProps) {\n  // Ensure that only one provider is active for an application\n  const parentCtx = useContext(UpdaterContext);\n  if (parentCtx != null) return props.children ?? null;\n  return h(_DarkModeProvider, props);\n}\n\nconst useDarkMode = () => useContext(ValueContext);\nconst useInDarkMode = () => useDarkMode()?.isEnabled ?? false;\nconst inDarkMode = () => {\n  console.warn(\"inDarkMode is deprecated. Use useInDarkMode instead\");\n  return useInDarkMode();\n};\nconst darkModeUpdater = () => useContext(UpdaterContext);\n\nconst DarkModeButton = (\n  props: ButtonProps & { allowReset: boolean; showText: boolean },\n) => {\n  const { allowReset = true, showText = false, children, ...rest } = props;\n  const { isEnabled, isAutoset } = useDarkMode();\n  const update = darkModeUpdater();\n  const [mounted, setMounted] = useState(false);\n  useEffect(() => setMounted(true), []);\n\n  // Use stable defaults until after hydration to avoid server/client mismatch\n  const icon = mounted && isEnabled ? \"flash\" : \"moon\";\n  const active = mounted && !isAutoset;\n\n  const onClick: React.MouseEventHandler = (event) => {\n    if (update == null) {\n      console.warn(\"No DarkModeProvider is available\");\n      return;\n    }\n    if (allowReset && event.shiftKey) {\n      update(null);\n      return;\n    }\n    update(!isEnabled);\n  };\n\n  return h(Button, { active, ...rest, icon, onClick }, [\n    h.if(showText)([isEnabled ? \"Light\" : \"Dark\"]),\n    children,\n  ]);\n};\n\nexport {\n  DarkModeButton,\n  DarkModeProvider,\n  darkModeUpdater,\n  inDarkMode,\n  useDarkMode,\n  useInDarkMode,\n};\n"],"names":["meta","createContext","useStoredState","useEffect","useCallback","h","useContext","useState","Button"],"mappings":"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"}