taro-material
Version:
Mini Program components that implement Google's Material Design.
200 lines (173 loc) • 6.4 kB
JavaScript
import Nerv from "nervjs";
import Taro from "@tarojs/taro-h5";
import PropTypes from 'prop-types';
import { View, ScrollView } from '@tarojs/components';
import classNames from 'classnames';
import AtComponent from "../common/component";
import AtToast from "../components/toast/index";
import { delayQuerySelector, uuid, initTestEnv, isTest } from "../common/utils";
import RMList from "../List/index";
import RMListItem from "../ListItem/index";
initTestEnv();
const ENV = Taro.getEnv();
class RMIndexes extends AtComponent {
constructor() {
super(...arguments);
this.state = {
_scrollIntoView: '',
_scrollTop: 0,
_tipText: '',
_isShowToast: false
};
// 右侧导航高度
this.menuHeight = 0;
// 右侧导航距离顶部高度
this.startTop = 0;
// 右侧导航元素高度
this.itemHeight = 0;
// 当前索引
this.currentIndex = -1;
this.listId = isTest() ? 'indexes-list-AOTU2018' : `list-${uuid()}`;
}
handleClick = (...arg) => this.props.onClick(...arg);
handleTouchMove = event => {
event.stopPropagation();
event.preventDefault();
const { list } = this.props;
const pageY = event.touches[0].pageY;
const index = Math.floor((pageY - this.startTop) / this.itemHeight);
if (index >= 0 && index <= list.length && this.currentIndex !== index) {
this.currentIndex = index;
const key = index > 0 ? list[index - 1].key : 'top';
const touchView = `at-indexes__list-${key}`;
this.jumpTarget(touchView, index);
}
};
handleTouchEnd = () => {
this.currentIndex = -1;
};
jumpTarget(_scrollIntoView, idx) {
const { topKey, list } = this.props;
const _tipText = idx === 0 ? topKey : list[idx - 1].key;
if (ENV === Taro.ENV_TYPE.WEB) {
delayQuerySelector(this, '.at-indexes', 0).then(rect => {
const targetOffsetTop = this.listRef.childNodes[idx].offsetTop;
const _scrollTop = targetOffsetTop - rect[0].top;
this.updateState({
_scrollTop,
_scrollIntoView,
_tipText
});
});
return;
}
this.updateState({
_scrollIntoView,
_tipText
});
}
updateState(state) {
const { isShowToast, isVibrate } = this.props;
const { _scrollIntoView, _tipText, _scrollTop } = state;
this.setState({
_scrollIntoView,
_tipText,
_scrollTop,
_isShowToast: isShowToast
});
if (isVibrate) {
Taro.vibrateShort();
}
}
initData() {
delayQuerySelector(this, '.at-indexes__menu').then(rect => {
const len = this.props.list.length;
this.menuHeight = rect[0].height;
this.startTop = rect[0].top;
this.itemHeight = Math.floor(this.menuHeight / (len + 1));
});
}
componentWillReceiveProps(nextProps) {
if (nextProps.list.length !== this.props.list.length) {
this.initData();
}
}
componentDidMount() {
if (ENV === Taro.ENV_TYPE.WEB) {
this.listRef = document.getElementById(this.listId);
}
this.initData();
}
render() {
const {
className,
customStyle,
animation,
topKey,
list,
ListProps,
ListItemProps
} = this.props;
const { _scrollTop, _scrollIntoView, _tipText, _isShowToast } = this.state;
const toastStyle = { minWidth: Taro.pxTransform(100) };
const rootCls = classNames('at-indexes', className);
const menuList = list.map((dataList, i) => {
const { key } = dataList;
const targetView = `at-indexes__list-${key}`;
return <View className="at-indexes__menu-item" key={key} onClick={this.jumpTarget.bind(this, targetView, i + 1)}>
{key}
</View>;
});
const indexesList = list.map(dataList => <View id={`at-indexes__list-${dataList.key}`} className="at-indexes__list" key={dataList.key}>
<View className="at-indexes__list-title">{dataList.title}</View>
<RMList hasBorder={ListProps.hasBorder} customStyle={ListProps.customStyle}>
{dataList.items && dataList.items.map((item, i) => <RMListItem key={item.id || item.title || item.name} title={item.title || item.name} onClick={this.handleClick.bind(this, item)} note={item.note} thumb={item.thumb} iconThumb={item.iconThumb} iconThumbFill={item.iconThumbFill} iconThumbColor={item.iconThumbColor} isSwitch={item.isSwitch} switchIsCheck={item.switchIsCheck} extraText={item.extraText} extraTextColor={item.extraTextColor} extraThumb={item.extraThumb} extraIconThumb={item.extraIconThumb} extraIconThumbFill={item.extraIconThumbFill} extraIconThumbColor={item.extraIconThumbColor} arrow={item.arrow} disabled={item.disabled} badge={item.badge} hasBorder={ListItemProps.hasBorder} customStyle={ListItemProps.customStyle} />)}
</RMList>
</View>);
return <View className={rootCls} style={customStyle}>
<AtToast customStyle={toastStyle} isOpened={_isShowToast} text={_tipText} duration={2000} />
<View className="at-indexes__menu" onTouchMove={this.handleTouchMove} onTouchEnd={this.handleTouchEnd}>
<View className="at-indexes__menu-item" onClick={this.jumpTarget.bind(this, 'at-indexes__top', 0)}>
{topKey}
</View>
{menuList}
</View>
<ScrollView className="at-indexes__body" id={this.listId} scrollY scrollWithAnimation={animation} scrollTop={_scrollTop} scrollIntoView={_scrollIntoView}>
<View className="at-indexes__content" id="at-indexes__top">
{this.props.children}
</View>
{indexesList}
</ScrollView>
</View>;
}
}
RMIndexes.propTypes = {
customStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
className: PropTypes.oneOfType([PropTypes.array, PropTypes.string]),
animation: PropTypes.bool,
isVibrate: PropTypes.bool,
isShowToast: PropTypes.bool,
topKey: PropTypes.string,
list: PropTypes.array,
onClick: PropTypes.func,
ListProps: PropTypes.shape({ hasBorder: PropTypes.bool }),
ListItemProps: PropTypes.shape({ hasBorder: PropTypes.bool })
};
RMIndexes.defaultProps = {
isTest: false,
customStyle: '',
className: '',
animation: false,
topKey: 'Top',
isVibrate: true,
isShowToast: true,
list: [],
onClick: () => {},
ListProps: {
hasBorder: false
},
ListItemProps: {
hasBorder: true
}
};
export default RMIndexes;