UNPKG

nuke-theme-provider

Version:

主题换肤

202 lines (186 loc) 6.38 kB
/** @jsx createElement */ import { createElement, Component, PropTypes } from 'rax'; import hoistStatics from 'hoist-non-react-statics'; // import pickby from 'lodash.pickby'; import Theme from './theme'; // function throwConnectStyleError(errorMessage, componentDisplayName) { // throw Error( // `${errorMessage} - when connecting ${componentDisplayName} component to style.` // ); // } // 处理 componentStyle 有多层的情况,例如: // componentStyle: { // Card : { // normal:{ // width:100, // height:100 // }, // footer:{ // flex:1 // } // } , // [Card.Item]:{ // wrap:{ // backgroundColor:'#cccccc' // } // }, // [Card.Item.Button]:{ // primary:{ // backgroundColor:'#cccccc' // } // } // } let cacheStatic; function getTheme(context) { if (context.theme) { cacheStatic = context.theme; } // Fallback to a default theme if the component isn't // rendered in a StyleProvider. // return Theme.get(namespace) || Theme.getDefaultTheme(); // var a = Theme.get(); return context.theme || cacheStatic || Theme.getDefaultTheme(); } /** * 过滤,传递需要的样式到子节点 * @param {[object]} componentStyle [样式] * @param {[string]} componentDisplayName [组件名] * @param {[array]} parentPath [父级路径] * @return {[type]} [description] */ // function injectToChildFilter( // componentStyle, // componentDisplayName, // parentPath = [] // ) { // return pickby(componentStyle, (value, key) => { // let nextPath = [...parentPath]; // nextPath.push(componentDisplayName); // let maxCurrentStyleKey = nextPath.join('.'); // //当前 keyPath 是否是currentPath 的子路径, 例如 keyPath : Cart.Item.Button, currentPath: Cart.Item, return true // return ( // key.indexOf(maxCurrentStyleKey) === 0 && key.length > maxCurrentStyleKey // ); // }); // } /** * add display name property in each componeent to prevent being aliasd. */ export default (componentStyleProvider = {}, options = {}) => { function getComponentDisplayName(WrappedComponent) { return WrappedComponent.displayName || WrappedComponent.name || 'Component'; } return function wrapWithStyledComponent(WrappedComponent) { const componentDisplayName = getComponentDisplayName(WrappedComponent); class StyledComponent extends Component { static contextTypes = { theme: PropTypes.object, parentPath: PropTypes.array, namespace: PropTypes.string, }; static childContextTypes = { parentPath: PropTypes.array, parentStyle: PropTypes.object, namespace: PropTypes.string, }; static propTypes = { // Element style that overrides any other style of the component style: PropTypes.object, }; static WrappedComponent = WrappedComponent; constructor(props, context) { super(props, context); const style = props.style; const theme = getTheme(context); this.displayName = componentDisplayName; const componentStyle = componentStyleProvider(theme.themeStyle); this.setWrappedInstance = this.setWrappedInstance.bind(this); const finalStyle = this.getFinalStyle(componentStyle, context, style); this.state = { style: finalStyle, componentStyle, addedProps: this.resolveAddedProps(), }; } getChildContext() { return { // parentStyle: injectToChildFilter( // this.state.componentStyle, // this.displayName, // this.currentPath // ), namespace: this.getNamespace(), // this.context.parentStyle : // this.state.childrenStyle, // resolveStyle: this.resolveConnectedComponentStyle, parentPath: this.getParentPath(), }; } componentWillReceiveProps(nextProps, nextContext) { const theme = getTheme(nextContext); const componentStyle = componentStyleProvider(theme.themeStyle); const style = nextProps.style; const finalStyle = this.getFinalStyle(componentStyle, nextContext, style); this.setState({ style: finalStyle, }); } // getStyleFromKlsName(klassName) { // // console.log(klassName); // } getFinalStyle(componentStyle, context, style) { const currentPath = context.parentPath ? [...context.parentPath] : []; currentPath.push(this.displayName); let stylesObject = {}; for (let i = 0; i < currentPath.length; i++) { const key = currentPath.slice(currentPath.length - 1 - i).join('.'); if (componentStyle[key]) { stylesObject = { ...stylesObject, ...componentStyle[key] }; } } return { ...stylesObject, ...style }; } getParentPath() { if (!this.context.parentPath) { return [this.displayName]; } return [...this.context.parentPath, this.displayName]; } getNamespace() { return this.context.namespace || 'Nuke'; } setNativeProps(nativeProps) { if (this.wrappedInstance.setNativeProps) { this.wrappedInstance.setNativeProps(nativeProps); } } setWrappedInstance(component) { if (component && component._root) { this._root = component._root; } else { this._root = component; } this.wrappedInstance = this._root; this.wrappedInstance && Object.keys(this.wrappedInstance).forEach((attr) => { if (!this[attr]) { this[attr] = this.wrappedInstance[attr]; } }); } resolveAddedProps() { const addedProps = {}; if (options.withRef) { addedProps.ref = 'wrappedInstance'; } return addedProps; } render() { const { addedProps, style } = this.state; return <WrappedComponent {...this.props} {...addedProps} themeStyle={style} ref={this.setWrappedInstance} />; } } // return StyledComponent; return hoistStatics(StyledComponent, WrappedComponent); }; };