vcc-ui
Version:
A React library for building user interfaces at Volvo Cars
150 lines (146 loc) • 5.05 kB
JavaScript
import { assignStyle } from 'css-in-js-utils';
import { createRenderer } from 'fela';
import enforceLonghandsEnhancer from 'fela-enforce-longhands';
import embedded from 'fela-plugin-embedded';
import extend from 'fela-plugin-extend';
import falllbackValue from 'fela-plugin-fallback-value';
import hoverMedia from 'fela-plugin-hover-media';
import namedKeys from 'fela-plugin-named-keys';
import prefixer from 'fela-plugin-prefixer';
import responsiveValue from 'fela-plugin-responsive-value';
import rtl from 'fela-plugin-rtl';
import themeValue from 'fela-plugin-theme-value';
import unit from 'fela-plugin-unit';
import sortClassnames from 'fela-sort-classnames';
import sortMediaQueryMobileFirst from 'fela-sort-media-query-mobile-first';
import { mapValueToMediaQuery } from 'fela-tools';
// whitelisting all the props that support responsive array values
export const responsiveProps = {
alignContent: true,
alignItems: true,
alignSelf: true,
aspectRatio: true,
columnGap: true,
display: true,
flex: true,
flexBasis: true,
flexDirection: true,
flexGrow: true,
flexShrink: true,
flexWrap: true,
gap: true,
height: true,
justifyContent: true,
margin: true,
marginBottom: true,
marginLeft: true,
marginRight: true,
marginTop: true,
maxHeight: true,
maxWidth: true,
minHeight: true,
minWidth: true,
order: true,
padding: true,
paddingBottom: true,
paddingLeft: true,
paddingRight: true,
paddingTop: true,
rowGap: true,
textAlign: true,
width: true
};
const getResponsiveMediaQueries = (values, props) => {
const {
fromM,
fromL,
fromXL
} = props.theme.breakpoints;
const mediaQueryMap = {
2: [fromM],
3: [fromM, fromL],
4: [fromM, fromL, fromXL]
};
return mediaQueryMap[values.length];
};
export function styleRenderer() {
let {
isRtl,
enforceLonghands = true,
wrapHoverWithMediaHover = false,
renderFonts = true,
...rest
} = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
const options = rest || {};
// eslint-disable-next-line testing-library/render-result-naming-convention
const renderer = createRenderer({
...options,
enhancers: [sortClassnames(), sortMediaQueryMobileFirst(), ...(enforceLonghands ? [enforceLonghandsEnhancer()] : []), ...(options.enhancers || [])],
plugins: [responsiveObjectValue(responsiveProps), extend(), embedded(), responsiveValue(getResponsiveMediaQueries, responsiveProps), namedKeys(props => props && props.theme && props.theme.breakpoints || {}), renderFonts && themeFontsPlugin, rtl(isRtl ? 'rtl' : 'ltr'), themeValue({
color: theme => theme.color,
backgroundColor: theme => theme.color,
borderColor: theme => theme.color,
stroke: theme => theme.color,
fill: theme => theme.color,
'--icon-color': theme => theme.color,
'--icon-color-secondary': theme => theme.color
}), ...(options.plugins || []), unit(), falllbackValue(), prefixer(), wrapHoverWithMediaHover && hoverMedia()].filter(Boolean)
});
return renderer;
}
// TODO: move to fela repo as an official plugin
function themeFontsPlugin(style, type, renderer, props) {
const fonts = props.theme && props.theme.fonts || [];
const fontsPath = props.theme && props.theme.fontsPath || '';
for (const property in style) {
const value = style[property];
// TODO: maybe we wanna cache already rendered fonts
// but no high prio as Fela does that as well
if (typeof value === 'string' && property === 'fontFamily') {
// check each alternative font value
const fontValues = value.split(',');
const usedFonts = fonts.filter(font => fontValues.indexOf(font.fontFamily) !== -1);
if (usedFonts.length > 0) {
usedFonts.forEach(_ref => {
let {
fontFamily,
src,
...fontProps
} = _ref;
return renderer.renderFont(fontFamily, src.map(
// allow absolute files with http prefix
file => (file.indexOf('http') === -1 ? fontsPath : '') + file), fontProps);
});
}
} else if (typeof value === 'object' && !Array.isArray(value)) {
themeFontsPlugin(value, type, renderer, props);
}
}
return style;
}
// TODO: move to fela repo as an official plugin
function resolveResponsiveObjectValues(style, properties) {
for (const property in style) {
if (properties[property]) {
const value = style[property];
if (typeof value === 'object' && !Array.isArray(value)) {
const {
default: defaultValue,
...mediaValues
} = value;
assignStyle(style, {
[property]: defaultValue
}, {
extend: [mapValueToMediaQuery(mediaValues, value => ({
[property]: value
}))]
});
}
}
}
return style;
}
function responsiveObjectValue() {
let properties = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
return style => resolveResponsiveObjectValues(style, properties);
}