react-breakpoints
Version:
A library that allows you to mount/unmount components depending on the viewport size. Welcome to the next level of responsive React applications.
205 lines (171 loc) • 8.03 kB
JavaScript
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 _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof 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; }
import React from 'react';
import PropTypes from 'prop-types';
import debounce from 'lodash.debounce';
import { em, stripUnit } from './utils';
import { Provider } from './BreakpointsContext';
import { ERRORS } from './messages';
var ReactBreakpoints = function (_React$Component) {
_inherits(ReactBreakpoints, _React$Component);
function ReactBreakpoints(props) {
_classCallCheck(this, ReactBreakpoints);
var _this = _possibleConstructorReturn(this, _React$Component.call(this, props));
_initialiseProps.call(_this);
var _this$props = _this.props,
breakpoints = _this$props.breakpoints,
defaultBreakpoint = _this$props.defaultBreakpoint,
guessedBreakpoint = _this$props.guessedBreakpoint;
// throw Error if no breakpoints were passed
if (!breakpoints) throw new Error(ERRORS.NO_BREAKPOINTS);
// throw Error if breakpoints is not an object
if ((typeof breakpoints === 'undefined' ? 'undefined' : _typeof(breakpoints)) !== 'object') throw new Error(ERRORS.NOT_OBJECT);
var currentBreakpoint = null;
// if we are on the client, we directly compote the breakpoint using window width
if (global.window) {
currentBreakpoint = _this.calculateCurrentBreakpoint(_this.convertScreenWidth(global.window.innerWidth));
} else if (guessedBreakpoint) {
currentBreakpoint = _this.calculateCurrentBreakpoint(guessedBreakpoint);
} else if (defaultBreakpoint) {
currentBreakpoint = _this.calculateCurrentBreakpoint(defaultBreakpoint);
}
var screenWidth = global.window ? _this.convertScreenWidth(global.window.innerWidth) : defaultBreakpoint;
_this.state = {
breakpoints: breakpoints || {},
// if we are on the client, we set the screen width to the window width,
// otherwise, we use the default breakpoint
screenWidth: screenWidth,
currentBreakpoint: currentBreakpoint
};
return _this;
}
ReactBreakpoints.prototype.convertScreenWidth = function convertScreenWidth(screenWidth) {
var breakpointUnit = this.props.breakpointUnit;
return breakpointUnit === 'em' ? stripUnit(em(screenWidth)) : screenWidth;
};
ReactBreakpoints.prototype.componentDidMount = function componentDidMount() {
if (typeof window !== 'undefined') {
this.readWidth(); // initial width calculation
if (this.props.debounceResize) {
window.addEventListener('resize', debounce(this.readWidth, this.props.debounceDelay));
} else {
window.addEventListener('resize', this.readWidth);
}
window.addEventListener('orientationchange', this.readWidth);
}
};
ReactBreakpoints.prototype.componentWillUnmount = function componentWillUnmount() {
if (typeof window !== 'undefined') {
if (this.props.debounceResize) {
window.addEventListener('resize', debounce(this.readWidth, this.props.debounceDelay));
} else {
window.addEventListener('resize', this.readWidth);
}
window.removeEventListener('orientationchange', this.readWidth);
}
};
ReactBreakpoints.prototype.calculateCurrentBreakpoint = function calculateCurrentBreakpoint(screenWidth) {
var _this2 = this;
var currentBreakpoint = null;
var breakpointKeys = Object.keys(this.props.breakpoints);
new (Function.prototype.bind.apply(Array, [null].concat(breakpointKeys)))().reverse() // reverse array to put largest breakpoint first
.map(function (breakpoint) {
var breakpointValue = _this2.props.breakpoints[breakpoint];
if (!currentBreakpoint && screenWidth >= breakpointValue) {
currentBreakpoint = breakpoint;
}
});
// If currentBreakpoint is null here, screenWidth is below lowest breakpoint,
// so it will still be set to equal lowest breakpoint instead of null
if (currentBreakpoint === null) {
currentBreakpoint = breakpointKeys[0];
}
return currentBreakpoint;
};
ReactBreakpoints.prototype.render = function render() {
var children = this.props.children;
return React.createElement(
Provider,
{ value: this.getContextValues() },
children && children
);
};
return ReactBreakpoints;
}(React.Component);
ReactBreakpoints.defaultProps = {
breakpointUnit: 'px',
debounceResize: false,
debounceDelay: 50,
snapMode: true
};
ReactBreakpoints.propTypes = {
/*
@breakpoints
Your breakpoints object.
*/
breakpoints: PropTypes.objectOf(PropTypes.number),
/*
@breakpointUnit
The type of unit that your breakpoints should use - px or em.
*/
breakpointUnit: PropTypes.oneOf(['px', 'em']),
/*
@guessedBreakpoint
When rendering on the server, you can do your own magic with for example UA
to guess which viewport width a user probably has.
*/
guessedBreakpoint: PropTypes.number, // from server
/*
@defaultBreakpoint
In case you don't want to default to mobile on SSR and no guessedBreakpoint
is passed, use defaultBreakpoint to set your own value.
*/
defaultBreakpoint: PropTypes.number,
/*
@debounceResize
If you don't want the resize listener to be debounced, set to false. Defaults to false
when snapMode is true.
*/
debounceResize: PropTypes.bool,
/*
@debounceDelay: number
Set a custom delay for how long the debounce timeout should be.
*/
debounceDelay: PropTypes.number,
/*
@snapMode
Replaces breakpoints.current with screenWidth, disabling re-render only
when breakpoint changes, instead potentially re-rendering when
calculateCurrentBreakpoint returns a new value.
*/
snapMode: PropTypes.bool
};
var _initialiseProps = function _initialiseProps() {
var _this3 = this;
this.readWidth = function (event) {
var snapMode = _this3.props.snapMode;
var width = event ? event.target.innerWidth ? event.target.innerWidth : window.innerWidth : window.innerWidth;
var screenWidth = _this3.convertScreenWidth(width);
var current = _this3.calculateCurrentBreakpoint(screenWidth);
_this3.setState(function (state) {
if (state.currentBreakpoint === current) return null;
return {
currentBreakpoint: snapMode ? current : null,
screenWidth: snapMode ? null : screenWidth
};
});
};
this.getContextValues = function () {
return _extends({
breakpoints: _extends({}, _this3.props.breakpoints)
}, _this3.props.snapMode && {
currentBreakpoint: _this3.state.currentBreakpoint
}, !_this3.props.snapMode && {
screenWidth: _this3.state.screenWidth
});
};
};
export default ReactBreakpoints;