UNPKG

@artsy/fresnel

Version:

An SSR compatible approach to CSS media query based responsive layouts for React.

353 lines (286 loc) 16.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Breakpoints = exports.BreakpointConstraint = void 0; var _Utils = require("./Utils"); function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? Object(arguments[i]) : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys.push.apply(ownKeys, Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a 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); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 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; } function breakpointKey(breakpoint) { return Array.isArray(breakpoint) ? breakpoint.join("-") : breakpoint; } var BreakpointConstraint; /** * Encapsulates all breakpoint data needed by the Media component. The data is * generated on initialization so no further runtime work is necessary. */ exports.BreakpointConstraint = BreakpointConstraint; (function (BreakpointConstraint) { BreakpointConstraint["at"] = "at"; BreakpointConstraint["lessThan"] = "lessThan"; BreakpointConstraint["greaterThan"] = "greaterThan"; BreakpointConstraint["greaterThanOrEqual"] = "greaterThanOrEqual"; BreakpointConstraint["between"] = "between"; })(BreakpointConstraint || (exports.BreakpointConstraint = BreakpointConstraint = {})); var Breakpoints = /*#__PURE__*/function () { _createClass(Breakpoints, null, [{ key: "validKeys", value: function validKeys() { return [BreakpointConstraint.at, BreakpointConstraint.lessThan, BreakpointConstraint.greaterThan, BreakpointConstraint.greaterThanOrEqual, BreakpointConstraint.between]; } }]); function Breakpoints(_breakpoints) { var _this = this, _this$_mediaQueries; _classCallCheck(this, Breakpoints); _defineProperty(this, "_sortedBreakpoints", void 0); _defineProperty(this, "_breakpoints", void 0); _defineProperty(this, "_mediaQueries", void 0); _defineProperty(this, "findBreakpointsForWidths", function (fromWidth, throughWidth) { var fromBreakpoint = _this.findBreakpointAtWidth(fromWidth); if (!fromBreakpoint) { return undefined; } var throughBreakpoint = _this.findBreakpointAtWidth(throughWidth); if (!throughBreakpoint || fromBreakpoint === throughBreakpoint) { return [fromBreakpoint]; } else { return _this._sortedBreakpoints.slice(_this._sortedBreakpoints.indexOf(fromBreakpoint), _this._sortedBreakpoints.indexOf(throughBreakpoint) + 1); } }); _defineProperty(this, "findBreakpointAtWidth", function (width) { return _this._sortedBreakpoints.find(function (breakpoint, i) { var nextBreakpoint = _this._sortedBreakpoints[i + 1]; if (nextBreakpoint) { return width >= _this._breakpoints[breakpoint] && width < _this._breakpoints[nextBreakpoint]; } else { return width >= _this._breakpoints[breakpoint]; } }); }); _defineProperty(this, "valuesWithBreakpointProps", function (values) { var max = values.length; var valueBreakpoints = []; var lastTuple; _this._sortedBreakpoints.forEach(function (breakpoint, i) { var value = values[i]; if (i < max && (!lastTuple || lastTuple[0] !== value)) { lastTuple = [value, [breakpoint]]; valueBreakpoints.push(lastTuple); } else { lastTuple[1].push(breakpoint); } }); return valueBreakpoints.map(function (_ref, i) { var _ref2 = _slicedToArray(_ref, 2), value = _ref2[0], breakpoints = _ref2[1]; var props = {}; if (i === valueBreakpoints.length - 1) { props.greaterThanOrEqual = breakpoints[0]; } else if (breakpoints.length === 1) { props.at = breakpoints[0]; } else { // TODO: This is less than ideal, would be good to have a `through` // prop, which unlike `between` is inclusive. props.between = [breakpoints[0], valueBreakpoints[i + 1][1][0]]; } return [value, props]; }); }); this._breakpoints = _breakpoints; this._sortedBreakpoints = Object.keys(_breakpoints).map(function (breakpoint) { return [breakpoint, _breakpoints[breakpoint]]; }).sort(function (a, b) { return a[1] < b[1] ? -1 : 1; }).map(function (breakpointAndValue) { return breakpointAndValue[0]; }); // List of all possible and valid `between` combinations var betweenCombinations = this._sortedBreakpoints.slice(0, -1).reduce(function (acc, b1, i) { return _toConsumableArray(acc).concat(_toConsumableArray(_this._sortedBreakpoints.slice(i + 1).map(function (b2) { return [b1, b2]; }))); }, []); this._mediaQueries = (_this$_mediaQueries = {}, _defineProperty(_this$_mediaQueries, BreakpointConstraint.at, this._createBreakpointQueries(BreakpointConstraint.at, this._sortedBreakpoints)), _defineProperty(_this$_mediaQueries, BreakpointConstraint.lessThan, this._createBreakpointQueries(BreakpointConstraint.lessThan, this._sortedBreakpoints.slice(1))), _defineProperty(_this$_mediaQueries, BreakpointConstraint.greaterThan, this._createBreakpointQueries(BreakpointConstraint.greaterThan, this._sortedBreakpoints.slice(0, -1))), _defineProperty(_this$_mediaQueries, BreakpointConstraint.greaterThanOrEqual, this._createBreakpointQueries(BreakpointConstraint.greaterThanOrEqual, this._sortedBreakpoints)), _defineProperty(_this$_mediaQueries, BreakpointConstraint.between, this._createBreakpointQueries(BreakpointConstraint.between, betweenCombinations)), _this$_mediaQueries); } _createClass(Breakpoints, [{ key: "toVisibleAtBreakpointSet", value: function toVisibleAtBreakpointSet(breakpointProps) { breakpointProps = this._normalizeProps(breakpointProps); if (breakpointProps.lessThan) { var breakpointIndex = this.sortedBreakpoints.findIndex(function (bp) { return bp === breakpointProps.lessThan; }); return this.sortedBreakpoints.slice(0, breakpointIndex); } else if (breakpointProps.greaterThan) { var _breakpointIndex = this.sortedBreakpoints.findIndex(function (bp) { return bp === breakpointProps.greaterThan; }); return this.sortedBreakpoints.slice(_breakpointIndex + 1); } else if (breakpointProps.greaterThanOrEqual) { var _breakpointIndex2 = this.sortedBreakpoints.findIndex(function (bp) { return bp === breakpointProps.greaterThanOrEqual; }); return this.sortedBreakpoints.slice(_breakpointIndex2); } else if (breakpointProps.between) { var between = breakpointProps.between; var fromBreakpointIndex = this.sortedBreakpoints.findIndex(function (bp) { return bp === between[0]; }); var toBreakpointIndex = this.sortedBreakpoints.findIndex(function (bp) { return bp === between[1]; }); return this.sortedBreakpoints.slice(fromBreakpointIndex, toBreakpointIndex); } return []; } }, { key: "toRuleSets", value: function toRuleSets() { var _this2 = this; var keys = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : Breakpoints.validKeys(); var selectedMediaQueries = keys.reduce(function (mediaQueries, query) { mediaQueries[query] = _this2._mediaQueries[query]; return mediaQueries; }, {}); return Object.entries(selectedMediaQueries).reduce(function (acc, _ref3) { var _ref4 = _slicedToArray(_ref3, 2), type = _ref4[0], queries = _ref4[1]; queries.forEach(function (query, breakpoint) { // We need to invert the query, such that it matches when we want the // element to be hidden. acc.push((0, _Utils.createRuleSet)((0, _Utils.createClassName)(type, breakpoint), "not all and ".concat(query))); }); return acc; }, []); } }, { key: "shouldRenderMediaQuery", value: function shouldRenderMediaQuery(breakpointProps, onlyRenderAt) { var _this3 = this; breakpointProps = this._normalizeProps(breakpointProps); if (breakpointProps.lessThan) { var width = this._breakpoints[breakpointProps.lessThan]; var lowestAllowedWidth = Math.min.apply(Math, _toConsumableArray(onlyRenderAt.map(function (breakpoint) { return _this3._breakpoints[breakpoint]; }))); return lowestAllowedWidth < width; } else if (breakpointProps.greaterThan) { var _width = this._breakpoints[this._findNextBreakpoint(breakpointProps.greaterThan)]; var highestAllowedWidth = Math.max.apply(Math, _toConsumableArray(onlyRenderAt.map(function (breakpoint) { return _this3._breakpoints[breakpoint]; }))); return highestAllowedWidth >= _width; } else if (breakpointProps.greaterThanOrEqual) { var _width2 = this._breakpoints[breakpointProps.greaterThanOrEqual]; var _highestAllowedWidth = Math.max.apply(Math, _toConsumableArray(onlyRenderAt.map(function (breakpoint) { return _this3._breakpoints[breakpoint]; }))); return _highestAllowedWidth >= _width2; } else if (breakpointProps.between) { // TODO: This is the only useful breakpoint to negate, but we’ll // we’ll see when/if we need it. We could then also decide // to add `oustide`. var fromWidth = this._breakpoints[breakpointProps.between[0]]; var toWidth = this._breakpoints[breakpointProps.between[1]]; var allowedWidths = onlyRenderAt.map(function (breakpoint) { return _this3._breakpoints[breakpoint]; }); return !(Math.max.apply(Math, _toConsumableArray(allowedWidths)) < fromWidth || Math.min.apply(Math, _toConsumableArray(allowedWidths)) >= toWidth); } return false; } }, { key: "_normalizeProps", value: function _normalizeProps(breakpointProps) { if (breakpointProps.at) { var fromIndex = this._sortedBreakpoints.indexOf(breakpointProps.at); var to = this._sortedBreakpoints[fromIndex + 1]; return to ? { between: [breakpointProps.at, to] } : { greaterThanOrEqual: breakpointProps.at }; } return breakpointProps; } }, { key: "_createBreakpointQuery", value: function _createBreakpointQuery(breakpointProps) { breakpointProps = this._normalizeProps(breakpointProps); if (breakpointProps.lessThan) { var width = this._breakpoints[breakpointProps.lessThan]; return "(max-width:".concat(width - 0.02, "px)"); } else if (breakpointProps.greaterThan) { var _width3 = this._breakpoints[this._findNextBreakpoint(breakpointProps.greaterThan)]; return "(min-width:".concat(_width3, "px)"); } else if (breakpointProps.greaterThanOrEqual) { var _width4 = this._breakpoints[breakpointProps.greaterThanOrEqual]; return "(min-width:".concat(_width4, "px)"); } else if (breakpointProps.between) { // TODO: This is the only useful breakpoint to negate, but we’ll // we’ll see when/if we need it. We could then also decide // to add `outside`. var fromWidth = this._breakpoints[breakpointProps.between[0]]; var toWidth = this._breakpoints[breakpointProps.between[1]]; return "(min-width:".concat(fromWidth, "px) and (max-width:").concat(toWidth - 0.02, "px)"); } throw new Error("Unexpected breakpoint props: ".concat(JSON.stringify(breakpointProps))); } }, { key: "_createBreakpointQueries", value: function _createBreakpointQueries(key, forBreakpoints) { var _this4 = this; return forBreakpoints.reduce(function (map, breakpoint) { map.set(breakpointKey(breakpoint), _this4._createBreakpointQuery(_defineProperty({}, key, breakpoint))); return map; }, new Map()); } }, { key: "_findNextBreakpoint", value: function _findNextBreakpoint(breakpoint) { var nextBreakpoint = this._sortedBreakpoints[this._sortedBreakpoints.indexOf(breakpoint) + 1]; if (!nextBreakpoint) { throw new Error("There is no breakpoint larger than ".concat(breakpoint)); } return nextBreakpoint; } }, { key: "sortedBreakpoints", get: function get() { return this._sortedBreakpoints; } }, { key: "dynamicResponsiveMediaQueries", get: function get() { return Array.from(this._mediaQueries[BreakpointConstraint.at].entries()).reduce(function (acc, _ref5) { var _ref6 = _slicedToArray(_ref5, 2), k = _ref6[0], v = _ref6[1]; return _objectSpread({}, acc, _defineProperty({}, k, v)); }, {}); } }, { key: "largestBreakpoint", get: function get() { return this._sortedBreakpoints[this._sortedBreakpoints.length - 1]; } }]); return Breakpoints; }(); exports.Breakpoints = Breakpoints; //# sourceMappingURL=Breakpoints.js.map