UNPKG

react-fullscreenable

Version:

React higher order component that provides fullscreen API to the enhanced component

349 lines (285 loc) 15.8 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); exports.default = withFullscreen; var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _propTypes = require('prop-types'); var _propTypes2 = _interopRequireDefault(_propTypes); var _fullscreen = require('fullscreen'); var _fullscreen2 = _interopRequireDefault(_fullscreen); var _reactDom = require('react-dom'); var _reactDisplayName = require('react-display-name'); var _reactDisplayName2 = _interopRequireDefault(_reactDisplayName); var _classnames = require('classnames'); var _classnames2 = _interopRequireDefault(_classnames); var _getViewportDimensions = require('./getViewportDimensions'); var _getViewportDimensions2 = _interopRequireDefault(_getViewportDimensions); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* eslint-disable no-return-assign */ var CAN_HAS_DOM = typeof window !== 'undefined' && window.document && window.document.createElement; var noop = function noop() {}; function withFullscreen() { var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, _ref$onError = _ref.onError, onError = _ref$onError === undefined ? noop : _ref$onError; return function (WrappedComponent) { var Fullscreenable = function (_Component) { _inherits(Fullscreenable, _Component); function Fullscreenable(props) { _classCallCheck(this, Fullscreenable); var _this = _possibleConstructorReturn(this, (Fullscreenable.__proto__ || Object.getPrototypeOf(Fullscreenable)).call(this, props)); var isAvailable = void 0; var isEnabled = void 0; if (CAN_HAS_DOM) { isAvailable = _fullscreen2.default.available(); isEnabled = _fullscreen2.default.enabled(); } _this.handleRootNodeRef = _this.handleRootNodeRef.bind(_this); _this.handleToggleFullscreen = _this.handleToggleFullscreen.bind(_this); _this.handleResize = _this.handleResize.bind(_this); // orientation change _this.handleOrntChange = _this.handleOrntChange.bind(_this); // ignore touch events _this.listenTouchMove = _this.listenTouchMove.bind(_this); _this.disposePseudoFullscreen = _this.disposePseudoFullscreen.bind(_this); _this.state = { isFullscreen: false, isPseudoFullscreen: false, isAvailable: isAvailable, isEnabled: isEnabled, isNativeCapable: isAvailable && isEnabled, viewportDimensions: null, scrollYStart: 0 }; return _this; } _createClass(Fullscreenable, [{ key: 'componentDidMount', value: function componentDidMount() { var isPseudoFullscreen = this.props.isPseudoFullscreen; if (this.state.isNativeCapable && !this.props.forcePseudoFullscreen && !isPseudoFullscreen) { this.fs = this.attachNativeFullscreen(); } else { this.fs = this.attachPseudoFullscreen(); if (isPseudoFullscreen) { this.fs.request(); } } } }, { key: 'attachNativeFullscreen', value: function attachNativeFullscreen() { var _this2 = this; var emitter = (0, _fullscreen2.default)(this.rootNode); emitter.on('attain', function () { _this2.rootNode.style.height = '100%'; _this2.rootNode.style.width = '100%'; // Set state immediately to avoid race condition with pressing Escape key _this2.setState({ isFullscreen: true }, function () { _this2.props.onFullscreenChange(true); }); // Delay is necessary in order to be able to get the // correct dimensions of the window. If we update too soon // the values reported by innerHeight and innerWidth are // incorrect. setTimeout(function () { if (_this2.state.isFullscreen) { _this2.setState({ viewportDimensions: (0, _getViewportDimensions2.default)() }); } }, 300); }); emitter.on('release', function () { _this2.rootNode.style.height = ''; _this2.rootNode.style.width = ''; _this2.setState({ isFullscreen: false, viewportDimensions: null }, function () { _this2.props.onFullscreenChange(false); }); }); emitter.on('error', function (error) { _this2.setState({ isFullscreen: false, viewportDimensions: null }); // You really only get onfullscreenerror when requesting // fullscreen outside of a real event, or if it's disabled // by the user. In that case, we don't // pass a toggle function so there shouldn't be any requests // when it's disabled. onError(error); }); return emitter; } }, { key: 'attachPseudoFullscreen', value: function attachPseudoFullscreen() { var _this3 = this; return { request: function request() { window.addEventListener('resize', _this3.handleResize); window.addEventListener('orientationchange', _this3.handleOrntChange); var scrawly = window.scrollY; window.scrollTo(0, 0); _this3.bodyPosOrig = window.document.body.style.position; _this3.bodyMarginOrig = window.document.body.style.margin; window.document.body.style.position = 'fixed'; window.document.body.style.margin = '0'; _this3.setState({ isPseudoFullscreen: true, viewportDimensions: (0, _getViewportDimensions2.default)(), scrollYStart: scrawly }, function () { _this3.props.onFullscreenChange(true); }); }, release: function release() { _this3.disposePseudoFullscreen(); _this3.setState({ isPseudoFullscreen: false, viewportDimensions: null, scrollYStart: _this3.state.scrollYStart }, function () { _this3.props.onFullscreenChange(false); }); }, dispose: function dispose() { _this3.disposePseudoFullscreen(); } }; } }, { key: 'disposePseudoFullscreen', value: function disposePseudoFullscreen() { window.removeEventListener('resize', this.handleResize); window.removeEventListener('orientationchange', this.handleOrntChange); window.document.body.style.position = this.bodyPosOrig || ''; window.document.body.style.margin = this.bodyMarginOrig; window.scrollTo(0, this.state.scrollYStart); } }, { key: 'handleResize', value: function handleResize() { window.scrollTo(0, 0); this.setState({ viewportDimensions: (0, _getViewportDimensions2.default)() }); } }, { key: 'handleOrntChange', value: function handleOrntChange() { // On iPhone/iPad the orientationchange event is fired at the // start of the rotation. The window.inner* dimension values // are not correct until after the animation completes. // So we wait. setTimeout(this.handleResize, 400); } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { this.fs.dispose(); } }, { key: 'handleRootNodeRef', value: function handleRootNodeRef(ref) { this.rootNode = (0, _reactDom.findDOMNode)(ref); } }, { key: 'handleToggleFullscreen', value: function handleToggleFullscreen() { var _state = this.state, isFullscreen = _state.isFullscreen, isPseudoFullscreen = _state.isPseudoFullscreen; if (isFullscreen || isPseudoFullscreen) { this.fs.release(); } else { this.fs.request(); } } }, { key: 'listenTouchMove', value: function listenTouchMove() { // If the user taps the top of the screen on an iPad or iPhone // this can trigger the URL bar to show. However this does not // fire a resize event. Instead, we check window size on // movement to see if it has changed. // If you don't want this to happen, you can call // event.stopPropagation(); in your own listener on a child node // and this handler won't see it. var dims = this.state.viewportDimensions; var newDims = (0, _getViewportDimensions2.default)(); if (dims) { if (dims.height !== newDims.height || dims.width !== newDims.width) { this.handleResize(); } } } }, { key: 'render', value: function render() { var _state2 = this.state, isEnabled = _state2.isEnabled, isPseudoFullscreen = _state2.isPseudoFullscreen, isFullscreen = _state2.isFullscreen, viewportDimensions = _state2.viewportDimensions; var wrapperClass = (0, _classnames2.default)({ fullscreenable: true, fullscreen: isFullscreen || isPseudoFullscreen, fullscreen_disabled: !isEnabled, fullscreen_pseudo: isPseudoFullscreen }); var listenTouchMove = void 0; var style = null; if (isPseudoFullscreen) { listenTouchMove = this.listenTouchMove; style = { height: viewportDimensions.height, width: viewportDimensions.width, position: 'absolute', top: '0', right: '0', bottom: '0', left: '0' }; } return _react2.default.createElement( 'div', { className: wrapperClass, style: style, onTouchMove: listenTouchMove, ref: this.handleRootNodeRef }, _react2.default.createElement(WrappedComponent, _extends({}, this.props, { toggleFullscreen: this.handleToggleFullscreen, isFullscreen: Boolean(isFullscreen || isPseudoFullscreen), viewportDimensions: viewportDimensions })) ); } }]); return Fullscreenable; }(_react.Component); Fullscreenable.displayName = 'Fullscreenable(' + (0, _reactDisplayName2.default)(WrappedComponent) + ')'; Fullscreenable.propTypes = { forcePseudoFullscreen: _propTypes2.default.bool, isPseudoFullscreen: _propTypes2.default.bool, onFullscreenChange: _propTypes2.default.func }; Fullscreenable.defaultProps = { onFullscreenChange: function onFullscreenChange() {} }; return Fullscreenable; }; }