nuke-theme-provider
Version:
主题换肤
202 lines (186 loc) • 6.38 kB
JSX
/** @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);
};
};