diff --git a/package.json b/package.json index 2955f10253..8d0abb3aa4 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "classnames": "^2.2.6", "deepmerge": "^4.2.2", "dompurify": "^3.0.0", + "fscreen": "^1.2.0", "i18next": "^21.0.0 || ^22.0.0 || ^23.0.0", "lodash": "^4.17.11", "manifesto.js": "^4.2.0", @@ -60,7 +61,6 @@ "react-dnd-html5-backend": "^16.0.0", "react-dnd-multi-backend": "^8.0.0", "react-dnd-touch-backend": "^16.0.0", - "react-full-screen": "^1.1.1", "react-i18next": "^11.7.0 || ^12.0.0 || ^13.0.0", "react-image": "^4.0.1", "react-intersection-observer": "^9.0.0", diff --git a/src/components/AppProviders.js b/src/components/AppProviders.js index f39aae6098..57c28a5083 100644 --- a/src/components/AppProviders.js +++ b/src/components/AppProviders.js @@ -1,6 +1,5 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import { FullScreen, useFullScreenHandle } from 'react-full-screen'; import { I18nextProvider } from 'react-i18next'; import { ThemeProvider, StyledEngineProvider, createTheme, @@ -12,6 +11,7 @@ import rtlPlugin from 'stylis-plugin-rtl'; import { prefixer } from 'stylis'; import { CacheProvider } from '@emotion/react'; import createCache from '@emotion/cache'; +import { FullScreen, useFullScreenHandle } from '../lib/FullScreenHandle'; import createI18nInstance from '../i18n'; import FullScreenContext from '../contexts/FullScreenContext'; diff --git a/src/lib/FullScreenHandle.js b/src/lib/FullScreenHandle.js new file mode 100644 index 0000000000..4cca540e97 --- /dev/null +++ b/src/lib/FullScreenHandle.js @@ -0,0 +1,91 @@ +import { + useCallback, + useState, + useEffect, + useMemo, +} from 'react'; +import PropTypes from 'prop-types'; +import fscreen from 'fscreen'; + +/** + * Used to request or exit fullscreen using the Fullscreen API, normalized + * using fscreen. + */ +export function useFullScreenHandle() { + const [active, setActive] = useState(false); + + /** */ + const handleFullScreenChange = () => { + setActive(fscreen.fullscreenElement === document.body); + }; + + useEffect(() => { + fscreen.addEventListener('fullscreenchange', handleFullScreenChange); + return () => fscreen.removeEventListener('fullscreenchange', handleFullScreenChange); + }, []); + + /** */ + const requestFullscreen = () => fscreen.requestFullscreen(document.body); + + const enter = useCallback(() => { + if (fscreen.fullscreenElement) { + return fscreen.exitFullscreen() + .then(() => requestFullscreen()); + } + return requestFullscreen(); + }, []); + + const exit = useCallback(() => { + if (fscreen.fullscreenElement !== document.body) return Promise.resolve(); + return fscreen.exitFullscreen(); + }, []); + + return useMemo( + () => ({ + active, + enter, + exit, + }), + [active, enter, exit], + ); +} + +/** + * Used to set its children to fullscreen. + */ +export const FullScreen = ({ + handle, + onChange, + children, + className = '', +}) => { + const fullScreenClasses = ['fullscreen', className, handle.active ? 'fullscreen-enabled' : ''].filter(Boolean); + + useEffect(() => { + if (onChange) { + onChange(handle.active, handle); + } + }, [handle, handle.active, onChange]); + + const styles = handle.active ? { + height: '100%', + width: '100%', + } : {}; + + return ( +
+ {children} +
+ ); +}; + +FullScreen.propTypes = { + children: PropTypes.node, + className: PropTypes.string.isRequired, + handle: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types + onChange: PropTypes.func, +}; +FullScreen.defaultProps = { + children: null, + onChange: undefined, +};