{"version":3,"file":"useTheme.mjs","names":[],"sources":["../../../../package/common/themeProvider/useTheme.ts"],"sourcesContent":["import { useSyncExternalStore } from 'react';\n\nimport { useMediaQuery } from '../hooks/useMediaQuery';\nimport { defaultTheme } from './defaultTheme';\nimport { lightTheme } from './lightTheme';\nimport { revealTheme } from './themeReveal';\n\n/** The themes `'system'` picks between, by the operating system's light or dark setting. */\nexport interface SystemThemes {\n  light: string;\n  dark: string;\n}\n\n/** How `setCurrentThemeId` makes the change. */\nexport interface SetThemeOptions {\n  /**\n   * Grows the new theme out of this element, usually the control that chose it, as a rippling\n   * blob across the page. Only where the browser has view transitions and the user has not asked for\n   * reduced motion; elsewhere the theme just changes.\n   */\n  revealFrom?: Element | null;\n}\n\nexport interface UseThemeResult {\n  /** The theme to apply, for `currentThemeId`. Never `'system'`: that is resolved to a theme. */\n  currentThemeId: string;\n  /** What was chosen, `'system'` included, for marking the active choice in a theme switcher. */\n  selectedThemeId: string;\n  /** Chooses a theme by its `id`, or `'system'`, and remembers it for the next visit. */\n  setCurrentThemeId: (themeId: string, options?: SetThemeOptions) => void;\n}\n\nconst STORAGE_KEY = 'theme';\n\n// One store for every caller, so two components using this hook cannot show different themes.\nconst listeners = new Set<() => void>();\n\n// Only set while storage refuses writes, so the switch still works for the rest of the visit.\nlet unsaved: string | null = null;\n\nfunction read(): string | null {\n  if (unsaved !== null) return unsaved;\n  try {\n    return localStorage.getItem(STORAGE_KEY);\n  } catch {\n    return null;\n  }\n}\n\nfunction subscribe(onChange: () => void): () => void {\n  listeners.add(onChange);\n  // A choice made in another tab. `key` is null when that tab cleared its storage.\n  const onStorage = (event: StorageEvent) => {\n    if (event.key === STORAGE_KEY || event.key === null) onChange();\n  };\n  window.addEventListener('storage', onStorage);\n  return () => {\n    listeners.delete(onChange);\n    window.removeEventListener('storage', onStorage);\n  };\n}\n\nfunction select(themeId: string, { revealFrom }: SetThemeOptions = {}): void {\n  // Choosing the theme already chosen changes nothing, so there is nothing to reveal.\n  if (revealFrom && themeId !== read()) revealTheme(() => store(themeId), revealFrom);\n  else store(themeId);\n}\n\nfunction store(themeId: string): void {\n  try {\n    localStorage.setItem(STORAGE_KEY, themeId);\n    unsaved = null;\n  } catch {\n    unsaved = themeId;\n  }\n  for (const listener of listeners) listener();\n}\n\n/** The server has no storage, so it renders the default and the client takes over after hydration. */\nconst readOnServer = () => null;\n\n/**\n * The theme the user chose, remembered across visits. Pass `'system'` as the default, or let the\n * user choose it, to follow the operating system's light or dark setting.\n */\nexport function useTheme(\n  defaultThemeId: string = defaultTheme.id,\n  systemThemes: SystemThemes = { light: lightTheme.id, dark: defaultTheme.id },\n): UseThemeResult {\n  const selectedThemeId = useSyncExternalStore(subscribe, read, readOnServer) ?? defaultThemeId;\n  const prefersDark = useMediaQuery('(prefers-color-scheme: dark)');\n\n  let currentThemeId = selectedThemeId;\n  if (selectedThemeId === 'system') currentThemeId = prefersDark ? systemThemes.dark : systemThemes.light;\n\n  return { currentThemeId, selectedThemeId, setCurrentThemeId: select };\n}\n"],"mappings":"oPAgCA,IAAM,EAAc,QAGd,EAAY,IAAI,IAGlB,EAAyB,KAE7B,SAAS,GAAsB,CAC7B,GAAI,IAAY,KAAM,OAAO,EAC7B,GAAI,CACF,OAAO,aAAa,QAAQ,CAAW,CACzC,MAAQ,CACN,OAAO,IACT,CACF,CAEA,SAAS,EAAU,EAAkC,CACnD,EAAU,IAAI,CAAQ,EAEtB,IAAM,EAAa,GAAwB,EACrC,EAAM,MAAQ,GAAe,EAAM,MAAQ,OAAM,EAAS,CAChE,EAEA,OADA,OAAO,iBAAiB,UAAW,CAAS,MAC/B,CACX,EAAU,OAAO,CAAQ,EACzB,OAAO,oBAAoB,UAAW,CAAS,CACjD,CACF,CAEA,SAAS,EAAO,EAAiB,CAAE,cAAgC,CAAC,EAAS,CAEvE,GAAc,IAAY,EAAK,EAAG,MAAkB,EAAM,CAAO,EAAG,CAAU,EAC7E,EAAM,CAAO,CACpB,CAEA,SAAS,EAAM,EAAuB,CACpC,GAAI,CACF,aAAa,QAAQ,EAAa,CAAO,EACzC,EAAU,IACZ,MAAQ,CACN,EAAU,CACZ,CACA,IAAK,IAAM,KAAY,EAAW,EAAS,CAC7C,CAGA,IAAM,MAAqB,KAM3B,SAAgB,EACd,EAAyB,EAAa,GACtC,EAA6B,CAAE,MAAO,EAAW,GAAI,KAAM,EAAa,EAAG,EAC3D,CAChB,IAAM,EAAkB,EAAqB,EAAW,EAAM,CAAY,GAAK,EACzE,EAAc,EAAc,8BAA8B,EAE5D,EAAiB,EAGrB,OAFI,IAAoB,WAAU,EAAiB,EAAc,EAAa,KAAO,EAAa,OAE3F,CAAE,iBAAgB,kBAAiB,kBAAmB,CAAO,CACtE"}