/* * Copyright (c) 2018-present, Revolut LTD. * * This source code is licensed under the Apache 2.0 license found in the * LICENSE file in the root directory of this source tree. */ import { Interpolation, InterpolationFunction, ThemedCssFunction, } from 'styled-components' import { NonFunctionKeys } from 'utility-types' import { space as ssSpace, style as ssStyle, fontSize as ssFontSize } from 'styled-system' import { theme as themeFn } from 'styled-tools' import * as _ from 'lodash' import { ThemeFields } from './themeFields' import { theme as defaultTheme, DefaultTheme } from './theme.main' type BooleanFunction

= (props: P) => boolean type CallbackOrPropKey

= NonFunctionKeys

| BooleanFunction

type CallbackOrValue

= Interpolation

| ThemedCssFunction

/** * Same as style from `styled-system` but wrapped with decorator, for custom things */ export function style(options) { return withTheme(ssStyle(options)) } export function themeGet(themeProp: keyof T, defaultValue?: string) { return withTheme(themeFn(themeProp as string, defaultValue)) as InterpolationFunction< any > } /** * Enables component to have fontSize prop that can have responsive scale styles as well as the direct override */ export const fontSize = withTheme(ssFontSize) /** * Enables component to have space prop that can have responsive scale styles as well as the direct override */ export const space = withTheme(ssSpace) /** * Builds getter for a slice of the theme for convenience use like * colorGet(ColorFields.PRIMARY) instead of `themeGet(THEME_FIELDS.COLORS + '.' + ColorFields.PRIMARY)` */ export function getter({ key }) { return (objectPath, fallbackValue = '') => themeGet(`${key}.${objectPath}`, fallbackValue) } /** * Gets prop value from props object by prop name. */ export const propGet =

(prop: keyof P, defaultValue?: string | number) => ( props: P, ) => { const value = props[prop] as any return value === 0 || value ? value : defaultValue } const px = (it: any) => { return it.toString().endsWith('px') ? it : it + 'px' } /** * Gets prop value from props object by prop name and convert it to css units */ export const getUnitProp =

(prop, defaultValue = '') => _.flow( propGet

(prop, defaultValue), px, ) /** * Adds support for css prop to inject css interpolation extending the styles in this way. */ export const cssMixin =

(cssProp = 'css') => // @ts-ignore withTheme

(props => props[cssProp]) export const colorGet = getter({ key: ThemeFields.Colors, }) export const fontFamilyGet = getter({ key: ThemeFields.FontFaces, }) export const fontSizeGet = getter({ key: ThemeFields.FontSizes, }) export const shadowGet = getter({ key: ThemeFields.Shadows, }) export const paddingGet = getter({ key: ThemeFields.Padding, }) /** * Mandatory to use for all theme related interpolations. * * Wraps styled-component interpolation function injecting default theme if styled-component has no ThemeProvider above * him. Enables all dependent on theme interpolations/components to be ThemeProvider agnostic so you can use components * without it. */ export function withTheme

( callback: InterpolationFunction

, ): InterpolationFunction

{ return ({ theme, ...rest }: P & { theme: Theme }) => { if (theme !== null && theme !== undefined && Object.keys(theme).length !== 0) { return callback({ theme, ...rest } as P & { theme: Theme }) } return callback({ theme: defaultTheme, ...rest } as P & { theme: Theme }) } } /** * Helper function to generate interpolation function for getting variants out of variant function. * Variant function should get theme and return object with keys of 'danger', 'info' etc. and styles for them */ export function variantsGet(getVariantFn) { return withTheme<{ variant?: string }>( props => getVariantFn(props.theme)[props.variant] || {}, ) } function guardArgument(arg) { switch (typeof arg) { case 'function': return props => arg(props) default: return () => arg } } const ifPropCurried = _.curry((propKeyOrCondFn, ifValue, elseValue) => props => { const conditionGuard = typeof propKeyOrCondFn === 'function' ? () => propKeyOrCondFn(props) : () => props[propKeyOrCondFn] const ifGuard = guardArgument(ifValue) const elseGuard = guardArgument(elseValue) return conditionGuard() ? ifGuard(props) : elseGuard(props) }) // curry(A, B, C) export function ifProp

( propKeyOrCondFn: CallbackOrPropKey

, ifValue: CallbackOrValue

, elseValue: CallbackOrValue

, ): InterpolationFunction

// curry(A)(B, C) export function ifProp

( propKeyOrCondFn: CallbackOrPropKey

, ): ( ifValue: CallbackOrValue, elseValue?: CallbackOrValue, ) => InterpolationFunction /** * Curried helper function that gets prop key or function that takes `props` and should return Truthy value so second argument * will be used as value, or Falsy to third to be used. * All arguments can be a typical `(props) =>` functions to retrieve some values from theme or other props. */ export function ifProp

( propKeyOrCondFn: CallbackOrPropKey

, ifValue?: CallbackOrValue

, elseValue?: CallbackOrValue

, ) { if (propKeyOrCondFn !== undefined && ifValue === undefined) { return ifPropCurried(propKeyOrCondFn) as ( ifValue: CallbackOrValue, elseValue?: CallbackOrValue, ) => InterpolationFunction } if (propKeyOrCondFn !== undefined && ifValue !== undefined && elseValue === undefined) { return ifPropCurried(propKeyOrCondFn, ifValue) as ( elseValue: CallbackOrValue, ) => InterpolationFunction } return ifPropCurried(propKeyOrCondFn, ifValue, elseValue) as InterpolationFunction

}