UNPKG

@retailmenot/anchor

Version:

A React UI Library by RetailMeNot

547 lines (469 loc) 18.1 kB
import { e as _typeof, j as _defineProperty, f as _classCallCheck, g as _createClass, k as _assertThisInitialized, h as _inherits, i as _createSuper, a as _taggedTemplateLiteral } from './anchor-chunk-7b9d8557.js'; import { a as __rest } from './anchor-chunk-27f34e54.js'; import { RootTheme } from './theme.js'; import { debounce } from 'ts-debounce'; import Responsive from 'react-responsive'; import { createContext, forwardRef, createElement, useContext, useState, Children, Fragment, useEffect, useRef, createRef, Component, cloneElement, useReducer, PureComponent, isValidElement, useImperativeHandle } from 'react'; import classNames from 'classnames'; import styled, { withTheme, css } from '@xstyled/styled-components'; import { breakpoints, space } from '@xstyled/system'; import './anchor-chunk-210b63ef.js'; import './skeleton.js'; import 'polished'; import './anchor-chunk-e1ca097b.js'; import './anchor-chunk-cd7ef49a.js'; import './anchor-chunk-20e4020f.js'; var GridContext = createContext({ debug: false }); var FLOW; (function (FLOW) { FLOW["column"] = "column"; FLOW["row"] = "row"; })(FLOW || (FLOW = {})); var debugColor = 'rgba(255, 0, 0, 0.4)'; /* Returns an array of objects sorted by their value, descending. Ex: const obj: { xs: 500: md: 800: sm: 650 }; sortBreakpoints(obj); // [{xs: 500}, {sm: 650}, {md: 800}] */ function sortBreakpoints(unsortedBreakpoints) { return Object.keys(unsortedBreakpoints).reduce(function (acc, next) { acc.push(_defineProperty({}, next, unsortedBreakpoints[next])); return acc; }, []).sort(function (a, b) { return Object.values(a)[0] - Object.values(b)[0]; }); } /* Returns the breakpoint key for the specified window's innerWidth. Ex: const innerWidth = 920; const sortedBreakpoints = [{ xs: 500, sm: 750, md: 1000 }]; getBreakpointKey(innerWidth, sortedBreakpoints); // sm */ function getBreakpointKey(innerWidth, sortedBreakpoints) { var breakpoint = sortedBreakpoints // Get only breakpoints that are above the window's width .filter(function (bp) { return innerWidth >= Object.values(bp)[0]; }) // Sort those breakpoints, putting the largest breakpoint first .sort(function (a, b) { return Object.values(b)[0] - Object.values(a)[0]; }) // Get the largest breakpoint from the array .shift(); // Gets the key based on the breakpoint (i.e. xs, sm, etc) return _typeof(breakpoint) === 'object' ? Object.keys(breakpoint)[0] : ''; } /* Small helper object for the generateBreakpointCSS() function to render the correct CSS for each grid setting. */ var ops = { // If width is 0, don't show the cell. If middle is true, add in the middle CSS. width: function width(_width, middle) { return _width > 0 ? "grid-column-end: span ".concat(_width, "; display: block;") : 'display: none;'; }, height: function height(_height) { return "grid-row-end: span ".concat(_height, ";"); }, left: function left(_left) { return "grid-column-start: ".concat(_left, ";"); }, top: function top(_top) { return "grid-row-start: ".concat(_top, ";"); } }; /* Takes the gridSettings object and parses the data to generate sorted css breakpoints. It groups css properties based on the breakpoint size so that a single breakpoint declaration for 'xs' can have css for left, top, height & width if necessary. NOTE: This function uses older loop structures as they are tremendously more performant. */ function generateBreakpointCSS(gridSettings, sortedBreakpoints) { var responsiveCSS = {}; var sortedResponsiveCSS = []; var generalSettings = {}; for (var gridSettingKey in gridSettings) { // forin requires an 'if' guard if (gridSettings[gridSettingKey]) { var gridSettingValue = gridSettings[gridSettingKey]; // Put non-responsive settings into the generalSettings object if (typeof gridSettingValue === 'number' || typeof gridSettingValue === 'undefined') { generalSettings[gridSettingKey] = gridSettingValue; } else if (_typeof(gridSettingValue) === 'object') { for (var breakpointKey in gridSettingValue) { if (gridSettingValue[breakpointKey] >= 0) { // 0 is a valid value, no truthiness var responsiveValue = gridSettingValue[breakpointKey]; if (!responsiveCSS[breakpointKey]) { responsiveCSS[breakpointKey] = ''; } responsiveCSS[breakpointKey] += ops[gridSettingKey](responsiveValue); } } } } } // eslint-disable-next-line: prefer-for-of for (var i = 0; i < sortedBreakpoints.length; i++) { for (var _breakpointKey in sortedBreakpoints[i]) { if (responsiveCSS[_breakpointKey] !== undefined) { sortedResponsiveCSS.push(_defineProperty({}, _breakpointKey, responsiveCSS[_breakpointKey])); } } } /* sortedResponsiveCSS is an array of breakpoint css associated to a breakpoint key, i.e. [ {xs: 'grid-column-end: span 2; display: block; grid-row-end: span 3;'}, {md: 'grid-column-end: span 10;'} ] generalSettings is the returned values which were not responsive objects, i.e. { left: 1, top: undefined, height: 1, width: 3 } */ return { sortedResponsiveCSS: sortedResponsiveCSS, generalSettings: generalSettings }; } function _templateObject() { var data = _taggedTemplateLiteral(["\n padding: 1rem 2rem;\n z-index: 1000000;\n background: black;\n color: white;\n position: fixed;\n bottom: 1rem;\n right: 1rem;\n font-family: mono;\n"]); _templateObject = function _templateObject() { return data; }; return data; } var StyledDebug = styled('div')(_templateObject()); StyledDebug.displayName = 'StyledDebug'; var ResponsiveContext = createContext({ breakpoints: [], current: '', innerWidth: 0 }); var BasicResponsiveProvider = /*#__PURE__*/function (_React$PureComponent) { _inherits(BasicResponsiveProvider, _React$PureComponent); var _super = _createSuper(BasicResponsiveProvider); function BasicResponsiveProvider(props) { var _this; _classCallCheck(this, BasicResponsiveProvider); _this = _super.call(this, props); var hasWindow = props.windowDep === false ? false : typeof window !== 'undefined'; var breakpoints$$1; // Priority is props > ThemeProvider > RootTheme if (props.breakpoints !== undefined) { breakpoints$$1 = props.breakpoints; } else if (props.theme !== undefined) { breakpoints$$1 = props.theme.breakpoints; } else { breakpoints$$1 = RootTheme.breakpoints; } var sortedBreakpoints = sortBreakpoints(breakpoints$$1); var innerWidth = hasWindow ? window.innerWidth : 0; _this.state = { breakpoints: sortedBreakpoints, current: getBreakpointKey(innerWidth, sortedBreakpoints), innerWidth: innerWidth }; _this.handleResize = debounce(_this.handleResize.bind(_assertThisInitialized(_this)), 100); _this.hasWindow = hasWindow; return _this; } _createClass(BasicResponsiveProvider, [{ key: "handleResize", value: function handleResize() { if (this.hasWindow) { this.setState({ current: getBreakpointKey(window.innerWidth, this.state.breakpoints), innerWidth: window.innerWidth }); } } }, { key: "componentDidMount", value: function componentDidMount() { if (this.hasWindow) { window.addEventListener('resize', this.handleResize); } } }, { key: "componentWillUnmount", value: function componentWillUnmount() { if (this.hasWindow) { window.removeEventListener('resize', this.handleResize); } } }, { key: "render", value: function render() { var _this$state = this.state, breakpoints$$1 = _this$state.breakpoints, current = _this$state.current, innerWidth = _this$state.innerWidth; var debug = this.props.debug; return createElement(ResponsiveContext.Provider, { value: { breakpoints: breakpoints$$1, current: current, innerWidth: innerWidth } }, debug && createElement(StyledDebug, null, current, " | w:", innerWidth, "px"), this.props.children); } }]); return BasicResponsiveProvider; }(PureComponent); var ResponsiveProvider = withTheme(BasicResponsiveProvider); function _templateObject2() { var data = _taggedTemplateLiteral(["\n box-sizing: border-box;\n\n ", "\n\n ", "\n"]); _templateObject2 = function _templateObject2() { return data; }; return data; } function _templateObject$1() { var data = _taggedTemplateLiteral(["\n height: 100%;\n min-width: 0;\n\n // The order in which the media queries are generated is very important, hence breakpoints() is\n // called multiple times as opposed to just once with a single object.\n ", "\n\n ", ";\n ", ";\n ", ";\n ", ";\n\n ", ";\n ", ";\n"]); _templateObject$1 = function _templateObject() { return data; }; return data; } var StyledCell = styled.div(_templateObject$1(), function (_ref) { var responsiveCSS = _ref.responsiveCSS; return responsiveCSS && responsiveCSS.map(function (k) { return breakpoints(k); }); }, function (_ref2) { var left = _ref2.left; return left && "grid-column-start: ".concat(left); }, function (_ref3) { var width = _ref3.width; return width && "grid-column-end: span ".concat(width); }, function (_ref4) { var top = _ref4.top; return top && "grid-row-start: ".concat(top); }, function (_ref5) { var height = _ref5.height; return height && "grid-row-end: span ".concat(height); }, function (_ref6) { var area = _ref6.area; return area && "grid-area: ".concat(area); }, function (_ref7) { var debug = _ref7.debug; return debug && css({ backgroundColor: debugColor }); }); /* Used these flex styles rather than xstyled 'flexboxes' system prop to limit the props the user would have to use for the same effect. May rethink this if users ask for even more flex control. */ var Box = styled('div')(_templateObject2(), space, function (_ref8) { var align = _ref8.align, center = _ref8.center, middle = _ref8.middle, valign = _ref8.valign; var styles = {}; // This is to keep the API from breaking from v1.3.3 and below. // The middle and center props should be deprecated. var tmpValign = middle ? 'middle' : valign; var tmpAlign = center ? 'center' : align; if (tmpAlign || tmpValign) { styles.display = 'flex'; if (tmpValign) { styles.height = '100%'; } } switch (tmpAlign) { case 'left': styles.justifyContent = 'flex-start'; break; case 'center': styles.justifyContent = 'center'; break; case 'right': styles.justifyContent = 'flex-end'; break; } switch (tmpValign) { case 'top': styles.alignItems = 'flex-start'; break; case 'middle': styles.alignItems = 'center'; break; case 'bottom': styles.alignItems = 'flex-end'; break; } return css(styles); }); Box.displayName = 'Box'; var Cell = /*#__PURE__*/function (_React$PureComponent) { _inherits(Cell, _React$PureComponent); var _super = _createSuper(Cell); function Cell(props, context) { var _this; _classCallCheck(this, Cell); _this = _super.call(this, props, context); // Generates css data for xstyle'd media queries in the constructor so as to only // fire a single time and before render. Width and height have a default of 1. var _generateBreakpointCS = generateBreakpointCSS({ left: props.left, height: props.height || 1, top: props.top, width: props.width || 1 }, context.breakpoints), sortedResponsiveCSS = _generateBreakpointCS.sortedResponsiveCSS, generalSettings = _generateBreakpointCS.generalSettings; _this.state = { generalSettings: generalSettings, sortedResponsiveCSS: sortedResponsiveCSS }; return _this; } _createClass(Cell, [{ key: "render", value: function render() { var _this2 = this; var _this$props = this.props, align = _this$props.align, center = _this$props.center, children = _this$props.children, className = _this$props.className, debug = _this$props.debug, middle = _this$props.middle, valign = _this$props.valign; var _this$state = this.state, generalSettings = _this$state.generalSettings, sortedResponsiveCSS = _this$state.sortedResponsiveCSS; return createElement(GridContext.Consumer, null, function (_ref9) { var contextDebug = _ref9.debug; return createElement(StyledCell, { className: classNames('anchor-cell', className), debug: contextDebug || debug, responsiveCSS: sortedResponsiveCSS, left: generalSettings.left || undefined, height: generalSettings.height || undefined, top: generalSettings.top || undefined, width: generalSettings.width || undefined }, createElement(Box, Object.assign({}, _this2.props, { align: align, center: center, className: "anchor-cell-box", valign: valign, middle: middle }), children)); }); } }]); return Cell; }(PureComponent); Cell.contextType = ResponsiveContext; function _templateObject2$1() { var data = _taggedTemplateLiteral(["\n height: ", ";\n grid-auto-flow: ", ";\n grid-auto-rows: minmax(", ", auto);\n ", ";\n grid-template-columns: ", ";\n grid-gap: ", ";\n ", ";\n ", ";\n ", ";\n ", ";\n ", ";\n ", ";\n "]); _templateObject2$1 = function _templateObject2() { return data; }; return data; } function _templateObject$2() { var data = _taggedTemplateLiteral(["\n display: grid;\n ", "\n\n ", "\n"]); _templateObject$2 = function _templateObject() { return data; }; return data; } var frGetter = function frGetter(value) { return typeof value === 'number' ? "repeat(".concat(value, ", 1fr)") : value; }; var formatAreas = function formatAreas(areas) { return areas.map(function (area) { return "'".concat(area, "'"); }).join(' '); }; var StyledGrid = styled('div')(_templateObject$2(), function (_ref) { var alignContent = _ref.alignContent, areas = _ref.areas, columnGap = _ref.columnGap, _ref$columns = _ref.columns, columns = _ref$columns === void 0 ? 12 : _ref$columns, debug = _ref.debug, _ref$flow = _ref.flow, flow = _ref$flow === void 0 ? FLOW.row : _ref$flow, _ref$gap = _ref.gap, gap = _ref$gap === void 0 ? '0.5rem' : _ref$gap, _ref$height = _ref.height, height = _ref$height === void 0 ? 'auto' : _ref$height, justifyContent = _ref.justifyContent, _ref$minRowHeight = _ref.minRowHeight, minRowHeight = _ref$minRowHeight === void 0 ? '1.25rem' : _ref$minRowHeight, rowGap = _ref.rowGap, rows = _ref.rows; return css(_templateObject2$1(), height, flow, minRowHeight, rows && "grid-template-rows: ".concat(frGetter(rows)), frGetter(columns), gap, columnGap && "column-gap: ".concat(columnGap), rowGap && "row-gap: ".concat(rowGap), areas && "grid-template-areas: ".concat(formatAreas(areas)), justifyContent && "justify-content: ".concat(justifyContent), alignContent && "align-content: ".concat(alignContent), debug && "background-color: ".concat(debugColor)); }, space); var Grid = function Grid(_a) { var alignContent = _a.alignContent, areas = _a.areas, children = _a.children, className = _a.className, columns = _a.columns, _a$debug = _a.debug, debug = _a$debug === void 0 ? false : _a$debug, flow = _a.flow, gap = _a.gap, rowGap = _a.rowGap, columnGap = _a.columnGap, justifyContent = _a.justifyContent, rows = _a.rows, props = __rest(_a, ["alignContent", "areas", "children", "className", "columns", "debug", "flow", "gap", "rowGap", "columnGap", "justifyContent", "rows"]); return createElement(StyledGrid, Object.assign({ areas: areas, className: classNames('anchor-grid', className), columns: columns, debug: debug, flow: flow, gap: gap, rowGap: rowGap, columnGap: columnGap, justifyContent: justifyContent, rows: rows }, props), createElement(GridContext.Provider, { value: { debug: debug } }, children)); }; Grid.Cell = Cell; var Adaptor = function Adaptor(_a) { var from = _a.from, to = _a.to, props = __rest(_a, ["from", "to"]); var _React$useContext = useContext(ResponsiveContext), breakpoints$$1 = _React$useContext.breakpoints; var breakpointValues = { minWidth: undefined, maxWidth: undefined }; if ((from !== undefined || to !== undefined) && !breakpoints$$1.length) { /* eslint-disable-next-line */ console.warn("When using the 'from' and/or 'to' props, the ResponsiveProvider must also be used."); return null; } if (from === undefined && to !== undefined) { /* eslint-disable-next-line */ console.warn("Although the 'from' prop can be used by itself, the 'to' prop requires 'from' to also exist"); return null; } if (from) { var min = breakpoints$$1.find(function (bp) { return Object.keys(bp)[0] === from; }); breakpointValues.minWidth = _typeof(min) === 'object' ? Object.values(min)[0] : undefined; } if (from && to) { var max = breakpoints$$1.find(function (bp) { return Object.keys(bp)[0] === to; }); breakpointValues.maxWidth = _typeof(max) === 'object' ? Object.values(max)[0] : undefined; } return createElement(Responsive, Object.assign({ className: "anchor-adaptor" }, breakpointValues, props)); }; export { ResponsiveContext, ResponsiveProvider, Cell, Grid, Adaptor }; //# sourceMappingURL=grid.js.map