{"version":3,"file":"themeProvider.mjs","names":[],"sources":["../../../../package/common/themeProvider/themeProvider.tsx"],"sourcesContent":["import React, { Component } from 'react';\nimport cx from 'classnames';\n\nimport type { Theme, ThemeProviderProps } from './theme.types';\nimport { defaultTheme } from './defaultTheme';\nimport { ThemeVariablesContext } from './themeVariablesContext';\nimport { isRevealingTheme } from './themeReveal';\nimport styles from './theme.module.pcss';\n\ninterface State {\n  resolvedVariables: Record<string, string>;\n  colorFadeActive: boolean;\n  currentThemeColor: string | null;\n  /** What `resolvedVariables` was resolved from, to tell a theme change from any other render. */\n  resolvedFrom: { themes: Theme[] | undefined; currentThemeId: string | undefined };\n}\n\n/**\n * Resolves a theme ID's variables by walking up its baseThemeId chain, ordered from deepest\n * ancestor to the requested theme so they merge in order (later entries win).\n */\nfunction resolveChain(themes: Theme[], id: string): Record<string, string>[] {\n  const theme = themes.find((t) => t.id === id);\n  if (!theme) return [];\n  const parentVars = theme.baseThemeId ? resolveChain(themes, theme.baseThemeId) : [];\n  return [...parentVars, theme.variables as Record<string, string>];\n}\n\nfunction resolveVariables({ themes = [defaultTheme], currentThemeId }: ThemeProviderProps): Record<string, string> {\n  // The root theme, the first without a baseThemeId, is always the foundation.\n  const rootTheme = themes.find((t) => !t.baseThemeId) ?? themes[0];\n  const rootVars = rootTheme ? (rootTheme.variables as Record<string, string>) : {};\n  const chainVars = currentThemeId ? resolveChain(themes, currentThemeId) : [];\n  return Object.assign({}, rootVars, ...chainVars);\n}\n\nclass ThemeProvider extends Component<ThemeProviderProps, State> {\n  // A nested provider themes a section, not the page, so it leaves the page's `theme-color` alone.\n  static contextType = ThemeVariablesContext;\n  declare context: React.ContextType<typeof ThemeVariablesContext>;\n\n  // Resolved before the first render rather than after mount: resolved in componentDidMount, every\n  // component rendered and measured itself unthemed first, and on each page load some 50\n  // backgrounds then faded in from nothing.\n  state: State = {\n    resolvedVariables: resolveVariables(this.props),\n    colorFadeActive: false,\n    currentThemeColor: null,\n    resolvedFrom: { themes: this.props.themes, currentThemeId: this.props.currentThemeId },\n  };\n\n  private colorFadeTimeout: ReturnType<typeof setTimeout> | null = null;\n  private wrapperRef = React.createRef<HTMLDivElement>();\n  private themeColorMeta: HTMLMetaElement | null = null;\n  private createdThemeColorMeta = false;\n\n  // The fade and the new variables have to land in the same render: added a render later, the\n  // browser may already have painted the new colours without it.\n  static getDerivedStateFromProps(props: ThemeProviderProps, state: State): Partial<State> | null {\n    const { themes, currentThemeId } = state.resolvedFrom;\n    if (props.themes === themes && props.currentThemeId === currentThemeId) return null;\n    return {\n      resolvedVariables: resolveVariables(props),\n      colorFadeActive: !isRevealingTheme(),\n      resolvedFrom: { themes: props.themes, currentThemeId: props.currentThemeId },\n    };\n  }\n\n  componentDidMount() {\n    this.readThemeColor();\n  }\n\n  componentDidUpdate(prevProps: ThemeProviderProps, prevState: State) {\n    if (prevState.resolvedVariables !== this.state.resolvedVariables) {\n      this.readThemeColor();\n      if (this.colorFadeTimeout) clearTimeout(this.colorFadeTimeout);\n      this.colorFadeTimeout = setTimeout(() => {\n        this.setState({ colorFadeActive: false });\n        this.colorFadeTimeout = null;\n      }, 500);\n    }\n    if (prevState.currentThemeColor !== this.state.currentThemeColor) {\n      this.syncThemeColorMeta(this.state.currentThemeColor ?? '');\n    }\n  }\n\n  componentWillUnmount() {\n    if (this.colorFadeTimeout) clearTimeout(this.colorFadeTimeout);\n    // Only remove the meta tag if we created it; leave a host-app tag intact.\n    // Also clear the cached refs — StrictMode's dev-only mount/unmount/remount\n    // cycle reuses this instance, and a stale ref would otherwise point at a\n    // detached element on remount instead of re-inserting into <head>.\n    if (this.createdThemeColorMeta) {\n      this.themeColorMeta?.remove();\n      this.themeColorMeta = null;\n      this.createdThemeColorMeta = false;\n    }\n  }\n\n  /**\n   * Replaces the former react-helmet usage: keep a single\n   * <meta name=\"theme-color\"> in sync with the active theme's primary colour.\n   * Reuses a tag the host app already declares; otherwise creates its own\n   * (and removes it again on unmount).\n   */\n  private syncThemeColorMeta(color: string) {\n    if (typeof document === 'undefined') return;\n\n    if (!this.themeColorMeta) {\n      const existing = document.head.querySelector<HTMLMetaElement>('meta[name=\"theme-color\"]');\n      if (existing) {\n        this.themeColorMeta = existing;\n      } else {\n        const meta = document.createElement('meta');\n        meta.setAttribute('name', 'theme-color');\n        document.head.appendChild(meta);\n        this.themeColorMeta = meta;\n        this.createdThemeColorMeta = true;\n      }\n    }\n\n    this.themeColorMeta.setAttribute('content', color);\n  }\n\n  /** `--primary-color` as the browser resolved it, which may be a `var()` of another variable. */\n  private readThemeColor() {\n    if (this.context || !this.wrapperRef.current) return;\n    const color = getComputedStyle(this.wrapperRef.current).getPropertyValue('--primary-color').trim();\n    if (color !== this.state.currentThemeColor) this.setState({ currentThemeColor: color });\n  }\n\n  render() {\n    const { children } = this.props;\n    const { resolvedVariables, colorFadeActive } = this.state;\n\n    return (\n      <div\n        ref={this.wrapperRef}\n        id='themeProvider'\n        className={cx(styles.themeRoot, colorFadeActive && styles.colorFade)}\n        style={resolvedVariables as React.CSSProperties}\n      >\n        <ThemeVariablesContext.Provider value={resolvedVariables}>{children}</ThemeVariablesContext.Provider>\n      </div>\n    );\n  }\n}\n\nexport default ThemeProvider;\n"],"mappings":"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"}