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; }; function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } // A grid component using the following libs as inspiration. // // For the implementation: // - http://v4-alpha.getbootstrap.com/layout/flexbox-grid/ // - https://github.com/kristoferjoseph/flexboxgrid/blob/master/src/css/flexboxgrid.css // - https://github.com/roylee0704/react-flexbox-grid // - https://material.angularjs.org/latest/layout/introduction // // Follow this flexbox Guide to better understand the underlying model: // - https://css-tricks.com/snippets/css/a-guide-to-flexbox/ import React from 'react'; import classNames from 'classnames'; import withStyles from '../styles/withStyles'; import { keys as breakpointKeys } from '../styles/createBreakpoints'; import requirePropFactory from '../utils/requirePropFactory'; import Hidden from '../Hidden'; const GUTTERS = [0, 8, 16, 24, 40]; const GRID_SIZES = [true, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; function generateGrid(globalStyles, theme, breakpoint) { // For the auto layouting const styles = { [`grid-${breakpoint}`]: { flexBasis: 0, flexGrow: 1, maxWidth: '100%' } }; GRID_SIZES.forEach(size => { if (typeof size === 'boolean') { // Skip the first one as handle above. return; } // Only keep 6 significant numbers. const width = `${Math.round(size / 12 * Math.pow(10, 6)) / Math.pow(10, 4)}%`; /* eslint-disable max-len */ // Close to the bootstrap implementation: // https://github.com/twbs/bootstrap/blob/b0508a975d711d6b24c01f57dd5445c22699fac4/scss/mixins/_grid.scss#L69 /* eslint-enable max-len */ styles[`grid-${breakpoint}-${size}`] = { flexBasis: width, maxWidth: width }; }); // No need for a media query for the first size. if (breakpoint === 'xs') { _extends(globalStyles, styles); } else { globalStyles[theme.breakpoints.up(breakpoint)] = styles; } } function generateGutter(theme, breakpoint) { const styles = {}; GUTTERS.forEach((spacing, index) => { if (index === 0) { // Skip the default style. return; } styles[`spacing-${breakpoint}-${spacing}`] = { margin: -spacing / 2, width: `calc(100% + ${spacing}px)`, '& > $typeItem': { padding: spacing / 2 } }; }); return styles; } // Default CSS values // flex: '0 1 auto', // flexDirection: 'row', // alignItems: 'flex-start', // flexWrap: 'nowrap', // justifyContent: 'flex-start', export const styles = theme => _extends({ typeContainer: { boxSizing: 'border-box', display: 'flex', flexWrap: 'wrap', width: '100%' }, typeItem: { boxSizing: 'border-box', flex: '0 0 auto', margin: '0' // For instance, it's useful when used with a `figure` element. }, 'direction-xs-column': { flexDirection: 'column' }, 'direction-xs-column-reverse': { flexDirection: 'column-reverse' }, 'direction-xs-row-reverse': { flexDirection: 'row-reverse' }, 'wrap-xs-nowrap': { flexWrap: 'nowrap' }, 'align-items-xs-center': { alignItems: 'center' }, 'align-items-xs-flex-start': { alignItems: 'flex-start' }, 'align-items-xs-flex-end': { alignItems: 'flex-end' }, 'align-items-xs-baseline': { alignItems: 'baseline' }, 'align-content-xs-center': { alignContent: 'center' }, 'align-content-xs-flex-start': { alignContent: 'flex-start' }, 'align-content-xs-flex-end': { alignContent: 'flex-end' }, 'align-content-xs-space-between': { alignContent: 'space-between' }, 'align-content-xs-space-around': { alignContent: 'space-around' }, 'justify-xs-center': { justifyContent: 'center' }, 'justify-xs-flex-end': { justifyContent: 'flex-end' }, 'justify-xs-space-between': { justifyContent: 'space-between' }, 'justify-xs-space-around': { justifyContent: 'space-around' } }, generateGutter(theme, 'xs'), breakpointKeys.reduce((accumulator, key) => { // Use side effect over immutability for better performance. generateGrid(accumulator, theme, key); return accumulator; }, {})); function Grid(props) { const { classes, className: classNameProp, component: ComponentProp, container, item, alignContent, alignItems, direction, spacing, hidden, justify, wrap, xs, sm, md, lg, xl } = props, other = _objectWithoutProperties(props, ['classes', 'className', 'component', 'container', 'item', 'alignContent', 'alignItems', 'direction', 'spacing', 'hidden', 'justify', 'wrap', 'xs', 'sm', 'md', 'lg', 'xl']); const className = classNames({ [classes.typeContainer]: container, [classes.typeItem]: item, [classes[`spacing-xs-${String(spacing)}`]]: container && spacing !== 0, [classes[`direction-xs-${String(direction)}`]]: direction !== Grid.defaultProps.direction, [classes[`wrap-xs-${String(wrap)}`]]: wrap !== Grid.defaultProps.wrap, [classes[`align-items-xs-${String(alignItems)}`]]: alignItems !== Grid.defaultProps.alignItems, [classes[`align-content-xs-${String(alignContent)}`]]: alignContent !== Grid.defaultProps.alignContent, [classes[`justify-xs-${String(justify)}`]]: justify !== Grid.defaultProps.justify, [classes['grid-xs']]: xs === true, [classes[`grid-xs-${String(xs)}`]]: xs && xs !== true, [classes['grid-sm']]: sm === true, [classes[`grid-sm-${String(sm)}`]]: sm && sm !== true, [classes['grid-md']]: md === true, [classes[`grid-md-${String(md)}`]]: md && md !== true, [classes['grid-lg']]: lg === true, [classes[`grid-lg-${String(lg)}`]]: lg && lg !== true, [classes['grid-xl']]: xl === true, [classes[`grid-xl-${String(xl)}`]]: xl && xl !== true }, classNameProp); const gridProps = _extends({ className }, other); if (hidden) { return React.createElement( Hidden, hidden, React.createElement(ComponentProp, gridProps) ); } return React.createElement(ComponentProp, gridProps); } Grid.defaultProps = { alignContent: 'stretch', alignItems: 'stretch', component: 'div', container: false, direction: 'row', hidden: undefined, item: false, justify: 'flex-start', spacing: 16, wrap: 'wrap' }; // Add a wrapper component to generate some helper messages in the development // environment. // eslint-disable-next-line import/no-mutable-exports let GridWrapper = Grid; if (process.env.NODE_ENV !== 'production') { const requireProp = requirePropFactory('Grid'); GridWrapper = props => React.createElement(Grid, props); // $FlowFixMe - cannot mix legacy propTypes with current HOC pattern - https://github.com/facebook/flow/issues/4644#issuecomment-332530909 GridWrapper.propTypes = { alignContent: requireProp('container'), alignItems: requireProp('container'), direction: requireProp('container'), justify: requireProp('container'), lg: requireProp('item'), md: requireProp('item'), sm: requireProp('item'), spacing: requireProp('container'), wrap: requireProp('container'), xs: requireProp('item') }; } export default withStyles(styles, { name: 'MuiGrid' })(GridWrapper);