@devseed-ui/theme-provider
Version:
devseed UI Kit Theme
196 lines (175 loc) • 5.05 kB
JavaScript
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);