react-cm-ui
Version:
React UI for Healthy Church
354 lines (337 loc) • 9.98 kB
JSX
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;