{"version":3,"file":"themeReveal.mjs","names":[],"sources":["../../../../package/common/themeProvider/themeReveal.ts"],"sourcesContent":["import { flushSync } from 'react-dom';\n\nlet revealing = false;\n\n/**\n * True while a reveal renders its new theme. ThemeProvider skips its colour fade then: the new\n * page is live inside the blob, so the fade played there too and the blob opened grey.\n */\nexport function isRevealingTheme(): boolean {\n  return revealing;\n}\n\n/**\n * The outline of the new page `progress` (0 to 1) of the way through: a blob round `x`, `y` whose\n * edge ripples as two waves travel round it. The ripple dies down as it grows, so it ends as a\n * circle of exactly `radius`, covering the farthest corner as the animation ends. Ending any\n * larger, so the dents could cover the corner too, it went on growing off screen for a while after\n * it looked finished, and the page took no clicks all that time.\n */\nfunction blob(x: number, y: number, radius: number, progress: number): string {\n  const size = radius * progress;\n  const phase = progress * Math.PI * 3;\n  const wobble = 0.16 * (1 - progress);\n  const vertices = Array.from({ length: 72 }, (_, i) => {\n    const angle = (i / 72) * Math.PI * 2;\n    const r = size * (1 + wobble * (0.6 * Math.sin(3 * angle + phase) + 0.4 * Math.sin(5 * angle - 1.7 * phase)));\n    return `${(x + r * Math.cos(angle)).toFixed(1)}px ${(y + r * Math.sin(angle)).toFixed(1)}px`;\n  });\n  return `polygon(${vertices.join(', ')})`;\n}\n\n/**\n * Applies `change` inside a view transition that clips the new page in as a blob growing out of\n * `origin`. Without view transitions, or when the user asks for reduced motion, the change just\n * happens.\n */\nexport function revealTheme(change: () => void, origin: Element): void {\n  const reduceMotion =\n    typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n  if (typeof document.startViewTransition !== 'function' || reduceMotion) {\n    change();\n    return;\n  }\n\n  const box = origin.getBoundingClientRect();\n  const x = box.left + box.width / 2;\n  const y = box.top + box.height / 2;\n  const radius = Math.hypot(Math.max(x, window.innerWidth - x), Math.max(y, window.innerHeight - y));\n  const root = document.documentElement;\n\n  // The new theme is captured when this callback returns, so React has to have rendered it by then.\n  const transition = document.startViewTransition(() => {\n    revealing = true;\n    try {\n      flushSync(change);\n    } finally {\n      revealing = false;\n    }\n  });\n  transition.ready\n    .then(() => {\n      const timing = { duration: 900, easing: 'cubic-bezier(0.4, 0, 0.2, 1)' };\n      // Both pictures kept whole and unblended over the browser's own cross-fade, which would show\n      // both themes at half strength inside the blob. Here rather than in the stylesheet, where\n      // an app's unlayered view-transition rule would beat the library's layered one.\n      const whole = { opacity: [1, 1], mixBlendMode: ['normal', 'normal'] };\n      root.animate(whole, { ...timing, pseudoElement: '::view-transition-old(root)' });\n      // Eight steps are enough for the ripple to read as motion rather than as a shape.\n      const clipPath = Array.from({ length: 9 }, (_, step) => blob(x, y, radius, step / 8));\n      root.animate({ ...whole, clipPath }, { ...timing, pseudoElement: '::view-transition-new(root)' });\n    })\n    // Rejects when the transition is skipped, such as by another one starting. The theme has\n    // changed all the same: a skipped transition still runs its update.\n    .catch(() => {});\n}\n"],"mappings":"mDAEA,IAAI,EAAY,GAMhB,SAAgB,GAA4B,CAC1C,OAAO,CACT,CASA,SAAS,EAAK,EAAW,EAAW,EAAgB,EAA0B,CAC5E,IAAM,EAAO,EAAS,EAChB,EAAQ,EAAW,KAAK,GAAK,EAC7B,EAAS,KAAQ,EAAI,GAM3B,MAAO,WALU,MAAM,KAAK,CAAE,OAAQ,EAAG,GAAI,EAAG,IAAM,CACpD,IAAM,EAAS,EAAI,GAAM,KAAK,GAAK,EAC7B,EAAI,GAAQ,EAAI,GAAU,GAAM,KAAK,IAAI,EAAI,EAAQ,CAAK,EAAI,GAAM,KAAK,IAAI,EAAI,EAAQ,IAAM,CAAK,IAC1G,MAAO,IAAI,EAAI,EAAI,KAAK,IAAI,CAAK,EAAA,CAAG,QAAQ,CAAC,EAAE,MAAM,EAAI,EAAI,KAAK,IAAI,CAAK,EAAA,CAAG,QAAQ,CAAC,EAAE,GAC3F,CACkB,CAAA,CAAS,KAAK,IAAI,EAAE,EACxC,CAOA,SAAgB,EAAY,EAAoB,EAAuB,CACrE,IAAM,EACJ,OAAO,OAAO,YAAe,YAAc,OAAO,WAAW,kCAAkC,CAAC,CAAC,QACnG,GAAI,OAAO,SAAS,qBAAwB,YAAc,EAAc,CACtE,EAAO,EACP,MACF,CAEA,IAAM,EAAM,EAAO,sBAAsB,EACnC,EAAI,EAAI,KAAO,EAAI,MAAQ,EAC3B,EAAI,EAAI,IAAM,EAAI,OAAS,EAC3B,EAAS,KAAK,MAAM,KAAK,IAAI,EAAG,OAAO,WAAa,CAAC,EAAG,KAAK,IAAI,EAAG,OAAO,YAAc,CAAC,CAAC,EAC3F,EAAO,SAAS,gBAWtB,SAR4B,wBAA0B,CACpD,EAAY,GACZ,GAAI,CACF,EAAU,CAAM,CAClB,QAAU,CACR,EAAY,EACd,CACF,CACA,CAAA,CAAW,MACR,SAAW,CACV,IAAM,EAAS,CAAE,SAAU,IAAK,OAAQ,8BAA+B,EAIjE,EAAQ,CAAE,QAAS,CAAC,EAAG,CAAC,EAAG,aAAc,CAAC,SAAU,QAAQ,CAAE,EACpE,EAAK,QAAQ,EAAO,CAAE,GAAG,EAAQ,cAAe,6BAA8B,CAAC,EAE/E,IAAM,EAAW,MAAM,KAAK,CAAE,OAAQ,CAAE,GAAI,EAAG,IAAS,EAAK,EAAG,EAAG,EAAQ,EAAO,CAAC,CAAC,EACpF,EAAK,QAAQ,CAAE,GAAG,EAAO,UAAS,EAAG,CAAE,GAAG,EAAQ,cAAe,6BAA8B,CAAC,CAClG,CAAC,CAAC,CAGD,UAAY,CAAC,CAAC,CACnB"}