npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-themeable-ts

v1.0.0

Published

Handle classname and style props in React

Downloads

33

Readme

Build Status Coverage Status npm Dependency Status DevDependency Status

react-themeable-ts

A CommonJS/ES2015/ES2017 module, inspired by react-themeable and react-themr.

Features:

  • Written in typescript and typings are auto generated.
  • Unlike react-themeable does not handle key in returned payload at all. That means that radium users must manually supply key to their react elements. You are better off using react-themeable instead.
  • Main function theme parameter can be empty, in case you do conditional stuff and it ends up being falsely.
  • You may mix classnames and style objects, they will be merged in the order you specify the arguments.
  • Fully memoized if you use it in combination with the higher order component decorator. That means it's really fast on re-renders, as it only calculates if the theme prop changes.
  • Allows you to do conditional classes if you wrap an object with an array, that is because an object will be used as a style prop payload. All other argument payload types will be passed to classname module.

Usage

<MyComponent theme={theme} />

Install

npm -S i react-themeable-ts Or yarn

Usage

react-themeable-ts exports themeable and internal function typescript interfaces.

This function is designed to accept a theme prop inside of your render method. This then returns a small helper function that accepts a series of style names.

const theme = themeable(this.props.theme);
...
<div {...theme(...styleNames)} />

This helper function detects whether a theme is class or style based, and creates the appropriate attributes for you. These attributes are either a style object or classname string depending on the payload.

For example:

import React, { Component } from 'react';
import { themeable } from 'react-themeable-ts';

class MyComponent extends Component {
  render() {
    const theme = themeable(this.props.theme);

    return (
      <div {...theme('root')}>
        <div {...theme('foo', 'bar')}>Foo Bar</div>
        <div {...theme('baz')}>Baz</div>
      </div>
    );
  }
}

A theme can now be passed to your component like so:

CSS Modules - maps to className prop to elements

MyComponentTheme.css

.root { color: black; }
.foo { color: red; }
.foo:hover { color: green; }
.bar { color: blue; }
import theme from './MyComponentTheme.css';
...
<MyComponent theme={theme} />

Aphrodite

import { StyleSheet, css } from 'aphrodite';

const theme = StyleSheet.create({
  root: {
    color: 'black'
  },
  foo: {
    color: 'red',
    ':hover': {
      color: 'green'
    }
  },
  bar: {
    color: 'blue'
  }
});
...
<MyComponent theme={[ theme, css ]} />

React Style

import StyleSheet from 'react-style';

const theme = StyleSheet.create({
  root: {
    color: 'black'
  },
  foo: {
    color: 'red'
  },
  bar: {
    color: 'blue'
  }
});
...
<MyComponent theme={theme} />

JSS

Import jss from 'jss';

const sheet = jss.createStyleSheet ({
  root: {
    color: 'black'
  },
  foo: {
    color: 'red'
  },
  bar: {
    color: 'blue'
  }
}).attach();
...
<MyComponent theme={sheet.classes} />

Global CSS

.MyComponent__root { color: black; }
.MyComponent__foo { color: red; }
.MyComponent__foo:hover { color: green; }
.MyComponent__bar { color: blue; }
const theme = {
  root: 'MyComponent__root',
  foo: 'MyComponent__foo',
  bar: 'MyComponent__bar'
};
...
<MyComponent theme={theme} />

Inline styles

const theme = {
  root: {
    color: 'black'
  },
  foo: {
    color: 'red'
  },
  bar: {
    color: 'blue'
  }
};
...
<MyComponent theme={theme} />

HOC - Higher order component

When decorating a stateless function/Class the defaults are:

  • theme prop will accept the classes/styles
  • t prop will become the resulting function invoked with themeable(props.theme) from above.
  • The themable function will be memoized and will return a new function if only the main theme object changes. The returning themeable function is in turn also memoized and will not compute unless argument changed which was not already memoized.

Examples:

Defaults

import { PureComponent } from 'react';
import { themeDecorator } from 'react-themeable-ts';

@themeDecorator()
class MyComponent extends PureComponent {

  render() {
    const theme = this.props.t;
    return (
      <div {...theme('root')}>
        <span {...theme('span1')}>
          My test component
        </span>
        <p {...theme('p1')}>
          My test component
        </p>
      </div>
    );
  }

};

// Component used
const MyJSXElement = (
  <MyComponent
    theme={{
      root: 'class-root',
      span1: 'class-span1',
      p1: 'class-p1',
    }}
  >
);

With custom options

import { PureComponent } from 'react';
import { themeDecorator } from 'react-themeable-ts';

@themeDecorator({
  // Rename default from 'theme' to something else
  themeKey: 'customThemeProp',
  // Rename default `t` to something else
  themeProp: 'myThemeableFn',
})
class MyComponent extends PureComponent {

  render() {
    const theme = this.props.myThemeableFn;
    return (
      <div {...theme('root')}>
        <span {...theme('span1')}>
          My test component
        </span>
        <p {...theme('p1')}>
          My test component
        </p>
      </div>
    );
  }

};

// Component used
const MyJSXElement = (
  <MyComponent
    customThemeProp={{
      root: 'class-root',
      span1: 'class-span1',
      p1: 'class-p1',
    }}
  >
);

ThemeProvider - Wrapper component

Optional HOC you can use if you feel like globally handling themes via React context. It accepts one single prop reactThemeable as an object which you can freely nest however you please.

Examples:

