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"