diff --git a/docs/useFullscreen.md b/docs/useFullscreen.md new file mode 100644 index 0000000000..7b7d0b0ae1 --- /dev/null +++ b/docs/useFullscreen.md @@ -0,0 +1,32 @@ +# `useFullscreen` + +Display an element full-screen, optional fallback for fullscreen video on iOS. + +## Usage + +```jsx +import React, { useRef } from 'react'; +import { useFullscreen } from 'react-use'; + +const Demo = () => { + const ref = useRef(null) + const videoRef = useRef(null) + const [fullscreen, toggle] = useFullscreen(ref, videoRef); + + return ( +
+
{fullscreen ? 'Fullscreen' : 'Not fullscreen'}
+ + + +
+ ); +}; +``` + +## Reference + +```ts +useFullscreen(ref: RefObject, videoRef?: RefObject); +``` diff --git a/package.json b/package.json index b14797abd4..38ed234f22 100644 --- a/package.json +++ b/package.json @@ -36,13 +36,11 @@ "dependencies": { "nano-css": "^5.1.0", "react-wait": "^0.3.0", + "screenfull": "^4.1.0", "throttle-debounce": "^2.0.1", "ts-easing": "^0.2.0" }, "devDependencies": { - "@types/react": "16.8.8", - "keyboardjs": "2.5.1", - "rebound": "0.1.0", "@semantic-release/changelog": "3.0.2", "@semantic-release/git": "7.0.8", "@semantic-release/npm": "5.1.4", @@ -51,13 +49,16 @@ "@storybook/addon-notes": "5.0.5", "@storybook/addon-options": "5.0.5", "@storybook/react": "5.0.5", + "@types/react": "16.8.8", "babel-core": "6.26.3", "fork-ts-checker-webpack-plugin": "1.0.0", "gh-pages": "2.0.1", + "keyboardjs": "2.5.1", "markdown-loader": "5.0.0", "react": "16.8.4", "react-dom": "16.8.4", "react-spring": "6.1.10", + "rebound": "0.1.0", "rimraf": "2.6.3", "rxjs": "6.4.0", "semantic-release": "15.13.3", diff --git a/src/__stories__/useFullscreen.story.tsx b/src/__stories__/useFullscreen.story.tsx new file mode 100644 index 0000000000..fada757526 --- /dev/null +++ b/src/__stories__/useFullscreen.story.tsx @@ -0,0 +1,24 @@ +import * as React from 'react'; +import {storiesOf} from '@storybook/react'; +import {useFullscreen} from '..'; +import ShowDocs from '../util/ShowDocs'; + +const Demo = () => { + const ref = React.useRef(null) + const videoRef = React.useRef(null) + const [fullscreen, toggle] = useFullscreen(ref, videoRef); + + return ( +
+
{fullscreen ? 'Fullscreen' : 'Not fullscreen'}
+ + + +
+ ); +}; + +storiesOf('Side effects|useFullscreen', module) + .add('Docs', () => ) + .add('Demo', () => ) diff --git a/src/index.ts b/src/index.ts index da3afd581b..91380a2105 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,7 @@ import useCounter from './useCounter'; import useCss from './useCss'; import useDebounce from './useDebounce'; import useFavicon from './useFavicon'; +import useFullscreen from './useFullscreen'; import useGeolocation from './useGeolocation'; import useGetSet from './useGetSet'; import useGetSetState from './useGetSetState'; @@ -68,6 +69,7 @@ export { useCss, useDebounce, useFavicon, + useFullscreen, useGeolocation, useGetSet, useGetSetState, diff --git a/src/useFullscreen.ts b/src/useFullscreen.ts new file mode 100644 index 0000000000..55f3003bb1 --- /dev/null +++ b/src/useFullscreen.ts @@ -0,0 +1,68 @@ +import {useEffect, RefObject, useCallback} from 'react'; +import screenfull from 'screenfull'; +import useToggle from './useToggle' + +export interface State { + fullscreen: boolean +} + +const useFullscreen = (ref: RefObject, videoRef?: RefObject): [boolean, (value?: boolean) => void] => { + const [fullscreen, toggle] = useToggle(false); + + useEffect(() => { + const onChange = () => { + if (screenfull) { + toggle(screenfull.isFullscreen) + } + } + + if (screenfull && screenfull.enabled) { + screenfull.on('change', onChange); + } + + return () => { + if (screenfull && screenfull.enabled) { + screenfull.off('change', onChange); + } + } + }, []); + + const toggleFullscreen = useCallback(async (nextValue?: boolean) => { + nextValue = typeof nextValue === 'undefined' ? !fullscreen : nextValue; + + if (screenfull && screenfull.enabled) { + try { + if (nextValue) { + await screenfull.request(ref.current || undefined); + } else { + await screenfull.exit(); + } + toggle(nextValue); + } catch {} + } else { + if (videoRef && videoRef.current) { + if (nextValue) { + if (videoRef.current.webkitEnterFullscreen) { + const onWebkitEndFullscreen = () => { + if (videoRef.current) { + videoRef.current.removeEventListener('webkitendfullscreen', onWebkitEndFullscreen); + toggle(false) + } + }; + + videoRef.current.webkitEnterFullscreen(); + toggle(true) + videoRef.current.addEventListener('webkitendfullscreen', onWebkitEndFullscreen); + } + } else if (videoRef.current.webkitExitFullscreen) { + videoRef.current.webkitExitFullscreen(); + toggle(false) + } + } + } + }, [fullscreen, toggle]) + + return [fullscreen, toggleFullscreen] +}; + +export default useFullscreen; diff --git a/yarn.lock b/yarn.lock index bd57004539..146ded6453 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9684,6 +9684,11 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" +screenfull@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-4.1.0.tgz#30eb338f615941f5a2cdd96c14e36063d2d9d764" + integrity sha512-/qH0HAmc+ilbZ9Vf8J7RHjjecSdqmjIh98iMkA6uCSKcHdJK1TiXhTbR+cin8rG70xi4Peyz7wW1KJVP6sp30g== + select@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"