styletron-inferno/src/styled.js

const createElement = require('inferno-create-element');
const {injectStylePrefixed} = require('styletron-utils');
const {
  assign,
  isNil,
  isObject,
  isString,
  isFunction
} = require('./utils');

const STYLETRON_KEY = '__STYLETRON';

module.exports = styled;

/**
 * Helper function to create styled components
 * @packagename styletron-inferno
 * @param  {string|function} name   Tag name or component function/class
 * @param  {function|object} styles Style object or function that returns a style object
 * @return {function}               Styled component
 *
 * @example
 * import { styled } from 'styletron-inferno';
 *
 * const Panel = styled('div', {
 *   backgroundColor: 'lightblue',
 *   fontSize: '12px'
 * });
 *
 * <Panel>Hello World</Panel>
 *
 * @example
 * import { styled } from 'styletron-inferno';
 *
 * const Panel = styled('div', (props) => ({
 *   backgroundColor: props.alert ? 'orange' : 'lightblue',
 *   fontSize: '12px'
 * }));
 *
 * <Panel alert>Danger!</Panel>
 *
 * @example
 * import { styled } from 'styletron-inferno';
 *
 * const DeluxePanel = styled(Panel, (props) => ({
 *   backgroundColor: props.alert ? 'red' : 'lime',
 *   boxShadow: '3px 3px 3px darkgray',
 *   color: 'white'
 * }));
 *
 * <DeluxePanel>Bonjour Monde</DeluxePanel>
 */
function styled(name, styles) {

  // Styled component
  if (name && name.hasOwnProperty(STYLETRON_KEY)) {
    const component = name[STYLETRON_KEY];
    const stylesArray = component.stylesArray.concat(styles);
    return createStyledComponent(component.name, stylesArray);

  // Tag name or non-styled component
  } else if (isString(name) || isFunction(name)) {
    return createStyledComponent(name, [styles]);
  }

  throw new Error('`styled` takes either a DOM element name or a component');
}

function createStyledComponent(name, stylesArray) {

  function StyledComponent(props, context) {
    const newProps = assign({}, props);
    const styles = resolveStyles(stylesArray, props, context);
    const className = injectStylePrefixed(context.styletron, styles);

    newProps.className = props.className
      ? `${props.className} ${className}`
      : className;

    if (isFunction(props.innerRef)) {
      newProps.ref = props.innerRef;
      delete newProps.innerRef;
    }
    return createElement(name, newProps);
  }

  StyledComponent[STYLETRON_KEY] = {name, stylesArray};

  return StyledComponent;
}

function resolveStyles(stylesArray, props, context) {
  const resolvedStyles = {};
  for (let i = 0, l = stylesArray.length, styles; i < l; i++) {
    styles = stylesArray[i];
    if (!isNil(styles)) {
      if (isFunction(styles)) {
        assign(resolvedStyles, styles(props, context));
      } else if (isObject(styles)) {
        assign(resolvedStyles, styles);
      }
    }
  }
  return resolvedStyles;
}