var _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; }; import React from 'react'; import PropTypes from 'prop-types'; import createBroadcast from 'brcast'; import themeListener, { CHANNEL } from './themeListener'; import exactProp from '../utils/exactProp'; /** * This component takes a `theme` property. * It makes the `theme` available down the React tree thanks to React context. * This component should preferably be used at **the root of your component tree**. */ class MuiThemeProvider extends React.Component { constructor(props, context) { super(props, context); // Get the outer theme from the context, can be null this.broadcast = createBroadcast(); this.unsubscribeId = null; this.outerTheme = null; this.outerTheme = themeListener.initial(context); // Propagate the theme so it can be accessed by the children this.broadcast.setState(this.mergeOuterLocalTheme(this.props.theme)); } getChildContext() { return { [CHANNEL]: this.broadcast, muiThemeProviderOptions: { sheetsManager: this.props.sheetsManager, disableStylesGeneration: this.props.disableStylesGeneration } }; } componentDidMount() { // Subscribe on the outer theme, if present this.unsubscribeId = themeListener.subscribe(this.context, outerTheme => { this.outerTheme = outerTheme; // Forward the parent theme update to the children this.broadcast.setState(this.mergeOuterLocalTheme(this.props.theme)); }); } componentWillReceiveProps(nextProps) { // Propagate a local theme update if (this.props.theme !== nextProps.theme) { this.broadcast.setState(this.mergeOuterLocalTheme(nextProps.theme)); } } componentWillUnmount() { if (this.unsubscribeId !== null) { themeListener.unsubscribe(this.context, this.unsubscribeId); } } // We are not using the React state in order to avoid unnecessary rerender. // Simple merge between the outer theme and the local theme mergeOuterLocalTheme(localTheme) { // To support composition of theme. if (typeof localTheme === 'function') { return localTheme(this.outerTheme); } if (!this.outerTheme) { return localTheme; } return _extends({}, this.outerTheme, localTheme); } render() { return this.props.children; } } MuiThemeProvider.defaultProps = { disableStylesGeneration: false, sheetsManager: null }; MuiThemeProvider.propTypes = { /** * You can only provide a single element with react@15, a node with react@16. */ children: PropTypes.node.isRequired, /** * You can disable the generation of the styles with this option. * It can be useful when traversing the React tree outside of the HTML * rendering step on the server. * Let's say you are using react-apollo to extract all * the queries made by the interface server side. * You can significantly speed up the traversal with this property. */ disableStylesGeneration: PropTypes.bool, /** * The sheetsManager is used to deduplicate style sheet injection in the page. * It's deduplicating using the (theme, styles) couple. * On the server, you should provide a new instance for each request. */ sheetsManager: PropTypes.object, /** * A theme object. */ theme: PropTypes.oneOfType([PropTypes.object, PropTypes.func]).isRequired }; MuiThemeProvider.childContextTypes = _extends({}, themeListener.contextTypes, { muiThemeProviderOptions: PropTypes.object }); MuiThemeProvider.contextTypes = themeListener.contextTypes; // Add a wrapper component to generate some helper messages in the development // environment. // eslint-disable-next-line import/no-mutable-exports let MuiThemeProviderWrapper = MuiThemeProvider; if (process.env.NODE_ENV !== 'production') { MuiThemeProviderWrapper = props => React.createElement(MuiThemeProvider, props); MuiThemeProviderWrapper.propTypes = exactProp(MuiThemeProvider.propTypes, 'MuiThemeProvider'); } export default MuiThemeProviderWrapper;