UNPKG

vcc-ui

Version:

VCC UI is a collection of React UI Components that can be used for developing front-end applications at Volvo Car Corporation.

173 lines (146 loc) 4.48 kB
import React from 'react'; import { useFela } from 'react-fela'; import { objectMap } from '../../object-map'; import { renameKeys } from '../../rename-keys'; import PropTypes from 'prop-types'; export const SUB_STYLES = ['standard', 'inline-link', 'emphasis', 'overline']; export const convertBaselineValues = objectMap((value, key) => { if (key === 'top') { return `${value}px 0 0`; } else if (key === 'bottom') { return `0 0 ${value}px`; } if (typeof value === 'object') { return convertBaselineValues(value); } return value; }); export const renameBaselineKeys = renameKeys({ top: 'padding', bottom: 'margin', }); export const convertBaselineKeys = obj => renameBaselineKeys( objectMap(value => { if (typeof value === 'object') { return renameBaselineKeys(value); } return value; })(obj) ); /** Baseline adjustments allow us to fine tune each type scale so it sits on the 4px baseline. It only needs to be applied to the root element. We use a combination of margin and padding to prevent margin collapsing. */ export const createBaselineAdjustments = ({ baselineAdjustments = {}, isRootElement, shouldAdjustBaseline, }) => { if (!shouldAdjustBaseline || !isRootElement) { return { margin: 0, padding: 0 }; } return convertBaselineKeys(convertBaselineValues(baselineAdjustments)); }; const TextContext = React.createContext(); export const Text = React.forwardRef( ( { extend, variant: userVariant, subStyle, alignBaseline: shouldAdjustBaseline = true, as, className, children, ...props }, ref ) => { const { css, theme } = useFela(props); /** Context is used for two reasons: 1. Allows us to derive the type scale from the context, letting us omit the variant name for nested styles, eg: <Text variant="columbus">Hello <Text subStyle="bold">World</Text></Text> 2. Lets us know if the element is the 'root' element, and apply baseline adjustments if required. */ return ( <TextContext.Consumer> {({ parentVariant: variant = userVariant, isRootElement = true, } = {}) => { const typeScaleStyle = theme.typeScale[variant]; const { element: preferredElement, styles, baselineAdjustments, } = typeScaleStyle[subStyle]; const baselineAdjustmentStyles = createBaselineAdjustments({ baselineAdjustments, isRootElement, shouldAdjustBaseline, }); const displayModifier = isRootElement ? { display: 'block' } : {}; const Element = as || preferredElement || 'p'; return ( <Element ref={ref} {...props} className={ css( { color: theme.color.foreground.primary, }, displayModifier, styles, baselineAdjustmentStyles, extend ) + (className ? ' ' + className : '') } > <TextContext.Provider value={{ parentVariant: variant, isRootElement: false }} > {children} </TextContext.Provider> </Element> ); }} </TextContext.Consumer> ); } ); Text.displayName = 'Text'; Text.defaultProps = { variant: 'columbus', subStyle: 'standard', }; Text.propTypes = { /** A JSX node */ children: PropTypes.node, /** Any valid React element, function, or a string specifying a name for an HTML element */ as: PropTypes.oneOfType([ PropTypes.string, PropTypes.element, PropTypes.func, ]), /** One of the available type scale styles */ variant: PropTypes.string, /** One of the available sub-styles for a given type scale */ subStyle: PropTypes.string, /** An object containing valid CSS style declarations */ extend: PropTypes.oneOfType([ PropTypes.object, PropTypes.func, PropTypes.array, ]), /** Add an additional custom className to element. Warning: make sure it doesn't collide with the classNames being generated for the atomic CSS */ className: PropTypes.string, /** Adjust padding-top/margin-bottom to the text to keep it aligned with the micro grid. */ alignBaseline: PropTypes.bool, };