taro-material
Version:
Mini Program components that implement Google's Material Design.
249 lines (221 loc) • 7 kB
JavaScript
import Taro from '@tarojs/taro';
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;