{
  "name": "use-fullscreen",
  "author": "Brendan Dash (https://shadcn-hooks.com)",
  "description": "A hook to manage fullscreen state",
  "dependencies": [
    "es-toolkit"
  ],
  "registryDependencies": [
    "@shadcnhooks/use-isomorphic-layout-effect",
    "@shadcnhooks/use-event-listener",
    "@shadcnhooks/use-unmount",
    "@shadcnhooks/use-latest",
    "@shadcnhooks/use-memoized-fn",
    "@shadcnhooks/create-effect-with-target",
    "@shadcnhooks/is-browser"
  ],
  "files": [
    {
      "path": "registry/hooks/use-fullscreen.ts",
      "content": "import { useRef, useState } from 'react'\nimport { useEffectWithTarget } from '@/registry/hooks/use-effect-with-target'\nimport { useEventListener } from '@/registry/hooks/use-event-listener'\nimport { useIsomorphicLayoutEffect } from '@/registry/hooks/use-isomorphic-layout-effect'\nimport { useMemoizedFn } from '@/registry/hooks/use-memoized-fn'\nimport { useUnmount } from '@/registry/hooks/use-unmount'\nimport { getTargetElement as getTargetElementUtil } from '@/registry/lib/create-effect-with-target'\nimport { isBrowser } from '@/registry/lib/is-browser'\nimport type { BasicTarget } from '@/registry/lib/create-effect-with-target'\n\nexport interface UseFullscreenOptions {\n  /**\n   * Automatically exit fullscreen when component is unmounted\n   *\n   * @default false\n   */\n  autoExit?: boolean\n}\n\nconst eventHandlers = [\n  'fullscreenchange',\n  'webkitfullscreenchange',\n  'webkitendfullscreen',\n  'mozfullscreenchange',\n  'MSFullscreenChange',\n] as const\n\ntype RequestMethod =\n  | 'requestFullscreen'\n  | 'webkitRequestFullscreen'\n  | 'webkitEnterFullscreen'\n  | 'webkitEnterFullScreen'\n  | 'webkitRequestFullScreen'\n  | 'mozRequestFullScreen'\n  | 'msRequestFullscreen'\n\ntype ExitMethod =\n  | 'exitFullscreen'\n  | 'webkitExitFullscreen'\n  | 'webkitExitFullScreen'\n  | 'webkitCancelFullScreen'\n  | 'mozCancelFullScreen'\n  | 'msExitFullscreen'\n\ntype FullscreenEnabledProperty =\n  | 'fullScreen'\n  | 'webkitIsFullScreen'\n  | 'webkitDisplayingFullscreen'\n  | 'mozFullScreen'\n  | 'msFullscreenElement'\n\ntype FullscreenElementProperty =\n  | 'fullscreenElement'\n  | 'webkitFullscreenElement'\n  | 'mozFullScreenElement'\n  | 'msFullscreenElement'\n\nfunction getTargetElement(target: BasicTarget<any>) {\n  return getTargetElementUtil(target, document.documentElement)\n}\n\nfunction getProperties(target: BasicTarget<any>) {\n  const targetElement = getTargetElement(target)\n\n  const getRequestMethod = () => {\n    const methods: RequestMethod[] = [\n      'requestFullscreen',\n      'webkitRequestFullscreen',\n      'webkitEnterFullscreen',\n      'webkitEnterFullScreen',\n      'webkitRequestFullScreen',\n      'mozRequestFullScreen',\n      'msRequestFullscreen',\n    ]\n\n    return methods.find(\n      (method) =>\n        (targetElement && method in targetElement) ||\n        (document && method in document),\n    )\n  }\n\n  const getExitMethod = () => {\n    const methods: ExitMethod[] = [\n      'exitFullscreen',\n      'webkitExitFullscreen',\n      'webkitExitFullScreen',\n      'webkitCancelFullScreen',\n      'mozCancelFullScreen',\n      'msExitFullscreen',\n    ]\n\n    return methods.find(\n      (method) =>\n        (targetElement && method in targetElement) ||\n        (document && method in document),\n    )\n  }\n\n  const getFullscreenEnabledProperty = () => {\n    const properties: FullscreenEnabledProperty[] = [\n      'fullScreen',\n      'webkitIsFullScreen',\n      'webkitDisplayingFullscreen',\n      'mozFullScreen',\n      'msFullscreenElement',\n    ]\n\n    return properties.find(\n      (property) =>\n        (document && property in document) ||\n        (targetElement && property in targetElement),\n    )\n  }\n\n  const getFullscreenElementProperty = () => {\n    const properties: FullscreenElementProperty[] = [\n      'fullscreenElement',\n      'webkitFullscreenElement',\n      'mozFullScreenElement',\n      'msFullscreenElement',\n    ]\n\n    return properties.find(\n      (property) =>\n        (document && property in document) ||\n        (targetElement && property in targetElement),\n    )\n  }\n\n  return {\n    requestMethod: getRequestMethod(),\n    exitMethod: getExitMethod(),\n    fullscreenEnabledProperty: getFullscreenEnabledProperty(),\n    fullscreenElementProperty: getFullscreenElementProperty(),\n  }\n}\n\nfunction getIsSupported(\n  target: BasicTarget<any>,\n  properties: ReturnType<typeof getProperties>,\n) {\n  const targetElement = getTargetElement(target)\n\n  const { requestMethod, exitMethod, fullscreenEnabledProperty } = properties\n  return !!(\n    targetElement &&\n    document &&\n    requestMethod !== undefined &&\n    exitMethod !== undefined &&\n    fullscreenEnabledProperty !== undefined\n  )\n}\n\n/**\n * Reactive Fullscreen API.\n *\n * @param target - The target element to make fullscreen. If not provided, uses document.documentElement\n * @param options - Configuration options\n */\nexport function useFullscreen(\n  target?: BasicTarget<any>,\n  options: UseFullscreenOptions = {},\n) {\n  const { autoExit = false } = options\n\n  const properties = useRef<{\n    requestMethod: RequestMethod | undefined\n    exitMethod: ExitMethod | undefined\n    fullscreenEnabledProperty: FullscreenEnabledProperty | undefined\n    fullscreenElementProperty: FullscreenElementProperty | undefined\n  }>({\n    requestMethod: undefined,\n    exitMethod: undefined,\n    fullscreenEnabledProperty: undefined,\n    fullscreenElementProperty: undefined,\n  })\n  const [isSupported, setIsSupported] = useState(() => {\n    if (!isBrowser) return false\n\n    return getIsSupported(target, getProperties(target))\n  })\n  const [isFullscreen, setIsFullscreen] = useState(false)\n\n  const exit = useMemoizedFn(async () => {\n    const { exitMethod } = properties.current\n    if (!isSupported || !isFullscreen) return\n\n    const element = getTargetElement(target)\n    const doc = document as any\n\n    if (exitMethod) {\n      if (doc[exitMethod] != null) {\n        await doc[exitMethod]()\n      } else if (element && (element as any)[exitMethod] != null) {\n        // Fallback for Safari iOS\n        await (element as any)[exitMethod]()\n      }\n    }\n\n    setIsFullscreen(false)\n  })\n\n  useEffectWithTarget(\n    () => {\n      if (!isBrowser) {\n        return\n      }\n\n      properties.current = getProperties(target)\n\n      setIsSupported(getIsSupported(target, properties.current))\n    },\n    [],\n    target,\n  )\n\n  const isCurrentElementFullScreen = useMemoizedFn((): boolean => {\n    const { fullscreenElementProperty } = properties.current\n    if (!fullscreenElementProperty || !isBrowser) return false\n\n    const element = getTargetElement(target)\n\n    return document[fullscreenElementProperty as keyof Document] === element\n  })\n\n  const isElementFullScreen = useMemoizedFn((): boolean => {\n    const { fullscreenEnabledProperty } = properties.current\n    if (!fullscreenEnabledProperty || !isBrowser) return false\n\n    const element = getTargetElement(target)\n    const doc = document as any\n\n    if (doc[fullscreenEnabledProperty] != null) {\n      return Boolean(doc[fullscreenEnabledProperty])\n    }\n\n    // Fallback for WebKit and iOS Safari browsers\n    if (element && (element as any)[fullscreenEnabledProperty] != null) {\n      return Boolean((element as any)[fullscreenEnabledProperty])\n    }\n\n    return false\n  })\n\n  const enter = useMemoizedFn(async () => {\n    const { requestMethod } = properties.current\n    if (!isSupported || isFullscreen) return\n\n    if (isElementFullScreen()) {\n      await exit()\n    }\n\n    const element = getTargetElement(target)\n    if (requestMethod && element && (element as any)[requestMethod] != null) {\n      await (element as any)[requestMethod]()\n      setIsFullscreen(true)\n    }\n  })\n\n  const toggle = useMemoizedFn(async () => {\n    await (isFullscreen ? exit() : enter())\n  })\n\n  const handlerCallback = useMemoizedFn(() => {\n    const isElementFullScreenValue = isElementFullScreen()\n\n    if (\n      !isElementFullScreenValue ||\n      (isElementFullScreenValue && isCurrentElementFullScreen())\n    ) {\n      setIsFullscreen(isElementFullScreenValue)\n    }\n  })\n\n  const listenerOptions = { capture: false, passive: true }\n  // Listen to fullscreen change events on document\n  useEventListener(eventHandlers as any, handlerCallback, {\n    target: () => document,\n    ...listenerOptions,\n  })\n\n  // Listen to fullscreen change events on target element\n  useEventListener(eventHandlers as any, handlerCallback, {\n    target: () => getTargetElement(target),\n    ...listenerOptions,\n  })\n\n  // Check initial state on mount\n  useIsomorphicLayoutEffect(() => {\n    if (isBrowser) {\n      handlerCallback()\n    }\n  }, [])\n\n  useUnmount(() => {\n    if (autoExit) exit()\n  })\n\n  return {\n    isSupported,\n    isFullscreen,\n    enter,\n    exit,\n    toggle,\n  }\n}\n\nexport type UseFullscreenReturn = ReturnType<typeof useFullscreen>\n",
      "type": "registry:hook"
    }
  ],
  "type": "registry:hook"
}
