react-fullscreenable
Version:
React higher order component that provides fullscreen API to the enhanced component
349 lines (285 loc) • 15.8 kB
JavaScript
;
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;
};
}