react-themeable-1.1.0/0000775000175000017500000000000015261220151013546 5ustar emfoxemfoxreact-themeable-1.1.0/test/0000775000175000017500000000000012717614046014542 5ustar emfoxemfoxreact-themeable-1.1.0/test/index.js0000664000175000017500000000462012717614046016211 0ustar emfoxemfoximport themeable from '../src'; import { expect } from 'chai'; describe('className', () => { const classes = { foo: 'aaa', bar: 'bbb' }; const classTheme = themeable(classes); it('should return a single class', () => { expect(classTheme(1, 'foo')) .to.deep.equal({ key: 1, className: classes.foo }); }); it('should return multiple classes', () => { expect(classTheme(2, 'foo', 'bar')) .to.deep.equal({ key: 2, className: `${classes.foo} ${classes.bar}` }); }); it('should ignore falsy values', () => { expect(classTheme(1, null, 'foo', undefined, false)) .to.deep.equal({ key: 1, className: classes.foo }); }); }); describe('styles with classname decorator (e.g. Aphrodite)', () => { const classes = { foo: { color: 'red' }, bar: { color: 'blue' } }; const colorsToString = (...styles) => styles.map(x => x.color).join(' '); const classTheme = themeable([classes, colorsToString]); it('should return a single class', () => { expect(classTheme(1, 'foo')) .to.deep.equal({ key: 1, className: 'red' }); }); it('should return multiple classes', () => { expect(classTheme(2, 'foo', 'bar')) .to.deep.equal({ key: 2, className: 'red blue' }); }); it('should ignore falsy values', () => { expect(classTheme(1, null, 'foo', undefined, false)) .to.deep.equal({ key: 1, className: 'red' }); }); }); describe('style', () => { const styles = { foo: { color: 'red', fontSize: '16px' }, bar: { color: 'blue', fontWeight: 'bold' } }; const styleTheme = themeable(styles); it('should return a single style', () => { expect(styleTheme(1, 'foo')) .to.deep.equal({ key: 1, style: { color: 'red', fontSize: '16px' } }); }); it('should return multiple styles merged', () => { expect(styleTheme(1, 'foo', 'bar')) .to.deep.equal({ key: 1, style: { fontSize: '16px', color: 'blue', fontWeight: 'bold' } }); }); it('should ignore falsy values', () => { expect(styleTheme(1, false, undefined, 'foo', null)) .to.deep.equal({ key: 1, style: { color: 'red', fontSize: '16px' } }); }); }); react-themeable-1.1.0/src/0000775000175000017500000000000012717614046014352 5ustar emfoxemfoxreact-themeable-1.1.0/src/index.js0000664000175000017500000000105312717614046016016 0ustar emfoxemfoximport assign from 'object-assign'; const truthy = x => x; export default input => { const [ theme, classNameDecorator ] = Array.isArray(input) && input.length === 2 ? input : [ input, null ]; return (key, ...names) => { const styles = names .map(name => theme[name]) .filter(truthy); return typeof styles[0] === 'string' || typeof classNameDecorator === 'function' ? { key, className: classNameDecorator ? classNameDecorator(...styles) : styles.join(' ') } : { key, style: assign({}, ...styles) }; }; }; react-themeable-1.1.0/package.json0000664000175000017500000000174112717614046016054 0ustar emfoxemfox{ "name": "react-themeable", "version": "1.1.0", "description": "Utility for making React components easily themeable", "main": "dist/index.js", "scripts": { "test": "babel-istanbul cover _mocha -- --compilers js:babel/register && babel-istanbul check-coverage --branches 100", "coverage": "cat ./coverage/coverage.json | ./node_modules/codecov.io/bin/codecov.io.js", "build": "babel src -d dist", "prepublish": "npm run build" }, "repository": { "type": "git", "url": "git+https://github.com/markdalgleish/react-themeable.git" }, "author": "Mark Dalgleish", "license": "MIT", "bugs": { "url": "https://github.com/markdalgleish/react-themeable/issues" }, "homepage": "https://github.com/markdalgleish/react-themeable#readme", "dependencies": { "object-assign": "^3.0.0" }, "devDependencies": { "babel": "^5.6.14", "babel-istanbul": "^0.2.10", "chai": "^3.0.0", "codecov.io": "^0.1.5", "mocha": "^2.2.5" } } react-themeable-1.1.0/README.md0000664000175000017500000000741712717614046015053 0ustar emfoxemfox[![Build Status](https://img.shields.io/travis/markdalgleish/react-themeable/master.svg?style=flat-square)](http://travis-ci.org/markdalgleish/react-themeable) [![Coverage](https://img.shields.io/codecov/c/github/markdalgleish/react-themeable/master.svg?style=flat-square)](https://codecov.io/github/markdalgleish/react-themeable) [![npm](https://img.shields.io/npm/v/react-themeable.svg?style=flat-square)](https://www.npmjs.com/package/react-themeable) # react-themeable Utility for making React components easily themeable. **This project is still experimental, so feedback from component authors would be greatly appreciated!** ## Why? The React community is highly fragmented when it comes to styling. How do we write components that can happily co-exist with all of these competing approaches? With react-themeable, you can support custom themes provided by [CSS Modules](https://github.com/css-modules/css-modules), [Radium](http://projects.formidablelabs.com/radium/), [Aphrodite](https://github.com/Khan/aphrodite), [React Style](https://github.com/js-next/react-style), [JSS](https://github.com/jsstyles/jss), global CSS or inline styles as easily as this: ```js ``` ## Install `npm install --save react-themeable` ## Usage `react-themeable` exposes just a single function. This function is designed to accept a `theme` prop inside of your `render` method. This then returns a small helper function that accepts a key and a series of style names. ```js const theme = themeable(this.props.theme); ...
``` *Note: A unique key for each themed element is required for [Radium](http://projects.formidablelabs.com/radium/) to work correctly.* This helper function detects whether a theme is class or style based, and creates the appropriate attributes for you. For example: ```js import React, { Component } from 'react'; import themeable from 'react-themeable'; class MyComponent extends Component { render() { const theme = themeable(this.props.theme); return (
Foo Bar
Baz
); } } ``` A theme can now be passed to your component like so: ### CSS Modules ```css .foo { color: red; } .foo:hover { color: green; } .bar { color: blue; } ``` ```js import theme from './MyComponentTheme.css'; ... ``` ### Radium ```js import Radium from 'radium'; const theme = { foo: { color: 'red', ':hover': { color: 'green' } }, bar: { color: 'blue' } }; const ThemedMyComponent = Radium(MyComponent); ... ``` ### Aphrodite ```js import { StyleSheet, css } from 'aphrodite'; const theme = StyleSheet.create({ foo: { color: 'red', ':hover': { color: 'green' } }, bar: { color: 'blue' } }); ... ``` ### React Style ```js import StyleSheet from 'react-style'; const theme = StyleSheet.create({ foo: { color: 'red' }, bar: { color: 'blue' } }); ... ``` ### JSS ```js import jss from 'jss'; const sheet = jss.createStyleSheet({ foo: { color: 'red' }, bar: { color: 'blue' } }).attach(); ... ``` ### Global CSS ```css .MyComponent__foo { color: red; } .MyComponent__foo:hover { color: green; } .MyComponent__bar { color: blue; } ``` ```js const theme = { foo: 'MyComponent__foo', bar: 'MyComponent__bar' }; ... ``` ### Inline styles ```js const theme = { foo: { color: 'red' }, bar: { color: 'blue' } }; ... ``` ## License [MIT](http://markdalgleish.mit-license.org) react-themeable-1.1.0/.travis.yml0000664000175000017500000000011012717614046015664 0ustar emfoxemfoxlanguage: node_js node_js: - "0.10" after_success: npm run coverage react-themeable-1.1.0/.npmignore0000664000175000017500000000000412717614046015554 0ustar emfoxemfoxsrc react-themeable-1.1.0/.gitignore0000664000175000017500000000003312717614046015547 0ustar emfoxemfoxnode_modules dist coverage react-themeable-1.1.0/.editorconfig0000664000175000017500000000031712717614046016241 0ustar emfoxemfox# editorconfig.org root = true [*] indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = false