Skip to content
This repository has been archived by the owner on Jun 5, 2023. It is now read-only.

Commit

Permalink
feat(styleProps): Responsive spacing props
Browse files Browse the repository at this point in the history
The style props for margin and padding
are now responsive, i.e. they can be
specified as an array when a breakpoints prop is also present
  • Loading branch information
diondiondion committed Nov 20, 2019
1 parent 897cec2 commit 64f5f92
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 73 deletions.
2 changes: 1 addition & 1 deletion src/Box/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The following properties can be changed:
<Box border="bottom" pb="s">
Hello
</Box>
<Box bold mt="s" p="s">
<Box bold breakpoints="s" mt={['xs', 'l']} p="s">
Hello
</Box>
</Playground>
Expand Down
2 changes: 1 addition & 1 deletion src/styleProps/borderProps.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {checkTheme} from '../utils/theme';
import {checkTheme} from '../utils/styleProps';

import {borderValue} from '../mixins';

Expand Down
68 changes: 38 additions & 30 deletions src/styleProps/marginProps.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,42 @@
import {getSpacing} from '../utils/spacing';
import {checkTheme} from '../utils/theme';
import {createStyleFunction} from '../utils/styleProps';

function marginProps(props) {
const {m, mx, my, mt, mr, mb, ml, theme} = props;

checkTheme(theme);

return {
margin: m ? getSpacing(m, theme) : undefined,
marginTop: my
? getSpacing(my, theme)
: mt
? getSpacing(mt, theme)
: undefined,
marginRight: mx
? getSpacing(mx, theme)
: mr
? getSpacing(mr, theme)
: undefined,
marginBottom: my
? getSpacing(my, theme)
: mb
? getSpacing(mb, theme)
: undefined,
marginLeft: mx
? getSpacing(mx, theme)
: ml
? getSpacing(ml, theme)
: undefined,
};
}
const marginProps = createStyleFunction([
{
styleProp: 'm',
properties: ['margin'],
getValue: getSpacing,
},
{
styleProp: 'mt',
properties: ['marginTop'],
getValue: getSpacing,
},
{
styleProp: 'mr',
properties: ['marginRight'],
getValue: getSpacing,
},
{
styleProp: 'mb',
properties: ['marginBottom'],
getValue: getSpacing,
},
{
styleProp: 'ml',
properties: ['marginLeft'],
getValue: getSpacing,
},
{
styleProp: 'mx',
properties: ['marginLeft', 'marginRight'],
getValue: getSpacing,
},
{
styleProp: 'my',
properties: ['marginTop', 'marginBottom'],
getValue: getSpacing,
},
]);

export default marginProps;
68 changes: 38 additions & 30 deletions src/styleProps/paddingProps.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,42 @@
import {getSpacing} from '../utils/spacing';
import {checkTheme} from '../utils/theme';
import {createStyleFunction} from '../utils/styleProps';

function paddingProps(props) {
const {p, px, py, pt, pr, pb, pl, theme} = props;

checkTheme(theme);

return {
padding: p ? getSpacing(p, theme) : undefined,
paddingTop: py
? getSpacing(py, theme)
: pt
? getSpacing(pt, theme)
: undefined,
paddingRight: px
? getSpacing(px, theme)
: pr
? getSpacing(pr, theme)
: undefined,
paddingBottom: py
? getSpacing(py, theme)
: pb
? getSpacing(pb, theme)
: undefined,
paddingLeft: px
? getSpacing(px, theme)
: pl
? getSpacing(pl, theme)
: undefined,
};
}
const paddingProps = createStyleFunction([
{
styleProp: 'p',
properties: ['padding'],
getValue: getSpacing,
},
{
styleProp: 'pt',
properties: ['paddingTop'],
getValue: getSpacing,
},
{
styleProp: 'pr',
properties: ['paddingRight'],
getValue: getSpacing,
},
{
styleProp: 'pb',
properties: ['paddingBottom'],
getValue: getSpacing,
},
{
styleProp: 'pl',
properties: ['paddingLeft'],
getValue: getSpacing,
},
{
styleProp: 'px',
properties: ['paddingLeft', 'paddingRight'],
getValue: getSpacing,
},
{
styleProp: 'py',
properties: ['paddingTop', 'paddingBottom'],
getValue: getSpacing,
},
]);

