@asphalt-react/theme-provider
Version:
Theme provider for Asphalt React
9 lines (6 loc) • 2.26 kB
JavaScript
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { legacyVariables } from '@gojek/asphalt-web-tokens';
import { hashUtil } from '@asphalt-react/helper';
const createHash=async themeStr=>{if(!themeStr)return null;return (await hashUtil.hash(themeStr)).substr(0,8)};const WARN_MSG="ThemeProvider: Failed to apply theme.";class ThemeProvider extends Component{constructor(props){super(props);this.state={hash:null};if(props.theme){createHash(JSON.stringify(props.theme)).then(hash=>{this.setState({hash},this.mountStyles);}).catch(err=>{if(err){console.warn(WARN_MSG);}});}this.createStyleSheet=this.createStyleSheet.bind(this);this.generateWrapperClass=this.generateWrapperClass.bind(this);}componentWillUnmount(){this.unMountStyles();}componentDidUpdate({theme}){if(!this.props.theme)return;const prevThemeStr=JSON.stringify(theme);const newThemeStr=JSON.stringify(this.props.theme);if(prevThemeStr===newThemeStr)return;createHash(newThemeStr).then(hash=>{this.setState({hash},()=>{this.unMountStyles();this.mountStyles();});}).catch(err=>{if(err){console.warn(WARN_MSG);}});}mountStyles(){const sheet=this.createStyleSheet();const selector=`.${this.generateWrapperClass()}`;sheet.insertRule(this.createRule(selector,this.props.theme),0);}generateWrapperClass(){return `asphalt-theme-${this.state.hash}`}unMountStyles(){const styleNode=document.querySelector(`#asphalt-tokens-${this.state.hash}`);if(styleNode){styleNode.remove();}}createStyleSheet(){const styleNode=document.createElement("style");styleNode.type="text/css";styleNode.rel="stylesheet";styleNode.id=`asphalt-tokens-${this.state.hash}`;document.head.appendChild(styleNode);return styleNode.sheet}createRule(selector,variables){return `${selector} { ${this.createTokens(variables)} }`}createTokens(variables){return Object.keys(variables).map(key=>`${key}: ${variables[key]};`).join("")}render(){const{theme=legacyVariables,children,as:Tag="div"}=this.props;return theme?React.createElement(Tag,{className:this.generateWrapperClass()},this.props.children):children}}ThemeProvider.propTypes={children:PropTypes.node.isRequired,theme:PropTypes.object,as:PropTypes.elementType};ThemeProvider.defaultProps={theme:legacyVariables,as:"div"};
export { ThemeProvider };