UNPKG

create-emotion-styled

Version:

The styled API for emotion

223 lines (208 loc) 6.92 kB
// @flow import type { Emotion, Interpolations } from 'create-emotion' import { channel, contextTypes } from '../../emotion-theming/src/utils' import type { ElementType } from 'react' import typeof ReactType from 'react' import type { CreateStyled, StyledOptions } from './utils' import { testPickPropsOnComponent, testAlwaysTrue, testPickPropsOnStringTag, pickAssign, setTheme } from './utils' let warnedAboutExtractStatic = false function createEmotionStyled(emotion: Emotion, view: ReactType) { let createStyled: CreateStyled = (tag, options) => { if (process.env.NODE_ENV !== 'production') { if (tag === undefined) { throw new Error( 'You are trying to create a styled element with an undefined component.\nYou may have forgotten to import it.' ) } } let staticClassName let identifierName let stableClassName let shouldForwardProp if (options !== undefined) { staticClassName = options.e identifierName = options.label stableClassName = options.target shouldForwardProp = tag.__emotion_forwardProp && options.shouldForwardProp ? propName => tag.__emotion_forwardProp(propName) && // $FlowFixMe options.shouldForwardProp(propName) : options.shouldForwardProp } const isReal = tag.__emotion_real === tag const baseTag = staticClassName === undefined ? (isReal && tag.__emotion_base) || tag : tag if (typeof shouldForwardProp !== 'function') { shouldForwardProp = typeof baseTag === 'string' && baseTag.charAt(0) === baseTag.charAt(0).toLowerCase() ? testPickPropsOnStringTag : testPickPropsOnComponent } return function() { let args = arguments let styles = isReal && tag.__emotion_styles !== undefined ? tag.__emotion_styles.slice(0) : [] if (identifierName !== undefined) { styles.push(`label:${identifierName};`) } if (staticClassName === undefined) { if (args[0] == null || args[0].raw === undefined) { styles.push.apply(styles, args) } else { styles.push(args[0][0]) let len = args.length let i = 1 for (; i < len; i++) { styles.push(args[i], args[0][i]) } } } else if ( process.env.NODE_ENV !== 'production' && !warnedAboutExtractStatic ) { console.warn( 'extractStatic is deprecated and will be removed in emotion@10. We recommend disabling extractStatic or using other libraries like linaria or css-literal-loader' ) warnedAboutExtractStatic = true } class Styled extends view.Component<*, { theme: Object }> { unsubscribe: number mergedProps: Object static toString: () => string static __emotion_real: any static __emotion_styles: Interpolations static __emotion_base: Styled static __emotion_target: string static __emotion_forwardProp: void | (string => boolean) static withComponent: (ElementType, options?: StyledOptions) => any componentWillMount() { if (this.context[channel] !== undefined) { this.unsubscribe = this.context[channel].subscribe( setTheme.bind(this) ) } } componentWillUnmount() { if (this.unsubscribe !== undefined) { this.context[channel].unsubscribe(this.unsubscribe) } } render() { const { props, state } = this this.mergedProps = pickAssign(testAlwaysTrue, {}, props, { theme: (state !== null && state.theme) || props.theme || {} }) let className = '' let classInterpolations = [] if (props.className) { if (staticClassName === undefined) { className += emotion.getRegisteredStyles( classInterpolations, props.className ) } else { className += `${props.className} ` } } if (staticClassName === undefined) { className += emotion.css.apply( this, styles.concat(classInterpolations) ) } else { className += staticClassName } if (stableClassName !== undefined) { className += ` ${stableClassName}` } return view.createElement( baseTag, // $FlowFixMe pickAssign(shouldForwardProp, {}, props, { className, ref: props.innerRef }) ) } } Styled.displayName = identifierName !== undefined ? identifierName : `Styled(${ typeof baseTag === 'string' ? baseTag : baseTag.displayName || baseTag.name || 'Component' })` if (tag.defaultProps !== undefined) { // $FlowFixMe Styled.defaultProps = tag.defaultProps } Styled.contextTypes = contextTypes Styled.__emotion_styles = styles Styled.__emotion_base = baseTag Styled.__emotion_real = Styled Styled.__emotion_forwardProp = shouldForwardProp Object.defineProperty(Styled, 'toString', { value() { if ( process.env.NODE_ENV !== 'production' && stableClassName === undefined ) { return 'NO_COMPONENT_SELECTOR' } // $FlowFixMe return `.${stableClassName}` } }) Styled.withComponent = ( nextTag: ElementType, nextOptions?: StyledOptions ) => { return createStyled( nextTag, nextOptions !== undefined ? // $FlowFixMe pickAssign(testAlwaysTrue, {}, options, nextOptions) : options )(...styles) } return Styled } } if (process.env.NODE_ENV !== 'production' && typeof Proxy !== 'undefined') { createStyled = new Proxy(createStyled, { get(target, property) { switch (property) { // react-hot-loader tries to access this stuff case '__proto__': case 'name': case 'prototype': case 'displayName': { return target[property] } default: { throw new Error( `You're trying to use the styled shorthand without babel-plugin-emotion.` + `\nPlease install and setup babel-plugin-emotion or use the function call syntax(\`styled('${property}')\` instead of \`styled.${property}\`)` ) } } } }) } return createStyled } export default createEmotionStyled