emotion-theming
Advanced tools
Comparing version 9.2.9 to 10.0.0-beta.0
@@ -1,2 +0,2 @@ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("prop-types"),require("react")):"function"==typeof define&&define.amd?define(["exports","prop-types","react"],t):t(e.emotionTheming={},null,e.React)}(this,function(e,t,n){"use strict";function i(){return(i=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var o in r)Object.prototype.hasOwnProperty.call(r,o)&&(e[o]=r[o])}return e}).apply(this,arguments)}function s(e,t){e.prototype=Object.create(t.prototype),(e.prototype.constructor=e).__proto__=t}t=t&&t.hasOwnProperty("default")?t.default:t;var r,o=function(e){var o={},r=0,n=e;return{publish:function(e){for(var t in n=e,o){var r=o[t];void 0!==r&&r(n)}},subscribe:function(e){var t=r;return o[t]=e,r+=1,e(n),t},unsubscribe:function(e){o[e]=void 0}}},u="__EMOTION_THEMING__",c=((r={})[u]=t.object,r),p=function(e){return"[object Object]"===Object.prototype.toString.call(e)};function a(e,t){if("function"==typeof e){var r=e(t);if(!p(r))throw new Error("[ThemeProvider] Please return an object from your theme function, i.e. theme={() => ({})}!");return r}if(!p(e))throw new Error("[ThemeProvider] Please make your theme prop a plain object");return void 0===t?e:i({},t,e)}var h=function(e){function t(){return e.apply(this,arguments)||this}s(t,e);var r=t.prototype;return r.componentWillMount=function(){var t=this;void 0!==this.context[u]&&(this.unsubscribeToOuterId=this.context[u].subscribe(function(e){t.outerTheme=e,void 0!==t.broadcast&&t.publish(t.props.theme)})),this.broadcast=o(a(this.props.theme,this.outerTheme))},r.getChildContext=function(){var e;return(e={})[u]={subscribe:this.broadcast.subscribe,unsubscribe:this.broadcast.unsubscribe},e},r.componentWillReceiveProps=function(e){this.props.theme!==e.theme&&this.publish(e.theme)},r.componentWillUnmount=function(){var e=this.context[u];void 0!==e&&e.unsubscribe(this.unsubscribeToOuterId)},r.publish=function(e){this.broadcast.publish(a(e,this.outerTheme))},r.render=function(){return this.props.children?n.Children.only(this.props.children):null},t.childContextTypes=c,t.contextTypes=c,t}(n.Component);"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;var f,l=(function(e,t){var p,a,h,f,l,b,d,m;e.exports=(p={childContextTypes:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},a={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},h=Object.defineProperty,f=Object.getOwnPropertyNames,l=Object.getOwnPropertySymbols,b=Object.getOwnPropertyDescriptor,d=Object.getPrototypeOf,m=d&&d(Object),function e(t,r,o){if("string"!=typeof r){if(m){var n=d(r);n&&n!==m&&e(t,n,o)}var i=f(r);l&&(i=i.concat(l(r)));for(var s=0;s<i.length;++s){var u=i[s];if(!(p[u]||a[u]||o&&o[u])){var c=b(r,u);try{h(t,u,c)}catch(e){}}}return t}return t})}(f={exports:{}},f.exports),f.exports);e.ThemeProvider=h,e.withTheme=function(o){var e=o.displayName||o.name||"Component",t=function(t){function e(e){return t.call(this,e)||this}s(e,t);var r=e.prototype;return r.componentWillMount=function(){var t=this,e=this.context[u];void 0!==e?this.unsubscribeId=e.subscribe(function(e){t.setState({theme:e})}):console.error("[withTheme] Please use ThemeProvider to be able to use withTheme")},r.componentWillUnmount=function(){-1!==this.unsubscribeId&&this.context[u].unsubscribe(this.unsubscribeId)},r.render=function(){return n.createElement(o,i({theme:this.state.theme},this.props))},e}(n.Component);return t.displayName="WithTheme("+e+")",t.contextTypes=c,l(t,o)},e.channel=u,e.contextTypes=c,e.createBroadcast=o,Object.defineProperty(e,"__esModule",{value:!0})}); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react"),require("@emotion/core")):"function"==typeof define&&define.amd?define(["exports","react","@emotion/core"],t):t(e.emotionTheming={},e.React,e.emotionCore)}(this,function(e,o,i){"use strict";function a(){return(a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e}).apply(this,arguments)}var t=function(r){var n=new WeakMap;return function(e){if(n.has(e))return n.get(e);var t=r(e);return n.set(e,t),t}},r=t(function(n){return t(function(e){return t=n,"function"==typeof(r=e)?r(t):a({},t,r);var t,r})});"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;var n,f=(function(e,t){var c,p,l,s,m,d,y,h;e.exports=(c={childContextTypes:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},p={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},l=Object.defineProperty,s=Object.getOwnPropertyNames,m=Object.getOwnPropertySymbols,d=Object.getOwnPropertyDescriptor,y=Object.getPrototypeOf,h=y&&y(Object),function e(t,r,n){if("string"!=typeof r){if(h){var o=y(r);o&&o!==h&&e(t,o,n)}var i=s(r);m&&(i=i.concat(m(r)));for(var a=0;a<i.length;++a){var f=i[a];if(!(c[f]||p[f]||n&&n[f])){var u=d(r,f);try{l(t,f,u)}catch(e){}}}return t}return t})}(n={exports:{}},n.exports),n.exports);e.ThemeProvider=function(t){return o.createElement(i.ThemeContext.Consumer,null,function(e){return t.theme!==e&&(e=r(e)(t.theme)),o.createElement(i.ThemeContext.Provider,{value:e},t.children)})},e.withTheme=function(n){var e=n.displayName||n.name||"Component",t=o.forwardRef(function(t,r){return o.createElement(i.ThemeContext.Consumer,null,function(e){return o.createElement(n,a({theme:e,ref:r},t))})});return t.displayName="WithTheme("+e+")",f(t,n)},Object.defineProperty(e,"__esModule",{value:!0})}); | ||
//# sourceMappingURL=emotion.umd.min.js.map |
'use strict'; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } | ||
var PropTypes = _interopDefault(require('prop-types')); | ||
var React = require('react'); | ||
var hoistNonReactStatics = _interopDefault(require('hoist-non-react-statics')); | ||
function _extends() { | ||
_extends = Object.assign || function (target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = arguments[i]; | ||
for (var key in source) { | ||
if (Object.prototype.hasOwnProperty.call(source, key)) { | ||
target[key] = source[key]; | ||
} | ||
} | ||
} | ||
return target; | ||
}; | ||
return _extends.apply(this, arguments); | ||
if (process.env.NODE_ENV === 'production') { | ||
module.exports = require('./index.cjs.prod.js'); | ||
} else { | ||
module.exports = require('./index.cjs.dev.js'); | ||
} | ||
function _inheritsLoose(subClass, superClass) { | ||
subClass.prototype = Object.create(superClass.prototype); | ||
subClass.prototype.constructor = subClass; | ||
subClass.__proto__ = superClass; | ||
} | ||
// https://github.com/styled-components/styled-components/blob/e05b3fe247e9d956bcde786cec376e32afb85bca/src/utils/create-broadcast.js | ||
var createBroadcast = function createBroadcast(initialState) { | ||
var listeners = {}; | ||
var id = 0; | ||
var state = initialState; | ||
function publish(nextState) { | ||
state = nextState; | ||
for (var key in listeners) { | ||
// $FlowFixMe | ||
var listener = listeners[key]; | ||
if (listener === undefined) { | ||
continue; | ||
} | ||
listener(state); | ||
} | ||
} | ||
function subscribe(listener) { | ||
var currentId = id; | ||
listeners[currentId] = listener; | ||
id += 1; | ||
listener(state); | ||
return currentId; | ||
} | ||
function unsubscribe(unsubID) { | ||
listeners[unsubID] = undefined; | ||
} | ||
return { | ||
publish: publish, | ||
subscribe: subscribe, | ||
unsubscribe: unsubscribe | ||
}; | ||
}; | ||
var channel = '__EMOTION_THEMING__'; | ||
var _contextTypes; | ||
var contextTypes = (_contextTypes = {}, _contextTypes[channel] = PropTypes.object, _contextTypes); | ||
var isPlainObject = function isPlainObject(test) { | ||
return Object.prototype.toString.call(test) === '[object Object]'; | ||
}; | ||
// Get the theme from the props, supporting both (outerTheme) => {} as well as object notation | ||
function getTheme(theme, outerTheme) { | ||
if (typeof theme === 'function') { | ||
var mergedTheme = theme(outerTheme); | ||
if (!isPlainObject(mergedTheme)) { | ||
throw new Error('[ThemeProvider] Please return an object from your theme function, i.e. theme={() => ({})}!'); | ||
} | ||
return mergedTheme; | ||
} | ||
if (!isPlainObject(theme)) { | ||
throw new Error('[ThemeProvider] Please make your theme prop a plain object'); | ||
} | ||
if (outerTheme === undefined) { | ||
return theme; | ||
} | ||
return _extends({}, outerTheme, theme); | ||
} | ||
var ThemeProvider = | ||
/*#__PURE__*/ | ||
function (_Component) { | ||
_inheritsLoose(ThemeProvider, _Component); | ||
function ThemeProvider() { | ||
return _Component.apply(this, arguments) || this; | ||
} | ||
var _proto = ThemeProvider.prototype; | ||
_proto.componentWillMount = function componentWillMount() { | ||
var _this = this; | ||
// If there is a ThemeProvider wrapper anywhere around this theme provider, merge this theme | ||
// with the outer theme | ||
if (this.context[channel] !== undefined) { | ||
this.unsubscribeToOuterId = this.context[channel].subscribe(function (theme) { | ||
_this.outerTheme = theme; | ||
if (_this.broadcast !== undefined) { | ||
_this.publish(_this.props.theme); | ||
} | ||
}); | ||
} | ||
this.broadcast = createBroadcast(getTheme(this.props.theme, this.outerTheme)); | ||
}; | ||
_proto.getChildContext = function getChildContext() { | ||
var _ref; | ||
return _ref = {}, _ref[channel] = { | ||
subscribe: this.broadcast.subscribe, | ||
unsubscribe: this.broadcast.unsubscribe | ||
}, _ref; | ||
}; | ||
_proto.componentWillReceiveProps = function componentWillReceiveProps(nextProps) { | ||
if (this.props.theme !== nextProps.theme) { | ||
this.publish(nextProps.theme); | ||
} | ||
}; | ||
_proto.componentWillUnmount = function componentWillUnmount() { | ||
var themeContext = this.context[channel]; | ||
if (themeContext !== undefined) { | ||
themeContext.unsubscribe(this.unsubscribeToOuterId); | ||
} | ||
}; | ||
_proto.publish = function publish(theme) { | ||
this.broadcast.publish(getTheme(theme, this.outerTheme)); | ||
}; | ||
_proto.render = function render() { | ||
if (!this.props.children) { | ||
return null; | ||
} | ||
return React.Children.only(this.props.children); | ||
}; | ||
ThemeProvider.childContextTypes = contextTypes; | ||
ThemeProvider.contextTypes = contextTypes; | ||
return ThemeProvider; | ||
}(React.Component); | ||
var withTheme = function withTheme(Component) { | ||
var componentName = Component.displayName || Component.name || 'Component'; | ||
var WithTheme = | ||
/*#__PURE__*/ | ||
function (_React$Component) { | ||
_inheritsLoose(WithTheme, _React$Component); | ||
function WithTheme(props) { | ||
return _React$Component.call(this, props) || this; | ||
} | ||
var _proto = WithTheme.prototype; | ||
_proto.componentWillMount = function componentWillMount() { | ||
var _this = this; | ||
var themeContext = this.context[channel]; | ||
if (themeContext === undefined) { | ||
// eslint-disable-next-line no-console | ||
console.error('[withTheme] Please use ThemeProvider to be able to use withTheme'); | ||
return; | ||
} | ||
this.unsubscribeId = themeContext.subscribe(function (theme) { | ||
_this.setState({ | ||
theme: theme | ||
}); | ||
}); | ||
}; | ||
_proto.componentWillUnmount = function componentWillUnmount() { | ||
if (this.unsubscribeId !== -1) { | ||
this.context[channel].unsubscribe(this.unsubscribeId); | ||
} | ||
}; | ||
_proto.render = function render() { | ||
return React.createElement(Component, _extends({ | ||
theme: this.state.theme | ||
}, this.props)); | ||
}; | ||
return WithTheme; | ||
}(React.Component); | ||
WithTheme.displayName = "WithTheme(" + componentName + ")"; | ||
WithTheme.contextTypes = contextTypes; | ||
return hoistNonReactStatics(WithTheme, Component); | ||
}; | ||
exports.ThemeProvider = ThemeProvider; | ||
exports.withTheme = withTheme; | ||
exports.channel = channel; | ||
exports.contextTypes = contextTypes; | ||
exports.createBroadcast = createBroadcast; |
@@ -1,3 +0,4 @@ | ||
import PropTypes from 'prop-types'; | ||
import { Component, Children, createElement } from 'react'; | ||
import { createElement, forwardRef } from 'react'; | ||
import { ThemeContext } from '@emotion/core'; | ||
import weakMemoize from '@emotion/weak-memoize'; | ||
import hoistNonReactStatics from 'hoist-non-react-statics'; | ||
@@ -23,63 +24,7 @@ | ||
function _inheritsLoose(subClass, superClass) { | ||
subClass.prototype = Object.create(superClass.prototype); | ||
subClass.prototype.constructor = subClass; | ||
subClass.__proto__ = superClass; | ||
} | ||
// https://github.com/styled-components/styled-components/blob/e05b3fe247e9d956bcde786cec376e32afb85bca/src/utils/create-broadcast.js | ||
var createBroadcast = function createBroadcast(initialState) { | ||
var listeners = {}; | ||
var id = 0; | ||
var state = initialState; | ||
function publish(nextState) { | ||
state = nextState; | ||
for (var key in listeners) { | ||
// $FlowFixMe | ||
var listener = listeners[key]; | ||
if (listener === undefined) { | ||
continue; | ||
} | ||
listener(state); | ||
} | ||
} | ||
function subscribe(listener) { | ||
var currentId = id; | ||
listeners[currentId] = listener; | ||
id += 1; | ||
listener(state); | ||
return currentId; | ||
} | ||
function unsubscribe(unsubID) { | ||
listeners[unsubID] = undefined; | ||
} | ||
return { | ||
publish: publish, | ||
subscribe: subscribe, | ||
unsubscribe: unsubscribe | ||
}; | ||
}; | ||
var channel = '__EMOTION_THEMING__'; | ||
var _contextTypes; | ||
var contextTypes = (_contextTypes = {}, _contextTypes[channel] = PropTypes.object, _contextTypes); | ||
var isPlainObject = function isPlainObject(test) { | ||
return Object.prototype.toString.call(test) === '[object Object]'; | ||
}; | ||
// Get the theme from the props, supporting both (outerTheme) => {} as well as object notation | ||
function getTheme(theme, outerTheme) { | ||
var getTheme = function getTheme(outerTheme, theme) { | ||
if (typeof theme === 'function') { | ||
var mergedTheme = theme(outerTheme); | ||
if (!isPlainObject(mergedTheme)) { | ||
if (process.env.NODE_ENV !== 'production' && (mergedTheme == null || typeof mergedTheme !== 'object' || Array.isArray(mergedTheme))) { | ||
throw new Error('[ThemeProvider] Please return an object from your theme function, i.e. theme={() => ({})}!'); | ||
@@ -91,134 +36,46 @@ } | ||
if (!isPlainObject(theme)) { | ||
if (process.env.NODE_ENV !== 'production' && (theme == null || typeof theme !== 'object' || Array.isArray(theme))) { | ||
throw new Error('[ThemeProvider] Please make your theme prop a plain object'); | ||
} | ||
if (outerTheme === undefined) { | ||
return theme; | ||
} | ||
return _extends({}, outerTheme, theme); | ||
} | ||
}; | ||
var ThemeProvider = | ||
/*#__PURE__*/ | ||
function (_Component) { | ||
_inheritsLoose(ThemeProvider, _Component); | ||
var createCacheWithTheme = weakMemoize(function (outerTheme) { | ||
return weakMemoize(function (theme) { | ||
return getTheme(outerTheme, theme); | ||
}); | ||
}); | ||
function ThemeProvider() { | ||
return _Component.apply(this, arguments) || this; | ||
} | ||
var _proto = ThemeProvider.prototype; | ||
_proto.componentWillMount = function componentWillMount() { | ||
var _this = this; | ||
// If there is a ThemeProvider wrapper anywhere around this theme provider, merge this theme | ||
// with the outer theme | ||
if (this.context[channel] !== undefined) { | ||
this.unsubscribeToOuterId = this.context[channel].subscribe(function (theme) { | ||
_this.outerTheme = theme; | ||
if (_this.broadcast !== undefined) { | ||
_this.publish(_this.props.theme); | ||
} | ||
}); | ||
var ThemeProvider = function ThemeProvider(props) { | ||
return createElement(ThemeContext.Consumer, null, function (theme) { | ||
if (props.theme !== theme) { | ||
theme = createCacheWithTheme(theme)(props.theme); | ||
} | ||
this.broadcast = createBroadcast(getTheme(this.props.theme, this.outerTheme)); | ||
}; | ||
return createElement(ThemeContext.Provider, { | ||
value: theme | ||
}, props.children); | ||
}); | ||
}; | ||
_proto.getChildContext = function getChildContext() { | ||
var _ref; | ||
// should we change this to be forwardRef/withCSSContext style so it doesn't merge with props? | ||
var withTheme = function withTheme(Component) { | ||
var componentName = Component.displayName || Component.name || 'Component'; | ||
return _ref = {}, _ref[channel] = { | ||
subscribe: this.broadcast.subscribe, | ||
unsubscribe: this.broadcast.unsubscribe | ||
}, _ref; | ||
}; | ||
var render = function render(props, ref) { | ||
return createElement(ThemeContext.Consumer, null, function (theme) { | ||
return createElement(Component, _extends({ | ||
theme: theme, | ||
ref: ref | ||
}, props)); | ||
}); | ||
}; // $FlowFixMe | ||
_proto.componentWillReceiveProps = function componentWillReceiveProps(nextProps) { | ||
if (this.props.theme !== nextProps.theme) { | ||
this.publish(nextProps.theme); | ||
} | ||
}; | ||
_proto.componentWillUnmount = function componentWillUnmount() { | ||
var themeContext = this.context[channel]; | ||
if (themeContext !== undefined) { | ||
themeContext.unsubscribe(this.unsubscribeToOuterId); | ||
} | ||
}; | ||
_proto.publish = function publish(theme) { | ||
this.broadcast.publish(getTheme(theme, this.outerTheme)); | ||
}; | ||
_proto.render = function render() { | ||
if (!this.props.children) { | ||
return null; | ||
} | ||
return Children.only(this.props.children); | ||
}; | ||
ThemeProvider.childContextTypes = contextTypes; | ||
ThemeProvider.contextTypes = contextTypes; | ||
return ThemeProvider; | ||
}(Component); | ||
var withTheme = function withTheme(Component$$1) { | ||
var componentName = Component$$1.displayName || Component$$1.name || 'Component'; | ||
var WithTheme = | ||
/*#__PURE__*/ | ||
function (_React$Component) { | ||
_inheritsLoose(WithTheme, _React$Component); | ||
function WithTheme(props) { | ||
return _React$Component.call(this, props) || this; | ||
} | ||
var _proto = WithTheme.prototype; | ||
_proto.componentWillMount = function componentWillMount() { | ||
var _this = this; | ||
var themeContext = this.context[channel]; | ||
if (themeContext === undefined) { | ||
// eslint-disable-next-line no-console | ||
console.error('[withTheme] Please use ThemeProvider to be able to use withTheme'); | ||
return; | ||
} | ||
this.unsubscribeId = themeContext.subscribe(function (theme) { | ||
_this.setState({ | ||
theme: theme | ||
}); | ||
}); | ||
}; | ||
_proto.componentWillUnmount = function componentWillUnmount() { | ||
if (this.unsubscribeId !== -1) { | ||
this.context[channel].unsubscribe(this.unsubscribeId); | ||
} | ||
}; | ||
_proto.render = function render() { | ||
return createElement(Component$$1, _extends({ | ||
theme: this.state.theme | ||
}, this.props)); | ||
}; | ||
return WithTheme; | ||
}(Component); | ||
var WithTheme = forwardRef(render); | ||
WithTheme.displayName = "WithTheme(" + componentName + ")"; | ||
WithTheme.contextTypes = contextTypes; | ||
return hoistNonReactStatics(WithTheme, Component$$1); | ||
return hoistNonReactStatics(WithTheme, Component); | ||
}; | ||
export { ThemeProvider, withTheme, channel, contextTypes, createBroadcast }; | ||
export { ThemeProvider, withTheme }; |
{ | ||
"name": "emotion-theming", | ||
"version": "9.2.9", | ||
"version": "10.0.0-beta.0", | ||
"description": "A CSS-in-JS theming solution, inspired by styled-components", | ||
@@ -35,14 +35,21 @@ "main": "dist/index.cjs.js", | ||
"devDependencies": { | ||
"@types/react": "16.0.16", | ||
"dtslint": "^0.3.0", | ||
"prop-types": "^15.6.1" | ||
"@types/react": "16.3.18", | ||
"dtslint": "^0.3.0" | ||
}, | ||
"dependencies": { | ||
"@emotion/weak-memoize": "^0.2.0", | ||
"hoist-non-react-statics": "^2.3.1" | ||
}, | ||
"peerDependencies": { | ||
"prop-types": "15.x", | ||
"@emotion/core": "0.x.x", | ||
"react": "15.x || 16.x" | ||
}, | ||
"umd:main": "./dist/emotion.umd.min.js" | ||
"umd:main": "./dist/emotion.umd.min.js", | ||
"publishConfig": { | ||
"tag": "next" | ||
}, | ||
"browser": { | ||
"./dist/index.cjs.js": "./dist/index.browser.cjs.js", | ||
"./dist/index.esm.js": "./dist/index.browser.esm.js" | ||
} | ||
} |
104
README.md
# emotion-theming | ||
> A CSS-in-JS theming solution for React(-like) views. | ||
> A CSS-in-JS theming solution for React | ||
@@ -9,10 +9,9 @@ _`emotion-theming` is a theming library inspired by [styled-components](https://github.com/styled-components/styled-components)_ | ||
* [Install](#install) | ||
* [Usage](#usage) | ||
* [API](#api) | ||
* [ThemeProvider](#themeprovider) | ||
* [withTheme](#withthemecomponent) | ||
* [channel](#channel) | ||
* [Credits](#credits) | ||
* [License](#license) | ||
- [Install](#install) | ||
- [Usage](#usage) | ||
- [API](#api) | ||
- [ThemeProvider](#themeprovider) | ||
- [withTheme](#withthemecomponent) | ||
- [Credits](#credits) | ||
- [License](#license) | ||
@@ -31,5 +30,7 @@ ## Install | ||
### TODO: Add example with the css prop | ||
Theming is accomplished by placing the `ThemeProvider` component, at the top of the React component tree and wrapping descendants with the `withTheme` higher-order component (HOC). This HOC seamlessly acquires the current theme and injects it as a "prop" into your own component. | ||
The theme prop is automatically injected into components created with `react-emotion`'s `styled`. | ||
The theme prop is automatically injected into components created with `styled`. | ||
@@ -40,4 +41,4 @@ Here is a complete example for a typical React + Emotion app (information about each piece of the theming API is listed afterward): | ||
/** child.js */ | ||
import React from 'react'; | ||
import styled from 'react-emotion'; | ||
import React from 'react' | ||
import styled from 'react-emotion' | ||
@@ -54,11 +55,9 @@ const Container = styled.div` | ||
export default Page extends React.Component { | ||
export default class Page extends React.Component { | ||
render() { | ||
return ( | ||
<Container> | ||
<Headline> | ||
I'm red! | ||
</Headline> | ||
<Headline>I'm red!</Headline> | ||
</Container> | ||
); | ||
) | ||
} | ||
@@ -68,11 +67,11 @@ } | ||
/** parent.js */ | ||
import React from 'react'; | ||
import ReactDOM from 'react-dom'; | ||
import { ThemeProvider } from 'emotion-theming'; | ||
import React from 'react' | ||
import ReactDOM from 'react-dom' | ||
import { ThemeProvider } from 'emotion-theming' | ||
import Page from './child.js'; | ||
import Page from './child.js' | ||
const theme = { | ||
color: 'red', | ||
}; | ||
color: 'red' | ||
} | ||
@@ -85,3 +84,3 @@ class App extends React.Component { | ||
</ThemeProvider> | ||
); | ||
) | ||
} | ||
@@ -91,3 +90,3 @@ } | ||
// this assumes the HTML page template has a <main> element already inside <body> | ||
ReactDOM.render(<App />, document.querySelector('main')); | ||
ReactDOM.render(<App />, document.querySelector('main')) | ||
``` | ||
@@ -99,10 +98,10 @@ | ||
### ThemeProvider: ReactComponent | ||
### ThemeProvider: React.ComponentType | ||
A React component that passes the theme object down the component tree via [context](https://facebook.github.io/react/docs/context.html). Additional `<ThemeProvider>` wrappers may be added deeper in the hierarchy to override the original theme. The theme object will be merged into its ancestor as if by [`Object.assign`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign). If a function is passed instead of an object it will be called with the ancestor theme and the result will be the new theme. | ||
A React component that passes the theme object down the component tree via [context](https://reactjs.org/docs/context.html). Additional `<ThemeProvider>` wrappers may be added deeper in the hierarchy to override the original theme. The theme object will be merged into its ancestor as if by [`Object.assign`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign). If a function is passed instead of an object it will be called with the ancestor theme and the result will be the new theme. | ||
_Accepts:_ | ||
* **`children`: ReactComponent** - A single React component. | ||
* **`theme`: Object|Function** - An object or function that provides an object. | ||
- **`children`: ReactComponent** - A single React component. | ||
- **`theme`: Object|Function** - An object or function that provides an object. | ||
@@ -144,3 +143,3 @@ ```jsx | ||
### withTheme(component: ReactComponent): Function | ||
### withTheme(component: React.ComponentType): React.ComponentType | ||
@@ -169,47 +168,2 @@ A higher-order component that provides the current theme as a prop to the wrapped child and listens for changes. If the theme is updated, the child component will be re-rendered accordingly. | ||
### channel: String | ||
The emotion-theming package uses this string as the React context key by default. | ||
If you wish to build your own components on top of this library, it is recommended to import the context key from this package instead of hardcoding its value. | ||
```js | ||
import { channel } from 'emotion-theming' | ||
console.log(channel) | ||
;('__EMOTION_THEMING__') | ||
``` | ||
### createBroadcast: Function | ||
Creates a broadcast that is used to listen to theme events via context. This is probably only useful for testing. | ||
If you have styled components that depend on `theme` via `ThemeProvider`, one option is to wrap all your components being tested | ||
in `ThemeProvider`. However if you're using `enzyme`, you'll lose the ability to call some methods that require it to be run on the root instance. | ||
Instead you can `mount` a component a pass the channel and broadcast as part of `context`. | ||
```js | ||
import { channel, createBroadcast } from 'emotion-theming' | ||
import { mount } from 'enzyme' | ||
import PropTypes from 'prop-types' | ||
import React from 'react' | ||
describe('emotion-theming', function() { | ||
it('can use theme from a ThemeProvider', function() { | ||
const myTheme = { color: 'green' } | ||
const broadcast = createBroadcast(myTheme) | ||
const wrapper = mount(<MyComponent />, { | ||
context: { | ||
[channel]: broadcast | ||
}, | ||
childContextTypes: { | ||
[channel]: PropTypes.object | ||
} | ||
}) | ||
wrapper.setState({ foo: 'bar' }) | ||
expect(wrapper.state('foo')).toBe('bar') | ||
}) | ||
}) | ||
``` | ||
## Credits | ||
@@ -216,0 +170,0 @@ |
// @flow | ||
export { default as ThemeProvider } from './theme-provider' | ||
export { default as withTheme } from './with-theme' | ||
export { channel, contextTypes, createBroadcast } from './utils' |
// @flow | ||
// adapted from styled-components' ThemeProvider | ||
// https://github.com/styled-components/styled-components/blob/4503cab5b86aa9ef8314c5baa360a2fbb4812485/src/models/ThemeProvider.js | ||
import * as React from 'react' | ||
import { ThemeContext } from '@emotion/core' | ||
import weakMemoize from '@emotion/weak-memoize' | ||
import { Component, Children, type Node as ReactNode } from 'react' | ||
import createBroadcast from './create-broadcast' | ||
import { channel, contextTypes, type Theme } from './utils' | ||
const isPlainObject = test => | ||
Object.prototype.toString.call(test) === '[object Object]' | ||
type Props = { | ||
theme: Theme, | ||
children: ReactNode | ||
} | ||
// Get the theme from the props, supporting both (outerTheme) => {} as well as object notation | ||
function getTheme(theme: Theme, outerTheme?: Object) { | ||
let getTheme = (outerTheme: Object, theme: Object | (Object => Object)) => { | ||
if (typeof theme === 'function') { | ||
const mergedTheme = theme(outerTheme) | ||
if (!isPlainObject(mergedTheme)) { | ||
if ( | ||
process.env.NODE_ENV !== 'production' && | ||
(mergedTheme == null || | ||
typeof mergedTheme !== 'object' || | ||
Array.isArray(mergedTheme)) | ||
) { | ||
throw new Error( | ||
@@ -28,3 +21,6 @@ '[ThemeProvider] Please return an object from your theme function, i.e. theme={() => ({})}!' | ||
} | ||
if (!isPlainObject(theme)) { | ||
if ( | ||
process.env.NODE_ENV !== 'production' && | ||
(theme == null || typeof theme !== 'object' || Array.isArray(theme)) | ||
) { | ||
throw new Error( | ||
@@ -35,68 +31,33 @@ '[ThemeProvider] Please make your theme prop a plain object' | ||
if (outerTheme === undefined) { | ||
return theme | ||
} | ||
return { ...outerTheme, ...theme } | ||
} | ||
class ThemeProvider extends Component<Props> { | ||
outerTheme: Object | ||
broadcast: * | ||
unsubscribeToOuterId: number | ||
componentWillMount() { | ||
// If there is a ThemeProvider wrapper anywhere around this theme provider, merge this theme | ||
// with the outer theme | ||
if (this.context[channel] !== undefined) { | ||
this.unsubscribeToOuterId = this.context[channel].subscribe(theme => { | ||
this.outerTheme = theme | ||
let createCacheWithTheme = weakMemoize(outerTheme => { | ||
return weakMemoize(theme => { | ||
return getTheme(outerTheme, theme) | ||
}) | ||
}) | ||
if (this.broadcast !== undefined) { | ||
this.publish(this.props.theme) | ||
type Props = { | ||
theme: Object | (Object => Object), | ||
children: React.Node | ||
} | ||
let ThemeProvider = (props: Props) => { | ||
return ( | ||
<ThemeContext.Consumer> | ||
{theme => { | ||
if (props.theme !== theme) { | ||
theme = createCacheWithTheme(theme)(props.theme) | ||
} | ||
}) | ||
} | ||
this.broadcast = createBroadcast( | ||
getTheme(this.props.theme, this.outerTheme) | ||
) | ||
} | ||
getChildContext() { | ||
return { | ||
[channel]: { | ||
subscribe: this.broadcast.subscribe, | ||
unsubscribe: this.broadcast.unsubscribe | ||
} | ||
} | ||
} | ||
componentWillReceiveProps(nextProps: Props) { | ||
if (this.props.theme !== nextProps.theme) { | ||
this.publish(nextProps.theme) | ||
} | ||
} | ||
componentWillUnmount() { | ||
const themeContext = this.context[channel] | ||
if (themeContext !== undefined) { | ||
themeContext.unsubscribe(this.unsubscribeToOuterId) | ||
} | ||
} | ||
publish(theme: Theme) { | ||
this.broadcast.publish(getTheme(theme, this.outerTheme)) | ||
} | ||
render() { | ||
if (!this.props.children) { | ||
return null | ||
} | ||
return Children.only(this.props.children) | ||
} | ||
return ( | ||
<ThemeContext.Provider value={theme}> | ||
{props.children} | ||
</ThemeContext.Provider> | ||
) | ||
}} | ||
</ThemeContext.Consumer> | ||
) | ||
} | ||
ThemeProvider.childContextTypes = contextTypes | ||
ThemeProvider.contextTypes = contextTypes | ||
export default ThemeProvider |
// @flow | ||
import * as React from 'react' | ||
import hoistNonReactStatics from 'hoist-non-react-statics' | ||
import { channel, contextTypes } from './utils' | ||
import { ThemeContext } from '@emotion/core' | ||
type Props = { theme: Object } | ||
// should we change this to be forwardRef/withCSSContext style so it doesn't merge with props? | ||
const withTheme = (Component: React.ComponentType<Props>) => { | ||
const componentName = Component.displayName || Component.name || 'Component' | ||
let render = (props, ref) => { | ||
return ( | ||
<ThemeContext.Consumer> | ||
{theme => { | ||
return <Component theme={theme} ref={ref} {...props} /> | ||
}} | ||
</ThemeContext.Consumer> | ||
) | ||
} | ||
// $FlowFixMe | ||
let WithTheme = React.forwardRef(render) | ||
class WithTheme extends React.Component<{}, { theme: Object }> { | ||
unsubscribeId: number | ||
componentWillMount() { | ||
const themeContext = this.context[channel] | ||
if (themeContext === undefined) { | ||
// eslint-disable-next-line no-console | ||
console.error( | ||
'[withTheme] Please use ThemeProvider to be able to use withTheme' | ||
) | ||
return | ||
} | ||
this.unsubscribeId = themeContext.subscribe(theme => { | ||
this.setState({ theme }) | ||
}) | ||
} | ||
componentWillUnmount() { | ||
if (this.unsubscribeId !== -1) { | ||
this.context[channel].unsubscribe(this.unsubscribeId) | ||
} | ||
} | ||
render() { | ||
return <Component theme={this.state.theme} {...this.props} /> | ||
} | ||
} | ||
WithTheme.displayName = `WithTheme(${componentName})` | ||
WithTheme.contextTypes = contextTypes | ||
@@ -40,0 +26,0 @@ return hoistNonReactStatics(WithTheme, Component) |
@@ -1,34 +0,29 @@ | ||
// TypeScript Version: 2.3 | ||
import { ComponentClass, SFC } from "react"; | ||
// Definitions by: Junyoung Clare Jang <https://github.com/Ailrun> | ||
// TypeScript Version: 2.8 | ||
export type OptionalThemeProps<Props, Theme> = Props & { theme?: Theme }; | ||
import * as React from 'react' | ||
import { AddOptionalTo, PropsOf } from './helper' | ||
export interface ThemeProviderProps<Theme> { | ||
theme: Partial<Theme> | ((theme: Theme) => Theme); | ||
theme: Partial<Theme> | ((outerTheme: Theme) => Theme) | ||
} | ||
export type ThemeProviderComponent<Theme> = ComponentClass<ThemeProviderProps<Theme>>; | ||
export const ThemeProvider: ThemeProviderComponent<object>; | ||
export function ThemeProvider<Theme>( | ||
props: ThemeProviderProps<Theme> | ||
): React.ReactElement<any> | ||
/** | ||
* Inject theme into component | ||
* @todo Add more constraint to C so that | ||
* this function only accepts components with theme props. | ||
*/ | ||
// tslint:disable-next-line:no-unnecessary-generics | ||
export function withTheme<Props, Theme = {}>(component: ComponentClass<Props> | SFC<Props>): ComponentClass<OptionalThemeProps<Props, Theme>>; | ||
export function withTheme<C extends React.ComponentType<any>>( | ||
component: C | ||
): React.SFC<AddOptionalTo<PropsOf<C>, 'theme'>> | ||
export interface EmotionThemingModule<Theme> { | ||
ThemeProvider: ThemeProviderComponent<Theme>; | ||
withTheme<Props>(component: ComponentClass<Props> | SFC<Props>): ComponentClass<OptionalThemeProps<Props, Theme>>; | ||
export interface EmotionTheming<Theme> { | ||
ThemeProvider(props: ThemeProviderProps<Theme>): React.ReactElement<any> | ||
withTheme<C extends React.ComponentType<any>>( | ||
component: C | ||
): React.SFC<AddOptionalTo<PropsOf<C>, 'theme'>> | ||
} | ||
export const channel: "__EMOTION_THEMING__"; | ||
export type BroadcastListener<Theme = {}> = (state: Theme) => void; | ||
export interface Broadcast<Theme = {}> { | ||
publish(nextState: Theme): void; | ||
subscribe(listener: BroadcastListener<Theme>): number; | ||
unsubscribe(unsubID: number): void; | ||
} | ||
export function createBroadcast<Theme = {}>(initialState: Theme): Broadcast<Theme>; |
{ | ||
"extends": "dtslint/dtslint.json", | ||
"rules": { | ||
"no-relative-import-in-test": false | ||
"array-type": [true, "generic"], | ||
"semicolon": false, | ||
"whitespace": [ | ||
true, | ||
"check-branch", | ||
"check-decl", | ||
"check-operator", | ||
"check-module", | ||
"check-rest-spread", | ||
"check-type", | ||
"check-typecast", | ||
"check-type-operator", | ||
"check-preblock" | ||
], | ||
"no-unnecessary-generics": false | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
2
19
34405
4
536
2
166
12
1
+ Added@emotion/weak-memoize@^0.2.0
+ Added@emotion/cache@0.8.8(transitive)
+ Added@emotion/core@0.13.1(transitive)
+ Added@emotion/css@0.9.8(transitive)
+ Added@emotion/hash@0.6.6(transitive)
+ Added@emotion/memoize@0.6.6(transitive)
+ Added@emotion/serialize@0.9.1(transitive)
+ Added@emotion/sheet@0.8.1(transitive)
+ Added@emotion/stylis@0.7.1(transitive)
+ Added@emotion/unitless@0.6.7(transitive)
+ Added@emotion/utils@0.8.2(transitive)
+ Added@emotion/weak-memoize@0.2.5(transitive)