import { PureComponent, PropTypes } from 'react';
import { ThemeProvider } from 'react-themable-ts';

class App extends PureComponent {

  render() {
    return (
      <ThemeProvider
        reactThemeable={{
          ComponentWithContext2: {
            root: 'class-root',
            span1: 'class-span1',
            p1: 'class-p1',
          },
        }}
      >
        {props.children}
      </ThemeProvider>
    );
  }

}

// Any component can then access the context for example:
import { themeable } from 'react-themeable-ts';

const MyStatelessComponent = (props, context) => {
  const theme = themeable(context.reactThemeable.ComponentWithContext2);
  return (
    <div {...theme('root')}>
      My content
    </div>

  );
};
MyStatelessComponent.contextTypes = {
  reactThemeable: PropTypes.object.isRequired,
};

Usage with HOC:

import { themeDecorator } from 'react-themeable-ts';
import { PureComponent, PropTypes } from 'react';

// First decorate a component with HOC
const decoratorFn = themeDecorator({
  contextPath: ['reactThemeable', 'ComponentWithContext2'],
  // or string with dots, see lodash.get method https://lodash.com/docs/4.17.4#get
  // contextPath: 'reactThemeable.ComponentWithContext2',
});

const MyStatelessComponent = (props, context) => {
  const { t } = props;
  return (
    <div {...t('root')}>
      <span {...t('span1')}>
        My test SFC component
      </span>
      <p {...t('p1')}>
        My test SFC component
      </p>
    </div>
  );
};
MyStatelessComponent.contextTypes = {
  reactThemeable: PropTypes.object.isRequired,
};

const MyDecoratedComponent = decoratorFn(MyStatelessComponent);

// Then use in theme provider
// This works because the HOC comes with contextTypes enabled for prop reactThemeable.
class App extends React.PureComponent {

  render() {
    return (
      <ThemeProvider
        reactThemeable={{
          ComponentWithContext2: {
            root: 'class-root',
            span1: 'class-span1',
            p1: 'class-p1',
          },
        }}
      >
        <MyDecoratedComponent />
      </ThemeProvider>
    );
  }

}

API

themeable - Main function

import { themeable } from 'react-themeable-ts';
// Or without react as dependency
// import { themeable } from 'react-themeable-ts/themeable';

Accepts any number of comma-separated values. Also accepts nothing, in case you do conditional stuff and it ends up being falsely. The falsely values will be omitted and string values will be mapped to either className string or style object and then merged in order you specify the arguments.

Examples:

className

const theme = {
  root: 'root-class'
  elephant: ''
};

const t = themeable(theme);

t('root');
// {
//  className: 'root-class'
// }

t('root', 'does-not-exist', null, undefined, 'another one', 'elephant');
// {
//  className: 'root-class'
// }

style

const theme = {
  root: {
    color: 'red',
    display: 'none'
  }
};

const t = themeable(theme);

t('root');
// {
//  style: {
//    color: 'red',
//    display: 'none'
//  }
// }

t('root', 'does-not-exist', null, undefined, 'another one');
// {
//  style: {
//    color: 'red',
//    display: 'none'
//  }
// }

Advanced className and style and conditional mix, the real power of the library.

describe('advanced classnames with classnames module', () => {
  const styles = {
    root: 'rrr',
    common: 'common-class',
    foo: 'fff',
    bar: {
      color: 'blue',
      fontWeight: 'bold',
    },
    baz: 'bbb',
    hover: {
      color: 'red',
      display: 'block',
    },
    base: {
      fontWeight: 400,
      backgroundColor: 'blue',
    },
    con1: ['con1', 'con11'],
    con2: [{
      con2: true,
      con22: 'truthy string',
    }],
    con3: [
      {
        con3: false,
      },
      'con33',
    ],
  };
  const advClassnames = themeable(styles);

  it('it passes all but objects to classname module', () => {
    expect(advClassnames(
      0,
      null,
      undefined,
      'root',
      'common',
      'foo',
      'bar',
      'baz',
      'hover',
      'does not exist',
      '',
      'base',
      'con1',
      'con2',
      'con3'
    ))
      .to.deep.equal({
        style: {
          color: 'red',
          fontWeight: 400,
          display: 'block',
          backgroundColor: 'blue',
        },
        className: 'rrr common-class fff bbb con1 con11 con2 con22 con33',
      });
  });
});

themeDecorator - HOC

import { themeDecorator } from 'react-themeable-ts';

Accepts single argument and is an object with props:

| Parameter | Default | Type | Description |:---|:---|:---|:--- | themeKey(optional) | theme | string | The prop the HOC will use as argument for themeable. | | themeProp(optional) | t | string | The prop name passed prop to DecoratedComponent as the returned themeable function. | | memoizeeOpts(optional) | { length: 1, max: 10 } | Object | options passed to memoizee function. | | contextPath(optional) | undefined | string/string[] | If truthy will be used as a path variable to navigate in context and will override themeKey. It accepts lodash.get method argument to navigate in context prop. |

Contributing

Requires

  • [email protected] because of package.json - prepare script. (only required to run hook when publish)
  • npm -g i gulp-cli jest-cli or yarn global add gulp-cli jest-cli.

Usage

  • gulp --tasks to get going.

Developing

  • jest --watchAll to watch recompiled files and rerun tests.

Testing

Supports:

  • jest, needs jest-cli installed. it will execute the transpiled files from typescript.

Dist

  • gulp will run default task which consist of running tasks:
  • lint, clean, build, minify then jest and collect coverage.