UNPKG

react-cm-ui

Version:
354 lines (337 loc) 9.98 kB
import { camelCase, forEach, isNumber, } from 'lodash'; import ClassNames from 'classnames'; import PropTypes from 'prop-types'; import React from 'react'; import { BEM_GRID, BEM_GRID_COLUMN, BEM_GRID_ROW, UI_CLASS_NAME, } from '../../global/constants'; import { SPACINGS, } from './gridConstants'; import GridColumn from './gridColumn'; import GridRowDeprecated from './gridRowDeprecated'; import makeStyles from '../../styles/makeStyles'; const propTypes = { /** * Defines the `align-content` style property. * It's applied for all screen sizes. */ alignContent: PropTypes.oneOf([ 'center', 'flex-end', 'flex-start', 'space-around', 'space-between', 'stretch', ]), /** * Defines the `align-items` style property. * It's applied for all screen sizes. */ alignItems: PropTypes.oneOf([ 'baseline', 'center', 'flex-end', 'flex-start', 'stretch', ]), /** * The content of the Grid */ children: PropTypes.node, /** * Override or extend the styles applied to the component. */ classes: PropTypes.shape({}), /** * Assign additional class names to Grid. */ className: PropTypes.string, /** * Used for DOM testing. https://testing-library.com/docs/queries/bytestid/ */ dataTestId: PropTypes.string, /** * Defines the `flex-direction` style property. * It is applied for all screen sizes. */ direction: PropTypes.oneOf([ 'column-reverse', 'column', 'row-reverse', 'row', ]), /** * The `id` of the Grid. */ id: PropTypes.string, /** * Defines the `justify-content` style property. * It is applied for all screen sizes. */ justifyContent: PropTypes.oneOf([ 'center', 'flex-end', 'flex-start', 'space-around', 'space-between', 'space-evenly', ]), /** * Defines the space between the type `item` component. * It can only be used on a type `container` component. */ spacing: PropTypes.oneOf(SPACINGS), /** * Override styles applied to the component. */ style: PropTypes.shape({}), /** * Defines the `flex-wrap` style property. * It's applied for all screen sizes. */ wrap: PropTypes.oneOf([ 'nowrap', 'wrap-reverse', 'wrap', ]), // **** // NOTE: All props below are deprecated and should not be used. // **** /** * Deprecated prop. Please use breakpoint props on the Grid.Column */ columns: PropTypes.oneOf([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]), /** * Deprecated prop. Please use `classes` to override styles. */ textAlign: PropTypes.oneOf(['center', 'left', 'right']), /** * Deprecated prop. Please use `classes` to override styles. */ verticalAlign: PropTypes.oneOf(['bottom', 'middle', 'top']), }; const defaultProps = { alignContent: 'stretch', alignItems: 'stretch', children: null, classes: null, className: null, dataTestId: `${UI_CLASS_NAME}-${BEM_GRID}`, direction: 'row', id: null, justifyContent: 'flex-start', spacing: 0, style: null, wrap: 'wrap', // **** // NOTE: All props below are deprecated and should not be used. // **** columns: undefined, textAlign: undefined, verticalAlign: undefined, }; const useStyles = makeStyles(({ spacing }) => { function getOffset(val, div = 1) { const parse = parseFloat(val); return `${parse / div}${String(val).replace(String(parse), '') || 'px'}`; } const gutters = {}; // eslint-disable-next-line consistent-return forEach(SPACINGS, (value) => { const themeSpacing = spacing(value); if (themeSpacing === 0) { return null; } gutters[`spacing-${value}`] = { margin: `-${getOffset(themeSpacing, 2)}`, width: `calc(100% + ${getOffset(themeSpacing)})`, [`& > .${BEM_GRID_COLUMN}`]: { padding: getOffset(themeSpacing, 2), }, /** * Note: Remove once Grid.RowDeprecated has been removed. */ [`& > .${BEM_GRID_ROW} > .${BEM_GRID_COLUMN}`]: { padding: getOffset(themeSpacing, 2), }, }; }); return { root: { display: 'flex', flexWrap: 'wrap', width: '100%', /** * Deprecated classses */ '&$deprecatedTextAlignCenter': { textAlign: 'center', }, '&$deprecatedTextAlignLeft': { textAlign: 'left', }, '&$deprecatedTextAlignRight': { textAlign: 'right', }, '&$deprecatedVerticalAlignBottom': { alignItems: 'flex-end', }, '&$deprecatedVerticalAlignMiddle': { alignItems: 'center', }, '&$deprecatedVerticalAlignTop': { alignItems: 'flex-start', }, /** * End of deprecated classes */ }, 'alignContent-center': { alignContent: 'center', }, 'alignContent-flexStart': { alignContent: 'flex-start', }, 'alignContent-flexEnd': { alignContent: 'flex-end', }, 'alignContent-spaceBetween': { alignContent: 'space-between', }, 'alignContent-spaceAround': { alignContent: 'space-around', }, 'alignItems-center': { alignItems: 'center', }, 'alignItems-flexStart': { alignItems: 'flex-start', }, 'alignItems-flexEnd': { alignItems: 'flex-end', }, 'alignItems-baseline': { alignItems: 'baseline', }, /** * Deprecated classses */ deprecatedColumns: { [`& > .${BEM_GRID_COLUMN}, & > .${BEM_GRID_ROW} > .${BEM_GRID_COLUMN}`]: { width: ({ columns }) => `${(1 / columns) * 100}%`, }, }, deprecatedTextAlignCenter: {}, deprecatedTextAlignLeft: {}, deprecatedTextAlignRight: {}, deprecatedVerticalAlignBottom: {}, deprecatedVerticalAlignMiddle: {}, deprecatedVerticalAlignTop: {}, /** * End of deprecated classses */ 'direction-column': { flexDirection: 'column', }, 'direction-columnReverse': { flexDirection: 'column-reverse', }, 'direction-rowReverse': { flexDirection: 'row-reverse', }, 'justifyContent-center': { justifyContent: 'center', }, 'justifyContent-flexEnd': { justifyContent: 'flex-end', }, 'justifyContent-spaceBetween': { justifyContent: 'space-between', }, 'justifyContent-spaceAround': { justifyContent: 'space-around', }, 'justifyContent-spaceEvenly': { justifyContent: 'space-evenly', }, 'wrap-nowrap': { flexWrap: 'nowrap', }, 'wrap-wrapReverse': { flexWrap: 'wrap-reverse', }, ...gutters, }; }); /** * The Grid layout responsivly adapts to screen size, aiding in dividing * up content into their own regions. */ const Grid = React.forwardRef( // eslint-disable-next-line prefer-arrow-callback function Grid(props, ref) { const { alignContent, alignItems, children, className, dataTestId, id, direction, justifyContent, spacing, style, wrap, ...otherProps } = props; const classes = useStyles(props); const rootClasses = ClassNames( UI_CLASS_NAME, BEM_GRID, classes.root, className, { [classes[`alignContent-${camelCase(alignContent)}`]]: alignContent !== 'stretch', [classes[`alignItems-${camelCase(alignItems)}`]]: alignItems !== 'stretch', [classes[`direction-${camelCase(direction)}`]]: direction !== 'row', [classes[`justifyContent-${camelCase(justifyContent)}`]]: justifyContent !== 'flex-start', [classes[`spacing-${String(spacing)}`]]: spacing !== 0, [classes[`wrap-${camelCase(wrap)}`]]: wrap !== 'wrap', /** * Deprecated classses */ [classes.deprecatedColumns]: isNumber(otherProps.columns), [classes.deprecatedTextAlignCenter]: otherProps.textAlign === 'center', [classes.deprecatedTextAlignLeft]: otherProps.textAlign === 'left', [classes.deprecatedTextAlignRight]: otherProps.textAlign === 'right', [classes.deprecatedVerticalAlignBottom]: otherProps.verticalAlign === 'bottom', [classes.deprecatedVerticalAlignMiddle]: otherProps.verticalAlign === 'middle', [classes.deprecatedVerticalAlignTop]: otherProps.verticalAlign === 'top', }, ); return ( <div className={rootClasses} data-testid={dataTestId} id={id} ref={ref} style={style} > {children} </div> ); }, ); Grid.Column = GridColumn; Grid.RowDeprecated = GridRowDeprecated; Grid.propTypes = propTypes; Grid.defaultProps = defaultProps; export default Grid;