import React from 'react'; import styled, { InterpolationFunction, StyledComponent, StyledComponentPropsWithRef, ThemedStyledInterface, ThemedStyledProps, } from 'styled-components'; import getDefaultTheme, { Theme } from '../themes/default'; import { Base16Theme } from 'base16'; import { ThemeFromProvider } from './theme'; type StyleFunction< C extends keyof JSX.IntrinsicElements | React.ComponentType, // eslint-disable-next-line @typescript-eslint/ban-types O extends object = {}, > = InterpolationFunction< ThemedStyledProps & O, Theme> >; interface StylesObject< C extends keyof JSX.IntrinsicElements | React.ComponentType, // eslint-disable-next-line @typescript-eslint/ban-types O extends object = {}, > { [type: string]: StyleFunction; } type Styles< C extends keyof JSX.IntrinsicElements | React.ComponentType, // eslint-disable-next-line @typescript-eslint/ban-types O extends object = {}, > = StylesObject | StyleFunction; function isStylesObject< C extends keyof JSX.IntrinsicElements | React.ComponentType, // eslint-disable-next-line @typescript-eslint/ban-types O extends object = {}, >(styles: Styles): styles is StylesObject { return typeof styles === 'object'; } const getStyle = < C extends keyof JSX.IntrinsicElements | React.ComponentType, // eslint-disable-next-line @typescript-eslint/ban-types O extends object = {}, >( styles: Styles, type: string, ) => (isStylesObject(styles) ? styles[type] || styles.default : styles); function isThemeFromProvider( theme: Theme | Base16Theme, ): theme is ThemeFromProvider { return (theme as ThemeFromProvider).type !== undefined; } export default function createStyledComponent< C extends keyof JSX.IntrinsicElements | React.ComponentType, // eslint-disable-next-line @typescript-eslint/ban-types O extends object = {}, >( styles: Styles, component?: C, ): StyledComponent { return (styled as ThemedStyledInterface)((component || 'div') as C)` ${(props: ThemedStyledProps & O, Theme>) => isThemeFromProvider(props.theme as Theme | Base16Theme) ? getStyle(styles, props.theme.type as string) : // used outside of container (theme provider) getStyle( styles, 'default', )({ ...props, theme: getDefaultTheme(props.theme as Base16Theme), })} ` as StyledComponent; } // TODO: memoize it?