nuke-tabbar
Version:
带导航切换的tab组件
254 lines (239 loc) • 8.23 kB
JSX
/** @jsx createElement */
import { createElement, cloneElement, isValidElement, Component } from 'rax';
import View from 'nuke-view';
import ScrollView from 'nuke-scroll-view';
import Dimensions from 'nuke-dimensions';
import { isWeb } from 'nuke-env';
import Item from './item';
import Content from './content';
import { connectStyle } from 'nuke-theme-provider';
import stylesProvider from '../styles';
// import wpo from './log/log.js';
const WIDTH = '750rem';
// 0 tabbar 完全渲染完毕
// 2/3 tabbar 切换tab渲染完毕(普通模式/embed模式)
// const rec = t => Date.now() - t;
class Tabbar extends Component {
static Item = Item;
constructor(props) {
super(props);
// wpo.setConfig({
// sample : 1,
// spmId : 'nuke.perf',
// request: stream.fetch
// });
// this.t = Date.now();
// 兼容代码,旧api为activeKey为string,新api为obj
this.keyObj = props.activeKey.key !== undefined;
this.state = {
selectedKey: this.getKey(props),
};
this._control = { switch: true };
this.asFramework = props.asContainer === false || props.asFramework;
}
getKey(thisProps) {
const props = thisProps || this.props;
return this.keyObj ? props.activeKey.key : props.activeKey;
}
handleTouchTap = (tabKey, needChangeState = true) => {
// this.t = Date.now()
needChangeState && this.props.onChange && this.props.onChange({ prev: this.getKey(), next: tabKey });
needChangeState && this.setState({ selectedKey: tabKey });
this.keyObj ? (this.props.activeKey.key = tabKey) : (this.props.activeKey = tabKey);
};
componentWillReceiveProps(nextProps) {
// this.t = Date.now()
// 屏蔽掉来自item press事件的state中activeKey的变更,render中将不处理,并且同步本地的activeKey值
if (!this._control.switch) {
this.keyObj ? (nextProps.activeKey.key = this.props.activeKey.key) : (nextProps.activeKey = this.props.activeKey);
} else {
// 主动change state后不会产生onchange
this.setState({
selectedKey: this.keyObj ? nextProps.activeKey.key : nextProps.activeKey,
});
}
}
getTabs() {
const tabs = [];
if (this.props.children) {
if (this.props.children.length) {
this.props.children.map((tab) => {
if (isValidElement(tab)) {
tabs.push(tab);
}
});
return tabs;
}
tabs.push(this.props.children);
}
return tabs;
}
getStyles = () => {
const { navTop = false, capsule = false, iconBar = false, themeStyle } = this.props;
const { itemStyle = {} } = this.props;
if (itemStyle.height) {
itemStyle.height = parseInt(itemStyle.height, 10);
}
if (itemStyle.width) {
itemStyle.width = parseInt(itemStyle.width, 10);
}
const barWrapperStyle = Object.assign(
{},
{ width: WIDTH, ...itemStyle, position: 'absolute' },
navTop || capsule ? { top: 0 } : { bottom: 0 }
);
let barHeight = themeStyle.navBar.height;
if (capsule) {
barHeight = themeStyle.navCapsule.height;
}
if (iconBar) {
barHeight = themeStyle['nav-item-has-icon'].height;
}
if (itemStyle.height) {
barHeight = `${itemStyle.height}rem`;
}
const barStyle = Object.assign(
{},
themeStyle.navBar,
{ backgroundColor: barWrapperStyle.backgroundColor || '#ffffff' },
iconBar ? themeStyle['nav-item-has-icon'] : {},
capsule ? themeStyle.navCapsule : { width: itemStyle.width || '750rem' },
{ height: barHeight }
// capsule ? { height: itemStyle.height || null }
);
return {
barWrapperStyle,
barStyle,
};
};
renderTabNav(tabs) {
const { itemStyle = {}, navTop = false, capsule = false, iconBar = false } = this.props;
/*
* 修改导航条及content为绝对定位,以避免在embed模式下weex渲染遮挡问题。
*/
const { barWrapperStyle, barStyle } = this.getStyles();
const tabDOM = tabs && tabs.length ? tabs : null;
if (this.props.navScrollable) {
return (
<View className="bar-wrapper" style={barWrapperStyle}>
<ScrollView
horizontal="true"
showsHorizontalScrollIndicator={false}
className="tab-nav"
style={[
barStyle,
isWeb
? {
flexBasis: 'auto',
}
: {},
]}
contentContainerStyle={isWeb ? { flexBasis: 'auto' } : {}}
>
{tabDOM}
</ScrollView>
</View>
);
}
return (
<View className="bar-wrapper" style={barWrapperStyle}>
<View className="tab-nav" style={barStyle}>
{tabs}
</View>
</View>
);
}
render() {
const tabContent = [];
const { barStyle } = this.getStyles();
const themeStyle = this.props.themeStyle;
const tabsList = this.getTabs();
let tabs = [];
if (tabsList.length > 0) {
tabs = tabsList.map((tab, index) => {
if (tab.props.children || tab.props.src) {
tabContent.push(
createElement(
Content,
{
src: tab.props.src || null,
asFramework: this.asFramework,
customMethod: {
focus: this.props.customFocus,
changeTo: this.props.customChange,
},
key: index,
webUrl: tab.props.webUrl || null,
tabKey: tab.props.tabKey,
selected: this.state.selectedKey === tab.props.tabKey,
handleTouchTap: this.handleTouchTap,
},
tab.props.children
)
);
}
return cloneElement(tab, {
_control: this._control,
asFramework: this.asFramework,
navStyle: this.props.navStyle,
index,
style: Object.assign({}, tab.props.style, { height: barStyle.height }),
tabKey: tab.props.tabKey,
capsule: this.props.capsule,
navTop: this.props.navTop,
iconBar: this.props.iconBar,
navScrollable: this.props.navScrollable,
selected: this.state.selectedKey === tab.props.tabKey,
selectedColor: this.props.selectedColor,
selectedIcon: tab.props.selectedIcon,
handleTouchTap: this.handleTouchTap,
});
});
}
const tabContainerStyle = Object.assign({}, themeStyle.tabContainer, this.props.style);
// var barStyle = Object.assign({},styles.navBar,this.props.itemStyle);
// if(this.props.iconBar){
// Object.assign(barStyle, styles['nav-item-has-icon'])
// }
const autoStyleHeight = Dimensions.get('window').height;
const tabContentStyle =
this.props.navTop || this.props.capsule
? Object.assign({}, themeStyle.tabContent, {
marginTop: barStyle.height,
})
: Object.assign({}, themeStyle.tabContent, {
marginBottom: barStyle.height,
});
// console.log('this.state.selectedKey', this.state.selectedKey);
const index = Number(this.state.selectedKey) || 0;
const contentStyle = this.props.contentStyle;
if (this.props.navTop || this.props.capsule) {
return (
<View className="tab-container" style={tabContainerStyle}>
{tabsList.length > 0 ? this.renderTabNav(tabs) : null}
{tabContent.length ? (
<View className="tab-content" style={[tabContentStyle, contentStyle]}>
{tabContent}
</View>
) : null}
</View>
);
}
return (
<View className="tab-container" style={tabContainerStyle}>
{tabContent.length ? (
<View className="tab-content" style={[tabContentStyle, contentStyle]}>
{tabContent.length ? tabContent : null}
</View>
) : null}
{tabsList.length > 0 ? this.renderTabNav(tabs) : null}
</View>
);
}
}
Tabbar.defaultProps = {
asFramework: true,
themeStyle: {},
};
Tabbar.displayName = 'Tabbar';
export default connectStyle(stylesProvider)(Tabbar);