277 lines
9.7 KiB
JavaScript
277 lines
9.7 KiB
JavaScript
import React, { Component } from 'react';
|
|
import { Text, View, StyleSheet, TouchableOpacity, ScrollView } from 'react-native';
|
|
import cascaderStyles from './styles';
|
|
const styles = StyleSheet.create(cascaderStyles);
|
|
import { Icon } from '../Icon';
|
|
import variables from '../../common/styles/variables';
|
|
import Tree from '../../common/utils/Tree';
|
|
export class Cascader extends Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.handlePress = (item, index) => {
|
|
const fieldKeys = this.getFieldKeys();
|
|
const { tree } = this.state;
|
|
if (item[fieldKeys.activeKey]) {
|
|
return;
|
|
}
|
|
if (item[fieldKeys.disabledKey]) {
|
|
return;
|
|
}
|
|
let tmpTree = this.resetActive(tree, item, fieldKeys);
|
|
tmpTree = Cascader.resetChecked(tmpTree, item, 'radio', true, fieldKeys);
|
|
const { checkedValue, checkedResult } = Cascader.getCheckedInfo(tmpTree, 'radio', fieldKeys);
|
|
const menu = this.getMenu(tmpTree, fieldKeys);
|
|
this.props.onChange && this.props.onChange(checkedValue, checkedResult);
|
|
this.setState({
|
|
tree: tmpTree,
|
|
menu,
|
|
});
|
|
};
|
|
this.state = {
|
|
...this.state,
|
|
...this.init(props)
|
|
};
|
|
}
|
|
static resetChecked(tree, item, checkType, checked, fieldKeys) {
|
|
let tmpTree = [
|
|
...tree,
|
|
];
|
|
if (checkType === 'checkbox') {
|
|
// TODO
|
|
}
|
|
if (checkType === 'radio') {
|
|
tmpTree = resetRadio(tmpTree, item, checked, fieldKeys);
|
|
}
|
|
return tmpTree;
|
|
function resetRadio(tree, item, checked, fieldKeys) {
|
|
let tmpTree = [
|
|
...tree
|
|
];
|
|
tmpTree = tmpTree.map((treeItem) => {
|
|
if (treeItem[fieldKeys.idKey] === item[fieldKeys.idKey]) {
|
|
return {
|
|
...treeItem,
|
|
[fieldKeys.checkedKey]: true
|
|
};
|
|
}
|
|
else {
|
|
return {
|
|
...treeItem,
|
|
[fieldKeys.checkedKey]: false
|
|
};
|
|
}
|
|
});
|
|
return tmpTree;
|
|
}
|
|
}
|
|
static getCheckedInfo(tree, checkType, fieldKeys) {
|
|
const checkedValue = [];
|
|
const checkedResult = [];
|
|
if (checkType === 'checkbox') {
|
|
// TODO
|
|
}
|
|
if (checkType === 'radio') {
|
|
tree.some((treeItem) => {
|
|
if (treeItem[fieldKeys.checkedKey]) {
|
|
checkedValue.push(treeItem[fieldKeys.idKey]);
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
checkedValue.forEach((valueItem, valueIndex) => {
|
|
const target = tree.filter((treeItem) => {
|
|
return treeItem[fieldKeys.idKey] === valueItem;
|
|
})[0];
|
|
const ancestors = Cascader.recursiveAncestors(tree, target, fieldKeys);
|
|
ancestors.push(target);
|
|
checkedResult[valueIndex] = ancestors;
|
|
});
|
|
return {
|
|
checkedValue,
|
|
checkedResult,
|
|
};
|
|
}
|
|
static recursiveAncestors(tree, item, fieldKeys, ret) {
|
|
ret = ret || [];
|
|
const parentItem = tree.filter((treeItem) => {
|
|
return treeItem[fieldKeys.idKey] === item[fieldKeys.pIdKey];
|
|
})[0];
|
|
if (parentItem) {
|
|
ret = ret.concat();
|
|
ret.unshift(parentItem);
|
|
return Cascader.recursiveAncestors(tree, parentItem, fieldKeys, ret);
|
|
}
|
|
else {
|
|
return ret;
|
|
}
|
|
}
|
|
init(props) {
|
|
const fieldKeys = this.getFieldKeys(props);
|
|
const { data, dataStructureType } = props;
|
|
const value = props.value || [];
|
|
let tree = new Tree({
|
|
type: dataStructureType,
|
|
...fieldKeys,
|
|
data: data
|
|
}).getData();
|
|
/**
|
|
* 重置 checked 状态
|
|
*/
|
|
value.forEach((valueItem) => {
|
|
const target = tree.filter((treeItem) => {
|
|
return treeItem[fieldKeys.idKey] === valueItem;
|
|
})[0];
|
|
if (!target) {
|
|
console.log(`值${valueItem}在数据集合中不存在`);
|
|
return;
|
|
}
|
|
tree = Cascader.resetChecked(tree, target, 'radio', true, fieldKeys);
|
|
});
|
|
/**
|
|
* 重置 active 状态
|
|
*/
|
|
let activeItem;
|
|
if (value[0] != null) {
|
|
activeItem = tree.filter((treeItem) => {
|
|
return treeItem[fieldKeys.idKey] === value[0];
|
|
})[0];
|
|
}
|
|
tree = this.resetActive(tree, activeItem, fieldKeys);
|
|
const menu = this.getMenu(tree, fieldKeys);
|
|
return {
|
|
tree,
|
|
menu,
|
|
};
|
|
}
|
|
resetActive(tree, activeItem, fieldKeys) {
|
|
if (!activeItem) {
|
|
return tree;
|
|
}
|
|
let tmpTree = [
|
|
...tree,
|
|
];
|
|
tmpTree = tmpTree.map((treeItem) => {
|
|
return {
|
|
...treeItem,
|
|
[fieldKeys.activeKey]: false
|
|
};
|
|
});
|
|
recursive(activeItem);
|
|
return tmpTree;
|
|
function recursive(activeItem) {
|
|
tmpTree = tmpTree.map((treeItem) => {
|
|
if (treeItem[fieldKeys.idKey] === activeItem[fieldKeys.idKey]) {
|
|
return {
|
|
...treeItem,
|
|
[fieldKeys.activeKey]: true
|
|
};
|
|
}
|
|
else {
|
|
return treeItem;
|
|
}
|
|
});
|
|
const parentItem = tmpTree.filter((treeItem) => {
|
|
return treeItem[fieldKeys.idKey] === activeItem[fieldKeys.pIdKey];
|
|
})[0];
|
|
if (parentItem) {
|
|
recursive(parentItem);
|
|
}
|
|
}
|
|
}
|
|
getMenu(tree, fieldKeys) {
|
|
const menu = [
|
|
tree.filter((treeItem) => {
|
|
return treeItem[fieldKeys.pIdKey] == null;
|
|
})
|
|
];
|
|
recursive(menu[0]);
|
|
return menu;
|
|
function recursive(list) {
|
|
list.some((item) => {
|
|
if (item[fieldKeys.activeKey]) {
|
|
const tmpList = tree.filter((treeItem) => {
|
|
return treeItem[fieldKeys.pIdKey] === item[fieldKeys.idKey];
|
|
});
|
|
if (tmpList && tmpList.length) {
|
|
menu.push(tmpList);
|
|
recursive(tmpList);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
}
|
|
getFieldKeys(props) {
|
|
props = props || this.props;
|
|
const { fieldKeys } = props;
|
|
return {
|
|
idKey: fieldKeys.idKey || 'id',
|
|
pIdKey: fieldKeys.pIdKey || 'pId',
|
|
labelKey: fieldKeys.labelKey || 'label',
|
|
childrenKey: fieldKeys.childrenKey || 'children',
|
|
activeKey: fieldKeys.activeKey || 'active',
|
|
checkedKey: fieldKeys.checkedKey || 'checked',
|
|
disabledKey: fieldKeys.disabledKey || 'disabled'
|
|
};
|
|
}
|
|
componentWillReceiveProps(nextProps) {
|
|
if (nextProps.value !== this.props.value ||
|
|
nextProps.data !== this.props.data) {
|
|
this.setState({
|
|
...this.init(nextProps)
|
|
});
|
|
}
|
|
}
|
|
componentDidMount() {
|
|
}
|
|
renderMenuItem(menuItem, menuIndex, menu) {
|
|
const { proportion } = this.props;
|
|
const style = {
|
|
flex: proportion[menuIndex] || 1,
|
|
borderRightColor: variables.mtdBorderColor,
|
|
borderRightWidth: menuIndex < menu.length - 1 ? StyleSheet.hairlineWidth : 0
|
|
};
|
|
return (React.createElement(View, { style: style, key: menuIndex },
|
|
React.createElement(ScrollView, null, menuItem.map((item, index) => {
|
|
return this.renderItem(item, index);
|
|
}))));
|
|
}
|
|
renderItem(item, index) {
|
|
const fieldKeys = this.getFieldKeys();
|
|
const { tree } = this.state;
|
|
const isLeafNode = this.props.isLeafNode
|
|
? this.props.isLeafNode(item)
|
|
: !(item && item[fieldKeys.childrenKey] && item[fieldKeys.childrenKey].length);
|
|
const active = item[fieldKeys.activeKey];
|
|
return (React.createElement(TouchableOpacity, { key: index, onPress: this.handlePress.bind(this, item, index) }, this.props.renderItem ? this.props.renderItem(item, index) :
|
|
React.createElement(View, { style: [
|
|
styles.item,
|
|
active ? { backgroundColor: variables.mtdFillGray } : {},
|
|
] },
|
|
React.createElement(Text, { style: [
|
|
styles.itemText,
|
|
active ? { color: variables.mtdBrandPrimaryDark, fontWeight: 'bold' } : {},
|
|
], ellipsizeMode: 'middle' }, item[fieldKeys.labelKey]),
|
|
!isLeafNode ? React.createElement(Icon, { source: require(`../../common/images/icons/angle-right.png`), size: variables.mtdFontSizeM, tintColor: variables.mtdGrayLighter }) : null)));
|
|
}
|
|
render() {
|
|
const { style } = this.props;
|
|
const { menu } = this.state;
|
|
return (React.createElement(View, { style: [styles.container, style] }, menu.map((item, index) => {
|
|
return this.renderMenuItem(item, index, menu);
|
|
})));
|
|
}
|
|
}
|
|
Cascader.displayName = 'Cascader';
|
|
Cascader.defaultProps = {
|
|
data: [],
|
|
dataStructureType: 'nested',
|
|
value: [],
|
|
fieldKeys: {},
|
|
proportion: [2, 1, 1],
|
|
onChange: null,
|
|
renderItem: null
|
|
};
|
|
//# sourceMappingURL=index.js.map
|