export default paddingProps;
2 changes: 1 addition & 1 deletion src/styleProps/positionProps.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {getSpacing} from '../utils/spacing';
import {checkTheme} from '../utils/theme';
import {checkTheme} from '../utils/styleProps';

function positionProps(props) {
const {pos, position, top, left, bottom, right, z, theme} = props;
Expand Down
2 changes: 1 addition & 1 deletion src/styleProps/textProps.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {alpha} from '../utils/colors';
import {checkTheme} from '../utils/theme';
import {checkTheme} from '../utils/styleProps';
import {ellipsis, overflowWrap as wrap} from '../mixins';

const textTransformMap = {
Expand Down
112 changes: 112 additions & 0 deletions src/utils/styleProps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import {ThemeSectionError} from '../ThemeSection';

function checkTheme(theme) {
if (!theme || !theme.globals) {
throw new ThemeSectionError();
}
}

/**
* Retrieve a responsive style prop's value.
* If prop is not an array, return raw value
*
* @param {string|number|Array} prop - Prop passed to the component
* @param {number} index - index used to retrieve value if prop is an array
*/

function getValueByIndex(prop, index = 0) {
if (Array.isArray(prop)) {
return prop[index];
}
if (index > 0) {
return undefined;
}
return prop;
}

/**
* Builds a CSS ruleset based on the props passed to the component.
*
* @param {object[]} stylePropConfig - Style prop configuration objects
* @param {string} stylePropConfig[].styleProp - Name of the style prop
* @param {Array|Function} stylePropConfig[].properties - Array of the CSS properties
* to set, or a function to create such an array
* @param {Function} stylePropConfig[].getValue - Transforms the prop value into a
* valid CSS property value
* @param {object} passedProps
* @param {number} breakpointIndex
*/

function getStylePropRules(stylePropConfig, passedProps, breakpointIndex) {
const rules = {};
stylePropConfig.forEach(
({styleProp: stylePropKey, properties, getValue}) => {
const styleProp = getValueByIndex(
passedProps[stylePropKey],
breakpointIndex
);
const definedProps =
typeof properties === 'function'
? properties(styleProp)
: properties;
if (styleProp) {
definedProps.forEach(prop => {
rules[prop] = getValue(styleProp, passedProps.theme);
});
}
}
);
return rules;
}

function getResponsiveRules(stylePropConfig, passedProps) {
// Get baseline styles
const rules = getStylePropRules(stylePropConfig, passedProps, 0);

const {breakpoints} = passedProps;
if (!breakpoints) {
return rules;
}

// If any breakpoints exist, add them to the rule set
const bps = Array.isArray(breakpoints) ? breakpoints : [breakpoints];
bps.forEach((breakpoint, index) => {
const breakpointWidth =
passedProps.theme.globals.breakpoints[breakpoint] || breakpoint;
const breakpointQuery = `@media screen and (min-width: ${breakpointWidth})`;
rules[breakpointQuery] = getStylePropRules(
stylePropConfig,
passedProps,
index + 1
);
});

return rules;
}

/**
* Generates a style prop function that can be used directly in a styled
* component, e.g.
* ```
* const MyLink = styled.a`
* ${myStyleProp}
* `
* ```
*
* @param {object[]} stylePropConfig - Style prop configuration objects
* @param {string} stylePropConfig[].styleProp - Name of the style prop
* @param {Array|Function} stylePropConfig[].properties - Array of the CSS properties
* to set, or a function to create such an array
* @param {Function} stylePropConfig[].getValue - Transforms the prop value into a
* valid CSS property value
*/

function createStyleFunction(stylePropConfig) {
return function styleFunction(props) {
checkTheme(props.theme);
const rules = getResponsiveRules(stylePropConfig, props);
return rules;
};
}

export {checkTheme, createStyleFunction};
9 changes: 0 additions & 9 deletions src/utils/theme.js

This file was deleted.

0 comments on commit 64f5f92

Please sign in to comment.