import { CSSProperties, useContext } from 'rax'; import Core from '@rax-ui/core'; import { default as Context, StyleProps, ThemeType } from './context'; export interface StyleType { [key: string]: CSSProperties; } export interface StyledComponentProps { styles?: ((theme: ThemeType) => T) | StyleType; } export type StyleProviderFunction = (theme: ThemeType) => StyleType; const EXCLUDES = ['fontWeight', 'zIndex', 'flex', 'flexGrow', 'flexShinrk', 'opacity']; export const transformUnit = (style = {}, transform) => { Object.keys(style).forEach((key) => { if (EXCLUDES.indexOf(key) !== -1 || typeof style[key] !== 'number' || (key === 'lineHeight' && style[key] < 4)) { return; } if (typeof transform === 'function') { style[key] = transform(style[key]); return; } const unit = 'rpx'; style[key] = [style[key], unit].join(''); }); }; export function useStyles< T extends StyleType, S extends StyleType, N extends {[K in keyof T]?: boolean} | Array | keyof T >( styleProvider: T | ((theme: ThemeType) => T), props: StyledComponentProps = {}, classNameCreator: (classnames: (names: N) => CSSProperties) => S, ): S { let { styles: customStyles = {} } = props; const { transformUnit: contextTransform } = useContext(Context); const theme = useTheme(); const GLOBAL_STYLES: CSSProperties = { fontFamily: theme.Core['font-family-base'], boxSizing: 'border-box', }; if (typeof customStyles === 'function') { customStyles = customStyles(theme as ThemeType); } const styles = (typeof styleProvider === 'function' ? styleProvider(theme as ThemeType) : styleProvider) as StyleType; Object.keys(customStyles).forEach((key) => { if (!!styles[key] && !!customStyles[key]) { styles[key] = { ...styles[key], ...customStyles[key], }; } }); Object.keys(styles).forEach((key) => { transformUnit(styles[key], contextTransform); styles[key] = { ...GLOBAL_STYLES, ...styles[key], }; }); const classnames = (names: N) => { if (!names) { return {}; } if (Array.isArray(names)) { return Object.assign({}, ...Array.from(new Set(names)).map((name) => classnames(name))); } switch (typeof names) { case 'object': return classnames(Object.keys( (names as { [key: string]: boolean }), ).filter((key) => !!names[key]).join(' ') as N); case 'string': return Object.assign( {}, ...(names as string).split(' ').filter( (name) => !!name && !!styles[name], ).map((name) => styles[name])); default: break; } return {}; }; delete props.styles; return (classNameCreator ? classNameCreator(classnames) : styles) as S; } export const useTheme = () => { const { theme = {} } = useContext(Context); theme.Core = { ...Core, ...(theme.Core || {}), }; return theme as ThemeType; };