UNPKG

@devseed-ui/theme-provider

Version:
196 lines (175 loc) 5.05 kB
import get from 'lodash.get'; import { rgba as polishedRgba } from 'polished'; import { css } from 'styled-components'; import { multiply } from './math'; /** * Return a function that when executed appends the `unit` to the value. * * @example * const percent = unitify('%'); * percent(10) // -> 10% * * @param {string} unit The unit to use */ export const unitify = (unit) => (v) => typeof v === 'function' ? (...args) => `${v(...args)}${unit}` : `${v}${unit}`; /** * Return the given value with `rem` appended. * If value is a function will execute it. This allows to use directly in * styled-components with themeVal * * @see themeVal() * * @example * rem(themeVal('shape.rounded')) * * @param {mixed} val The value */ export const rem = unitify('rem'); /** * Return the given value with `px` appended. * If value is a function will execute it. This allows to use directly in * styled-components with themeVal * * @see themeVal() * * @example * px(themeVal('shape.rounded')) * * @param {mixed} val The value */ export const px = unitify('px'); /** * Convert the given value to the given unit using the base size defined in the * theme. (theme.type.base.root) If value is a function will execute it. This * allows to use directly in styled-components with themeVal. * Only conversion between rem and px is allowed. Any other destination unit * will be ignored and the value returned as is. * * @example * rp2rp(themeVal('layout.max'), 'px') * * @param {mixed} v The value * @param {mixed} unit The destination unit * * @throws Error if the root type pixel value is not defined. */ const rp2rp = (v, unit) => (...args) => { v = typeof v === 'function' ? v(...args) : v; unit = typeof unit === 'function' ? unit(...args) : unit; const rootV = get(args[0], 'theme.type.base.root', null); if (rootV === null) { throw new Error( 'Root type pixel value not found in theme.type.base.root' ); } const srcUnit = (v + '').match(/[0-9]*(?:.[0-9]+)?(.*)/)[1]; const srcVal = parseFloat(v); if (unit === 'px') { return px(srcUnit === 'rem' ? srcVal * parseFloat(rootV) : srcVal); } if (unit === 'rem') { return rem(srcUnit === 'px' ? srcVal / parseFloat(rootV) : srcVal); } // Invalid unit - return as is. return v; }; /** * Convert the given value to pixels using the base size defined in the theme. * (theme.type.base.root) * If value is a function will execute it. This allows to use directly in * styled-components with themeVal * * @see rp2rp() * * @example * val2px(themeVal('layout.max')) * * @param {mixed} val The value * * @throws Error if the root type pixel value is not defined. */ export const val2px = (val) => { return rp2rp(val, 'px'); }; /** * Convert the given value to rem using the base size defined in the theme. * (theme.type.base.root) * If value is a function will execute it. This allows to use directly in * styled-components with themeVal * * @see rp2rp() * * @example * val2rem(themeVal('layout.max')) * * @param {mixed} val The value * * @throws Error if the root type pixel value is not defined. */ export const val2rem = (val) => { return rp2rp(val, 'rem'); }; /** * Returns a function to be used with styled-components and gets a value from * the theme property. * * @param {string} path The path to get from theme */ export const themeVal = (path) => ({ theme }) => { const v = get(theme, path, undefined); if (v === undefined) { console.error( // eslint-disable-line `Theme Value Error: path [${path}] not found in theme.`, theme ); } return v; }; /** * Allows a function to be used with style-components interpolation, passing the * component props to each one of the functions arguments if those arguments are * functions. * * Useful in conjunction with themeVal. Instead of: * ${(props) => rgba(props.theme.colors.primaryColor, 0.16)} * you can do * ${rgbaFn(themeVal('colors.primaryColor'), 0.16)} * * @param {function} fn The function to wrap. * * @returns {function} Curried function */ export const stylizeFunction = (fn) => { return (...fnArgs) => (...props) => { const mappedArgs = fnArgs.map((arg) => typeof arg === 'function' ? arg(...props) : arg ); return fn(...mappedArgs); }; }; /** * Returns the layout.space value form the theme multiplied by the * given multiplier. * * @param {number} m multiplier */ export const glsp = (...args) => { args = args.length ? args : [1]; const fns = args.map((m) => multiply(themeVal('layout.space'), m)); // If the there's only one argument return in value format to be used by // other methods that need this to resolve to a number. if (fns.length === 1) return fns[0]; const spaces = Array(args.length - 1).fill(' '); return css(['', ...spaces, ''], ...fns); }; /** * Polished rgba function but stylized. */ export const rgba = stylizeFunction(polishedRgba);