UNPKG

@rooks/use-fullscreen

Version:

A React Hooks package for fullscreen.

261 lines (252 loc) 9.57 kB
import { __awaiter } from 'tslib'; import { useEffect, useLayoutEffect, useRef, useState, useCallback } from 'react'; /** * useIsomorphicEffect * Resolves to useEffect when "window" is not in scope and useLayout effect in the browser * @param {function} callback Callback function to be called on mount */ const useIsomorphicEffect = typeof window === "undefined" ? useEffect : useLayoutEffect; /** * useFreshRef * @param value The value which needs to be fresh at all times. Probably * best used with functions * @param preferLayoutEffect Should the value be updated using a layout effect * or a passive effect. Defaults to false. * @returns A ref containing the fresh value */ function useFreshRef(value, preferLayoutEffect = false) { const useEffectToUse = preferLayoutEffect ? useIsomorphicEffect : useEffect; const ref = useRef(value); useEffectToUse(() => { ref.current = value; }); return ref; } function useFreshTick(callback) { const freshRef = useFreshRef(callback); function tick(...args) { if (freshRef && typeof freshRef.current === "function") { freshRef.current(...args); } } return tick; } /** * useGlobalObjectEventListener hook * * A react hook to an event listener to a global object * * @param {Window|Document} globalObject The global object to add event onto * @param {string} eventName The event to track * @param {function} callback The callback to be called on event * @param {object} conditions The options to be passed to the event listener * @param {boolean} when Should the event listener be active * @param {boolean} isLayoutEffect Should it use layout effect. Defaults to false * @return {undefined} */ function useGlobalObjectEventListener(globalObject, eventName, callback, listenerOptions = {}, when = true, isLayoutEffect = false) { const freshCallback = useFreshTick(callback); const { capture, passive, once } = listenerOptions; const useEffectToRun = isLayoutEffect ? useIsomorphicEffect : useEffect; useEffectToRun(() => { if (typeof globalObject !== "undefined" && globalObject.addEventListener && when) { globalObject.addEventListener(eventName, freshCallback, listenerOptions); return () => { globalObject.removeEventListener(eventName, freshCallback, listenerOptions); }; } }, [eventName, capture, passive, once]); } /** * useDocumentEventListener hook * * A react hook to an event listener to the document * * @param {string} eventName The event to track * @param {function} callback The callback to be called on event * @param {object} conditions The options to be passed to the event listener * @param {boolean} isLayoutEffect Should it use layout effect. Defaults to false * @return {undefined} */ function useDocumentEventListener(eventName, callback, listenerOptions = {}, isLayoutEffect = false) { if (typeof document !== "undefined") { useGlobalObjectEventListener(document, eventName, callback, listenerOptions, true, isLayoutEffect); } else { console.warn("useDocumentEventListener can't attach an event listener as document is undefined."); } } const __DEV__ = process.env.NODE_ENV !== 'production'; let warning = function () { }; if (__DEV__) { const printWarning = function printWarning(...actualMessage) { const message = `Warning: ${actualMessage}`; if (typeof console !== 'undefined') { console.error(message); } try { // --- Welcome to debugging React --- // This error was thrown as a convenience so that you can use this stack // to find the callsite that caused this warning to fire. throw new Error(message); } catch (x) { } }; warning = function (condition, actualMessage) { if (!condition) { printWarning(actualMessage); } }; } const getFullscreenControls = () => { const fnMap = [ [ 'requestFullscreen', 'exitFullscreen', 'fullscreenElement', 'fullscreenEnabled', 'fullscreenchange', 'fullscreenerror', ], // New WebKit [ 'webkitRequestFullscreen', 'webkitExitFullscreen', 'webkitFullscreenElement', 'webkitFullscreenEnabled', 'webkitfullscreenchange', 'webkitfullscreenerror', ], // Old WebKit [ 'webkitRequestFullScreen', 'webkitCancelFullScreen', 'webkitCurrentFullScreenElement', 'webkitCancelFullScreen', 'webkitfullscreenchange', 'webkitfullscreenerror', ], [ 'mozRequestFullScreen', 'mozCancelFullScreen', 'mozFullScreenElement', 'mozFullScreenEnabled', 'mozfullscreenchange', 'mozfullscreenerror', ], [ 'msRequestFullscreen', 'msExitFullscreen', 'msFullscreenElement', 'msFullscreenEnabled', 'MSFullscreenChange', 'MSFullscreenError', ], ]; const ret = {}; fnMap.forEach((fnSet) => { if (fnSet && fnSet[1] in document) { fnSet.forEach((_fn, i) => { ret[fnMap[0][i]] = fnSet[i]; }); } }); return ret; }; const noop = () => { }; const defaultValue = { isEnabled: false, toggle: noop, onChange: noop, onError: noop, request: noop, exit: noop, isFullscreen: false, element: undefined, }; function warnDeprecatedOnChangeAndOnErrorUsage() { warning(false, `Using onChange and onError from the return value is deprecated and will be removed in the next major version. Please use it with arguments instead. For eg: useFullscreen({onChange: function() {}, onError: function(){}}) `); } /** * useFullscreen * A hook that helps make the document fullscreen */ function useFullscreen(options = {}) { if (typeof window === 'undefined') { return defaultValue; } const { onChange: onChangeArg, onError: onErrorArg } = options; const fullscreenControls = getFullscreenControls(); const [isFullscreen, setIsFullscreen] = useState(Boolean(document[fullscreenControls.fullscreenElement])); const [element, setElement] = useState(document[fullscreenControls.fullscreenElement]); const request = useCallback((element) => __awaiter(this, void 0, void 0, function* () { try { const finalElem = element || document.documentElement; return yield finalElem[fullscreenControls.requestFullscreen](); } catch (err) { console.log(err); } }), []); const exit = useCallback(() => __awaiter(this, void 0, void 0, function* () { if (element) { try { return yield document[fullscreenControls.exitFullscreen](); } catch (err) { console.warn(err); } } }), [element]); const toggle = useCallback((newElement) => Boolean(element) ? exit() : newElement ? request(newElement) : null, [element]); const onChangeDeprecatedHandlerRef = useRef(noop); const onErrorDeprecatedHandlerRef = useRef(noop); // Hack to not break it for everyone // Honestly these two functions are tragedy and must be removed in v5 const onChangeDeprecated = useCallback((callback) => { warnDeprecatedOnChangeAndOnErrorUsage(); return (onChangeDeprecatedHandlerRef.current = callback); }, []); const onErrorDeprecated = useCallback((callback) => { warnDeprecatedOnChangeAndOnErrorUsage(); return (onErrorDeprecatedHandlerRef.current = callback); }, []); useDocumentEventListener(fullscreenControls.fullscreenchange, function (event) { var _a; const currentFullscreenElement = document[fullscreenControls.fullscreenElement]; const isOpen = Boolean(currentFullscreenElement); if (isOpen) { //fullscreen was enabled setIsFullscreen(true); setElement(currentFullscreenElement); } else { //fullscreen was disabled setIsFullscreen(false); setElement(null); } onChangeArg === null || onChangeArg === void 0 ? void 0 : onChangeArg.call(document, event, isOpen); (_a = onChangeDeprecatedHandlerRef.current) === null || _a === void 0 ? void 0 : _a.call(document, event, isOpen); }); useDocumentEventListener(fullscreenControls.fullscreenerror, function (event) { var _a; onErrorArg === null || onErrorArg === void 0 ? void 0 : onErrorArg.call(document, event); (_a = onErrorDeprecatedHandlerRef.current) === null || _a === void 0 ? void 0 : _a.call(document, event); }); return { isEnabled: Boolean(document[fullscreenControls.fullscreenEnabled]), toggle, onChange: onChangeDeprecated, onError: onErrorDeprecated, request, exit, isFullscreen, element, }; } export default useFullscreen; //# sourceMappingURL=index.esm.js.map