UNPKG

rezponsive

Version:

React decorator for responsive behaviors

193 lines (164 loc) 6.21 kB
function _extends() { _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; }; return _extends.apply(this, arguments); } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } var canUseDOM = !!(typeof window !== "undefined" && window.document && window.document.createElement); import React, { Component } from "react"; import PropTypes from "prop-types"; // eslint-disable-next-line @typescript-eslint/no-var-requires var MQ = require("mediaquery"); export var RezponsiveContext = /*#__PURE__*/React.createContext({}); function isTouchDevice() { // unsupported browser such as IE11 are desktop only and will correctly // return false return window.matchMedia("(pointer: coarse)").matches; } var isObject = a => a !== undefined && a !== null && typeof a === "object"; var isDifferent = (a, b) => { return Object.keys(a).some(el => a[el] !== b[el]); }; export default function Rezponsive(Element) { class RezponsiveComponent extends Component { static getDerivedStateFromProps(props, state) { if (!canUseDOM) return null; // short circuit for invalid clientMedia if (!isObject(props.clientMedia)) return null; // no previous clientMedia defined if (!isObject(state.prevClientMedia)) { // override the currentMedia with the newly passed clientMedia prop if (isDifferent(props.clientMedia, state.currentMedia) || props.isTouch !== state.isTouch) { return { prevClientMedia: props.clientMedia, currentMedia: props.clientMedia, prevIsTouch: props.isTouch, isTouch: props.isTouch }; } else { return null; } } var needsIsTouchUpdate = props.isTouch !== state.prevIsTouch && props.isTouch !== state.isTouch; var needsClientMediaUpdate = isDifferent(props.clientMedia, state.prevClientMedia) && isDifferent(props.clientMedia, state.currentMedia); // both clientMedia and prevClientMedia are defined and valid if (needsClientMediaUpdate || needsIsTouchUpdate) { return { prevClientMedia: props.clientMedia, currentMedia: props.clientMedia, prevIsTouch: needsIsTouchUpdate ? props.isTouch : props.prevIsTouch, isTouch: needsIsTouchUpdate ? props.isTouch : isTouchDevice() }; } return null; } constructor(props) { super(props); _defineProperty(this, "skipInitialCheck", void 0); _defineProperty(this, "state", void 0); _defineProperty(this, "props", void 0); this.updateMediaQueries = this.updateMediaQueries.bind(this); var mq = MQ.asArray(props.mq); if (canUseDOM) { var isTouch = props.isTouch !== undefined ? props.isTouch : isTouchDevice(); var initialCurrentMedia = props.clientMedia || mq.reduce((matches, q, index, mq) => { if (index === mq.length) { matches[q[0]] = true; } else { matches[q[0]] = false; } return matches; }, {}); this.skipInitialCheck = props.clientMedia !== undefined; this.state = { prevClientMedia: props.clientMedia, prevIsTouch: props.isTouch, mm: window.matchMedia, mq: mq, isTouch: isTouch, currentMedia: initialCurrentMedia }; } else { this.state = { isTouch: props.isTouchOnServer, currentMedia: {} }; this.state.currentMedia = props.serverMedia; } } componentDidMount() { if (this.props.disableListeners) return; this.updateMediaQueries(); var { mm, mq } = this.state; Object.keys(mq).forEach(q => { mm(mq[q]).addListener(this.updateMediaQueries); }); } componentWillUnmount() { if (this.props.disableListeners) return; var { mm, mq } = this.state; Object.keys(mq).forEach(q => { mm(mq[q]).removeListener(this.updateMediaQueries); }); } updateMediaQueries() { var { mm, mq, currentMedia, skipInitialMatch } = this.state; if (this.skipInitialCheck) { this.skipInitialCheck = false; return; } var newMedia = mq.reduce((matches, q) => { matches[q[0]] = mm(q[1]).matches; return matches; }, {}); var needsUpdate = Object.keys(newMedia).reduce((shouldUpdate, query) => { return shouldUpdate || newMedia[query] !== currentMedia[query]; }, false); if (needsUpdate) { this.setState({ currentMedia: mq.reduce((matches, q) => { matches[q[0]] = mm(q[1]).matches; return matches; }, {}) }); } } render() { return /*#__PURE__*/React.createElement(RezponsiveContext.Provider, { value: { isTouch: this.state.isTouch, currentMedia: this.state.currentMedia } }, /*#__PURE__*/React.createElement(Element, _extends({ isTouch: this.state.isTouch, currentMedia: this.state.currentMedia }, this.props))); } } RezponsiveComponent.propTypes = { mq: PropTypes.object, isTouchOnServer: PropTypes.bool, isTouch: PropTypes.bool, serverMedia: PropTypes.object, clientMedia: PropTypes.object, disableListeners: PropTypes.bool }; RezponsiveComponent.defaultProps = { mq: { all: "all" }, isTouchOnServer: false, serverMedia: { all: true }, disableListeners: false }; return RezponsiveComponent; } export function RezponsiveConsumer(Element) { return props => /*#__PURE__*/React.createElement(RezponsiveContext.Consumer, null, ctx => /*#__PURE__*/React.createElement(Element, _extends({}, ctx, props))); } //# sourceMappingURL=rezponsive.js.map