|
| 1 | +'use strict'; |
| 2 | + |
| 3 | +Object.defineProperty(exports, '__esModule', { value: true }); |
| 4 | + |
| 5 | +var React = require('react'); |
| 6 | +var PropTypes = require('prop-types'); |
| 7 | +var invariant = require('invariant'); |
| 8 | +var json2mq = require('json2mq'); |
| 9 | + |
| 10 | +function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } |
| 11 | + |
| 12 | +var React__default = /*#__PURE__*/_interopDefaultLegacy(React); |
| 13 | +var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes); |
| 14 | +var invariant__default = /*#__PURE__*/_interopDefaultLegacy(invariant); |
| 15 | +var json2mq__default = /*#__PURE__*/_interopDefaultLegacy(json2mq); |
| 16 | + |
| 17 | +function _extends() { |
| 18 | + _extends = Object.assign ? Object.assign.bind() : function (target) { |
| 19 | + for (var i = 1; i < arguments.length; i++) { |
| 20 | + var source = arguments[i]; |
| 21 | + for (var key in source) { |
| 22 | + if (Object.prototype.hasOwnProperty.call(source, key)) { |
| 23 | + target[key] = source[key]; |
| 24 | + } |
| 25 | + } |
| 26 | + } |
| 27 | + return target; |
| 28 | + }; |
| 29 | + return _extends.apply(this, arguments); |
| 30 | +} |
| 31 | + |
| 32 | +var MediaQueryListener = /*#__PURE__*/function () { |
| 33 | + function MediaQueryListener(targetWindow, query, listener) { |
| 34 | + var _this = this; |
| 35 | + this.nativeMediaQueryList = targetWindow.matchMedia(query); |
| 36 | + this.active = true; |
| 37 | + // Safari doesn't clear up listener with removeListener |
| 38 | + // when the listener is already waiting in the event queue. |
| 39 | + // Having an active flag to make sure the listener is not called |
| 40 | + // after we removeListener. |
| 41 | + this.cancellableListener = function () { |
| 42 | + _this.matches = _this.nativeMediaQueryList.matches; |
| 43 | + if (_this.active) { |
| 44 | + listener.apply(void 0, arguments); |
| 45 | + } |
| 46 | + }; |
| 47 | + this.nativeMediaQueryList.addListener(this.cancellableListener); |
| 48 | + this.matches = this.nativeMediaQueryList.matches; |
| 49 | + } |
| 50 | + var _proto = MediaQueryListener.prototype; |
| 51 | + _proto.cancel = function cancel() { |
| 52 | + this.active = false; |
| 53 | + this.nativeMediaQueryList.removeListener(this.cancellableListener); |
| 54 | + }; |
| 55 | + return MediaQueryListener; |
| 56 | +}(); |
| 57 | + |
| 58 | +var checkInvariants = function checkInvariants(_ref) { |
| 59 | + var query = _ref.query, |
| 60 | + queries = _ref.queries, |
| 61 | + defaultMatches = _ref.defaultMatches; |
| 62 | + !(!(!query && !queries) || query && queries) ? invariant__default["default"](false, '<Media> must be supplied with either "query" or "queries"') : void 0; |
| 63 | + !(defaultMatches === undefined || !query || typeof defaultMatches === "boolean") ? invariant__default["default"](false, "<Media> when query is set, defaultMatches must be a boolean, received " + typeof defaultMatches) : void 0; |
| 64 | + !(defaultMatches === undefined || !queries || typeof defaultMatches === "object") ? invariant__default["default"](false, "<Media> when queries is set, defaultMatches must be a object of booleans, received " + typeof defaultMatches) : void 0; |
| 65 | +}; |
| 66 | + |
| 67 | +/** |
| 68 | + * Wraps a single query in an object. This is used to provide backward compatibility with |
| 69 | + * the old `query` prop (as opposed to `queries`). If only a single query is passed, the object |
| 70 | + * will be unpacked down the line, but this allows our internals to assume an object of queries |
| 71 | + * at all times. |
| 72 | + */ |
| 73 | +var wrapInQueryObject = function wrapInQueryObject(query) { |
| 74 | + return { |
| 75 | + __DEFAULT__: query |
| 76 | + }; |
| 77 | +}; |
| 78 | + |
| 79 | +/** |
| 80 | + * Unwraps an object of queries, if it was originally passed as a single query. |
| 81 | + */ |
| 82 | +var unwrapSingleQuery = function unwrapSingleQuery(queryObject) { |
| 83 | + var queryNames = Object.keys(queryObject); |
| 84 | + if (queryNames.length === 1 && queryNames[0] === "__DEFAULT__") { |
| 85 | + return queryObject.__DEFAULT__; |
| 86 | + } |
| 87 | + return queryObject; |
| 88 | +}; |
| 89 | +var useMedia = function useMedia(_ref2) { |
| 90 | + var query = _ref2.query, |
| 91 | + queries = _ref2.queries, |
| 92 | + defaultMatches = _ref2.defaultMatches, |
| 93 | + targetWindow = _ref2.targetWindow, |
| 94 | + onChange = _ref2.onChange; |
| 95 | + checkInvariants({ |
| 96 | + query: query, |
| 97 | + queries: queries, |
| 98 | + defaultMatches: defaultMatches |
| 99 | + }); |
| 100 | + var activeQueries = React.useRef([]); |
| 101 | + var getMatches = function getMatches() { |
| 102 | + var result = activeQueries.current.reduce(function (acc, _ref3) { |
| 103 | + var _extends2; |
| 104 | + var name = _ref3.name, |
| 105 | + mqListener = _ref3.mqListener; |
| 106 | + return _extends({}, acc, (_extends2 = {}, _extends2[name] = mqListener.matches, _extends2)); |
| 107 | + }, {}); |
| 108 | + |
| 109 | + // return result; |
| 110 | + return unwrapSingleQuery(result); |
| 111 | + }; |
| 112 | + var updateMatches = function updateMatches() { |
| 113 | + setMatches(getMatches()); |
| 114 | + }; |
| 115 | + var setUpMQLs = function setUpMQLs() { |
| 116 | + var activeTargetWindow = targetWindow || window; |
| 117 | + !(typeof activeTargetWindow.matchMedia === "function") ? invariant__default["default"](false, "<Media targetWindow> does not support `matchMedia`.") : void 0; |
| 118 | + var queryObject = queries || wrapInQueryObject(query); |
| 119 | + activeQueries.current = Object.keys(queryObject).map(function (name) { |
| 120 | + var currentQuery = queryObject[name]; |
| 121 | + var qs = typeof currentQuery !== "string" ? json2mq__default["default"](currentQuery) : currentQuery; |
| 122 | + var mqListener = new MediaQueryListener(activeTargetWindow, qs, updateMatches); |
| 123 | + return { |
| 124 | + name: name, |
| 125 | + mqListener: mqListener |
| 126 | + }; |
| 127 | + }); |
| 128 | + }; |
| 129 | + var _useState = React.useState(function () { |
| 130 | + // If props.defaultMatches has been set, ensure we trigger a two-pass render. |
| 131 | + // This is useful for SSR with mismatching defaultMatches vs actual matches from window.matchMedia |
| 132 | + // Details: https://github.com/ReactTraining/react-media/issues/81 |
| 133 | + // TODO: figure out whether this is still technically a two-pass render. |
| 134 | + if (typeof window !== "object") { |
| 135 | + // In case we're rendering on the server, apply the default matches |
| 136 | + if (defaultMatches !== undefined) { |
| 137 | + return defaultMatches; |
| 138 | + } |
| 139 | + if (query) { |
| 140 | + return true; |
| 141 | + } |
| 142 | + /* if (props.queries) */ |
| 143 | + return Object.keys(queries).reduce(function (acc, key) { |
| 144 | + var _extends3; |
| 145 | + return _extends({}, acc, (_extends3 = {}, _extends3[key] = true, _extends3)); |
| 146 | + }, {}); |
| 147 | + } |
| 148 | + // Else we'll use the state from the MQLs that were just set up. |
| 149 | + setUpMQLs(); |
| 150 | + return getMatches(); |
| 151 | + }), |
| 152 | + matches = _useState[0], |
| 153 | + setMatches = _useState[1]; |
| 154 | + React.useEffect( |
| 155 | + // Because setup happens in the state constructor, cleanup is the only thing that |
| 156 | + // useEffect is responsible for. |
| 157 | + // eslint-disable-next-line react-hooks/exhaustive-deps |
| 158 | + function () { |
| 159 | + return function () { |
| 160 | + return activeQueries.current.forEach(function (_ref4) { |
| 161 | + var mqListener = _ref4.mqListener; |
| 162 | + return mqListener.cancel(); |
| 163 | + }); |
| 164 | + }; |
| 165 | + }, []); |
| 166 | + React.useEffect( |
| 167 | + // Set up a separate listener for onChange since we ideally want to fire onChange |
| 168 | + // after flushes, rather than having to insert it synchronously before an update happens. |
| 169 | + function () { |
| 170 | + if (onChange) { |
| 171 | + onChange(matches); |
| 172 | + } |
| 173 | + }, [matches, onChange]); |
| 174 | + return matches; |
| 175 | +}; |
| 176 | + |
| 177 | +/** |
| 178 | + * Conditionally renders based on whether or not a media query matches. |
| 179 | + */ |
| 180 | +var Media = function Media(_ref5) { |
| 181 | + var defaultMatches = _ref5.defaultMatches, |
| 182 | + query = _ref5.query, |
| 183 | + queries = _ref5.queries, |
| 184 | + render = _ref5.render, |
| 185 | + children = _ref5.children, |
| 186 | + targetWindow = _ref5.targetWindow, |
| 187 | + onChange = _ref5.onChange; |
| 188 | + var matches = useMedia({ |
| 189 | + query: query, |
| 190 | + queries: queries, |
| 191 | + defaultMatches: defaultMatches, |
| 192 | + targetWindow: targetWindow, |
| 193 | + onChange: onChange |
| 194 | + }); |
| 195 | + |
| 196 | + // render |
| 197 | + var isAnyMatches = typeof matches === "object" ? Object.keys(matches).some(function (key) { |
| 198 | + return matches[key]; |
| 199 | + }) : matches; |
| 200 | + return render ? isAnyMatches ? render(matches) : null : children ? typeof children === "function" ? children(matches) : !Array.isArray(children) || children.length // Preact defaults to empty children array |
| 201 | + ? isAnyMatches |
| 202 | + // We have to check whether child is a composite component or not to decide should we |
| 203 | + // provide `matches` as a prop or not |
| 204 | + ? React__default["default"].Children.only(children) && typeof React__default["default"].Children.only(children).type === "string" ? React__default["default"].Children.only(children) : /*#__PURE__*/React__default["default"].cloneElement(React__default["default"].Children.only(children), { |
| 205 | + matches: matches |
| 206 | + }) : null : null : null; |
| 207 | +}; |
| 208 | +var queryType = PropTypes__default["default"].oneOfType([PropTypes__default["default"].string, PropTypes__default["default"].object, PropTypes__default["default"].arrayOf(PropTypes__default["default"].object.isRequired)]); |
| 209 | +Media.propTypes = { |
| 210 | + defaultMatches: PropTypes__default["default"].oneOfType([PropTypes__default["default"].bool, PropTypes__default["default"].objectOf(PropTypes__default["default"].bool)]), |
| 211 | + query: queryType, |
| 212 | + queries: PropTypes__default["default"].objectOf(queryType), |
| 213 | + render: PropTypes__default["default"].func, |
| 214 | + children: PropTypes__default["default"].oneOfType([PropTypes__default["default"].node, PropTypes__default["default"].func]), |
| 215 | + targetWindow: PropTypes__default["default"].object, |
| 216 | + onChange: PropTypes__default["default"].func |
| 217 | +}; |
| 218 | + |
| 219 | +exports["default"] = Media; |
| 220 | +exports.useMedia = useMedia; |
0 commit comments