{"version":3,"file":"use-fullscreen.cjs","names":[],"sources":["../../src/hooks/use-fullscreen.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\nimport type { RefObject } from \"react\";\nimport { getFullscreenElement, useFullscreenElement } from \"./use-fullscreen-element\";\n\n/**\n * The fullscreen members of `document` this hook calls, standard and\n * vendor-prefixed, every one of them optional.\n *\n * `lib.dom` declares the standard members as always present, which is a promise\n * the runtime does not keep: an older WebKit ships only the prefixed pair, and a\n * document inside an `<iframe>` without `allowfullscreen` reports\n * `fullscreenEnabled: false`. Reading through an all-optional shape is what makes\n * `typeof x === \"function\"` a check the compiler agrees is worth making.\n */\ninterface FullscreenDocument {\n    fullscreenEnabled?: boolean;\n    exitFullscreen?: () => Promise<void>;\n    webkitFullscreenEnabled?: boolean;\n    webkitExitFullscreen?: () => Promise<void> | void;\n}\n\n/** The fullscreen members of an element, standard and vendor-prefixed. */\ninterface FullscreenTarget {\n    requestFullscreen?: (options?: FullscreenOptions) => Promise<void>;\n    webkitRequestFullscreen?: () => Promise<void> | void;\n}\n\n/**\n * Resolve which element the hook acts on.\n *\n * `ref.current` when a ref was given, `document.documentElement` when it was not\n * — the whole page, which is what \"go fullscreen\" means with no element named.\n *\n * A missing `ref.current` deliberately resolves to `null` rather than falling\n * back to the page: a caller who named an element and whose element is not\n * mounted yet wants an error, not the entire document blown up to fill the\n * screen behind their video.\n *\n * @param ref - The element to present, or `undefined` for the whole page.\n * @returns The element, or `null` when there is nothing to act on.\n */\nfunction resolveTarget(ref?: RefObject<HTMLElement | null>): HTMLElement | null {\n    if (typeof document === \"undefined\") return null;\n    return ref ? ref.current : document.documentElement;\n}\n\n/**\n * Whether this environment can present anything fullscreen at all.\n *\n * Two independent reasons for `false`, and skipping either one ships a button\n * that does nothing when pressed: the API can be missing (older WebKit, and every\n * browser on iOS for non-`<video>` elements), or present and **disabled** — a\n * document inside an `<iframe>` without the `allowfullscreen` attribute exposes\n * `requestFullscreen` and rejects every call to it, which `fullscreenEnabled`\n * reports up front.\n *\n * An engine that exposes the methods and no flag at all is treated as capable:\n * the flag is the newer half of the API, so its absence says nothing.\n *\n * @returns `true` when a fullscreen request stands a chance of being honoured.\n */\nexport function isFullscreenSupported(): boolean {\n    if (typeof document === \"undefined\") return false;\n    const root: FullscreenTarget = document.documentElement;\n    const doc: FullscreenDocument = document;\n    const reachable =\n        typeof root.requestFullscreen === \"function\" ||\n        typeof root.webkitRequestFullscreen === \"function\";\n    return reachable && (doc.fullscreenEnabled ?? doc.webkitFullscreenEnabled ?? true);\n}\n\n/** Value returned by {@link useFullscreen}. */\nexport interface UseFullscreenResult {\n    /** Whether the target element is the one the browser is presenting right now. */\n    isFullscreen: boolean;\n    /** Whether a request stands a chance at all — hide the control when `false`. */\n    supported: boolean;\n    /** Present the target. Must be called from a user gesture. Rejects if refused. */\n    enter: () => Promise<void>;\n    /** Leave fullscreen. Resolves immediately when nothing is presented. */\n    exit: () => Promise<void>;\n    /** Leave if the target is presented, otherwise enter. Same gesture rule as `enter`. */\n    toggle: () => Promise<void>;\n}\n\n/**\n * Drive an immersive mode whose state stays true to the browser.\n *\n * The mistake this exists to remove is storing `isFullscreen` in state and\n * flipping it inside your own `enter()` / `exit()`. Fullscreen ends in ways your\n * code never sees — `Esc`, the browser's own exit affordance, F11 pressed while an\n * API fullscreen is active — and each of those leaves the flag saying \"sair\" over\n * a page that is already windowed. The only honest source is the\n * `fullscreenchange` event, which fires for every one of them, so that is what\n * this hook reads and what the returned `isFullscreen` reflects. The action\n * callbacks never touch it.\n *\n * The subscription is not this hook's own: it lives in `useFullscreenElement`,\n * shared with `usePortalHost`, which needs the same fact to decide where an\n * overlay mounts while fullscreen is on. Two independent listeners for one event\n * would mean two copies of the WebKit prefix dance and two places to fix the next\n * quirk, so the primitive answers \"which element is presented\" once and each\n * consumer asks its own question of the answer.\n *\n * WebKit's prefixed members are handled throughout — `webkitfullscreenchange`,\n * `webkitFullscreenElement`, `webkitRequestFullscreen`, `webkitExitFullscreen` —\n * because Safari on iPad still ships them and nothing else.\n *\n * `isFullscreen` is identity against the target, not containment: a `<video>`\n * inside your element going fullscreen on its own is not your element being\n * presented, and reporting `true` there would put your exit control inside a\n * subtree the browser is not painting.\n *\n * @param ref - The element to present. Omit it to present the whole page\n * (`document.documentElement`).\n * @returns `{ isFullscreen, supported, enter, exit, toggle }`.\n * @throws Nothing during render. `enter()` and `toggle()` reject with the\n * browser's own error when the request is refused — most often a `TypeError`\n * because the call did not come from a user gesture — and with an `Error` when\n * there is no element to present or the environment ships no Fullscreen API.\n *\n * @example\n * const stage = useRef<HTMLDivElement>(null);\n * const { isFullscreen, supported, toggle } = useFullscreen(stage);\n *\n * <div ref={stage}>\n *   <video src=\"/aula.mp4\" controls />\n *   {supported ? (\n *     <button onClick={() => void toggle().catch(() => setRefused(true))}>\n *       {isFullscreen ? \"Sair da tela cheia\" : \"Tela cheia\"}\n *     </button>\n *   ) : null}\n * </div>\n */\nexport function useFullscreen(ref?: RefObject<HTMLElement | null>): UseFullscreenResult {\n    const element = useFullscreenElement();\n    const [supported] = useState(isFullscreenSupported);\n    const [isFullscreen, setIsFullscreen] = useState(false);\n\n    useEffect(() => {\n        const target = resolveTarget(ref);\n        setIsFullscreen(target !== null && element === target);\n    }, [element, ref]);\n\n    const enter = useCallback(async (): Promise<void> => {\n        const target = resolveTarget(ref);\n        if (target === null) {\n            throw new Error(\n                \"useFullscreen: nothing to present. Either this environment has no document, \" +\n                    \"or the ref was never attached to a mounted element.\",\n            );\n        }\n        const candidate: FullscreenTarget = target;\n        if (typeof candidate.requestFullscreen === \"function\") {\n            await candidate.requestFullscreen();\n            return;\n        }\n        if (typeof candidate.webkitRequestFullscreen === \"function\") {\n            await candidate.webkitRequestFullscreen();\n            return;\n        }\n        throw new Error(\"useFullscreen: this browser exposes no way to enter fullscreen.\");\n    }, [ref]);\n\n    const exit = useCallback(async (): Promise<void> => {\n        if (getFullscreenElement() === null) return;\n        const doc: FullscreenDocument = document;\n        if (typeof doc.exitFullscreen === \"function\") {\n            await doc.exitFullscreen();\n            return;\n        }\n        if (typeof doc.webkitExitFullscreen === \"function\") {\n            await doc.webkitExitFullscreen();\n            return;\n        }\n        throw new Error(\"useFullscreen: this browser exposes no way to leave fullscreen.\");\n    }, []);\n\n    const toggle = useCallback(async (): Promise<void> => {\n        const target = resolveTarget(ref);\n        if (target !== null && getFullscreenElement() === target) {\n            await exit();\n            return;\n        }\n        await enter();\n    }, [enter, exit, ref]);\n\n    return { isFullscreen, supported, enter, exit, toggle };\n}\n"],"mappings":"uEAyCA,SAAS,EAAc,EAAyD,CAE5E,OADI,OAAO,SAAa,IAAoB,KACrC,EAAM,EAAI,QAAU,SAAS,eACxC,CAiBA,SAAgB,GAAiC,CAC7C,GAAI,OAAO,SAAa,IAAa,MAAO,GAC5C,IAAM,EAAyB,SAAS,gBAClC,EAA0B,SAIhC,OAFI,OAAO,EAAK,mBAAsB,YAClC,OAAO,EAAK,yBAA4B,cACvB,EAAI,mBAAqB,EAAI,yBAA2B,GACjF,CAiEA,SAAgB,EAAc,EAA0D,CACpF,IAAM,EAAU,EAAA,qBAAqB,EAC/B,CAAC,IAAA,EAAa,EAAA,SAAA,CAAS,CAAqB,EAC5C,CAAC,EAAc,IAAA,EAAmB,EAAA,SAAA,CAAS,EAAK,GAEtD,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAM,EAAS,EAAc,CAAG,EAChC,EAAgB,IAAW,MAAQ,IAAY,CAAM,CACzD,EAAG,CAAC,EAAS,CAAG,CAAC,EAEjB,IAAM,GAAA,EAAQ,EAAA,YAAA,CAAY,SAA2B,CACjD,IAAM,EAAS,EAAc,CAAG,EAChC,GAAI,IAAW,KACX,MAAU,MACN,iIAEJ,EAEJ,IAAM,EAA8B,EACpC,GAAI,OAAO,EAAU,mBAAsB,WAAY,CACnD,MAAM,EAAU,kBAAkB,EAClC,MACJ,CACA,GAAI,OAAO,EAAU,yBAA4B,WAAY,CACzD,MAAM,EAAU,wBAAwB,EACxC,MACJ,CACA,MAAU,MAAM,iEAAiE,CACrF,EAAG,CAAC,CAAG,CAAC,EAEF,GAAA,EAAO,EAAA,YAAA,CAAY,SAA2B,CAChD,GAAI,EAAA,qBAAqB,IAAM,KAAM,OACrC,IAAM,EAA0B,SAChC,GAAI,OAAO,EAAI,gBAAmB,WAAY,CAC1C,MAAM,EAAI,eAAe,EACzB,MACJ,CACA,GAAI,OAAO,EAAI,sBAAyB,WAAY,CAChD,MAAM,EAAI,qBAAqB,EAC/B,MACJ,CACA,MAAU,MAAM,iEAAiE,CACrF,EAAG,CAAC,CAAC,EAWL,MAAO,CAAE,eAAc,YAAW,QAAO,OAAM,QAAA,EAThC,EAAA,YAAA,CAAY,SAA2B,CAClD,IAAM,EAAS,EAAc,CAAG,EAChC,GAAI,IAAW,MAAQ,EAAA,qBAAqB,IAAM,EAAQ,CACtD,MAAM,EAAK,EACX,MACJ,CACA,MAAM,EAAM,CAChB,EAAG,CAAC,EAAO,EAAM,CAAG,CAE2B,CAAO,CAC1D"}