import React, {useEffect, useRef, useState} from 'react'; import { View, Text, StyleSheet, TouchableOpacity, FlatList, TextInput, Keyboard } from 'react-native'; import {breadthFirstRecursion, filterSearchData} from '../utils/menutransform'; import Ionicons from 'react-native-vector-icons/Ionicons'; import {TreeSelectProps} from '../index'; import _ from 'lodash'; const TreeSelect = (props: TreeSelectProps) => { const { defaultSelectedId = [], selectType, isOpen = false, data, openIds = [], leafCanBeSelected, onClick, onClickLeaf, isShowTreeId = false, selectedItemStyle, itemStyle, treeNodeStyle, searchAble,lazySelectedId = [] } = props; const [nodesStatus, setNodeStatus] = useState>(); const [currentNode, setCurrentNode] = useState([]); const [searchValue, setSearchValue] = useState(''); const [treeData, setTreeData] = useState([]); const searchInputRef = useRef(null); useEffect(() => { setCurrentNode(initCurrentNode()); setNodeStatus(initNodesStatus()); if (props.data) { setTreeData(data); } }, [data]); useEffect(() => { if (lazySelectedId.length>0 && currentNode!==null &&lazySelectedId.indexOf(currentNode)<0 && nodesStatus !==undefined) { let newNodesStatus = _.cloneDeep(nodesStatus); newNodesStatus.set(currentNode, !(newNodesStatus.get(currentNode))); setNodeStatus(newNodesStatus); } }, [lazySelectedId]); const initCurrentNode = () => { if (selectType === 'multiple') { return defaultSelectedId || []; } return defaultSelectedId && defaultSelectedId[0] || null; }; const initNodesStatus = () => { const nodesStatus = new Map(); if (!isOpen) { if (openIds && openIds.length) { for (let id of openIds) { // eslint-disable-line const routes = _find(data, id); routes.map(parent => nodesStatus.set(parent.id, true)); } } // 设置默认选中时父节点的展开操作 if (defaultSelectedId && defaultSelectedId.length) { for (let id of defaultSelectedId) { // eslint-disable-line const routes = _find(data, id); routes.map(parent => nodesStatus.set(parent.id, true)); } } return nodesStatus; } breadthFirstRecursion(data).map(item => nodesStatus.set(item.id, true)); return nodesStatus; }; const _find = (data, id) => { const stack = []; let going = true; const walker = (childrenData, innerId) => { childrenData.forEach(item => { if (!going) { return; } stack.push({ id: item.id, name: item.name, parentId: item.parentId, }); if (item['id'] === innerId) { going = false; } else if (item['children']) { walker(item['children'], innerId); } else { stack.pop(); } }); if (going) { stack.pop(); } }; walker(data, id); return stack; }; const _onPressCollapse = ({e, item}) => { const routes = _find(data, item.id); let newNodesStatus = _.cloneDeep(nodesStatus); let finalCurrentNode; newNodesStatus.set(item && item.id, !nodesStatus.get(item && item.id)); // toggle // 计算currentNode的内容 if (selectType === 'multiple') { const tempCurrentNode = currentNode.includes(item.id) ? currentNode.filter(nodeid => nodeid !== item.id) : currentNode.concat(item.id); if (leafCanBeSelected) { setNodeStatus(newNodesStatus); } finalCurrentNode = tempCurrentNode; } else { if (leafCanBeSelected) { setNodeStatus(newNodesStatus); } finalCurrentNode = item.id; } setCurrentNode(finalCurrentNode); setNodeStatus(newNodesStatus); if (onClick && typeof onClick === 'function') { onClick({ item, routes, currentNode: finalCurrentNode, }); } }; const _onClickLeaf = ({e, item}) => { // eslint-disable-line const routes = _find(data, item.id); let finalCurrentNode; if (selectType === 'multiple') { const tempCurrentNode = currentNode.includes(item.id) ? currentNode.filter(nodeid => nodeid !== item.id) : currentNode.concat(item.id); finalCurrentNode = tempCurrentNode; } else { finalCurrentNode = item.id; } setCurrentNode(finalCurrentNode); if (onClick && typeof onClick === 'function') { onClick({ item, routes, currentNode: finalCurrentNode, }); } if (onClickLeaf && typeof onClickLeaf === 'function') { onClickLeaf && onClickLeaf({ item, routes, currentNode: finalCurrentNode, }); } }; const renderTreeNodeIcon = (isOpen) => { const collapseIcon = isOpen ? { borderRightWidth: 5, borderRightColor: 'transparent', borderLeftWidth: 5, borderLeftColor: 'transparent', borderTopWidth: 10, borderTopColor: 'black', } : { borderBottomWidth: 5, borderBottomColor: 'transparent', borderTopWidth: 5, borderTopColor: 'transparent', borderLeftWidth: 10, borderLeftColor: 'black', }; const openIcon = treeNodeStyle && treeNodeStyle.openIcon; const closeIcon = treeNodeStyle && treeNodeStyle.closeIcon; return openIcon && closeIcon ? {isOpen ? openIcon : closeIcon} : ; }; const renderRow = ({item}) => { const {backgroudColor, fontSize, color} = itemStyle(item) && itemStyle(item); const openIcon = treeNodeStyle && treeNodeStyle.openIcon; const closeIcon = treeNodeStyle && treeNodeStyle.closeIcon; const selectedBackgroudColor = selectedItemStyle(item) && selectedItemStyle(item).backgroudColor; const selectedFontSize = selectedItemStyle(item) && selectedItemStyle(item).fontSize; const selectedColor = selectedItemStyle(item) && selectedItemStyle(item).color; const isCurrentNode = selectType === 'multiple' ? currentNode.includes(item.id) : (currentNode === item.id); if (item && item.children && item.children.length) { const isOpen = nodesStatus && nodesStatus.get(item && item.id) || false; return ( _onPressCollapse({e, item})}> {renderTreeNodeIcon(isOpen)} { isShowTreeId && {item.id} } {item.name} { !isOpen ? null : i.toString()} style={{flex: 1, marginLeft: 15}} onEndReachedThreshold={0.01} {...props} getItemLayout={(data, index) => ( {length: 30, offset: 30 * index, index} )} data={item.children} extraData={{nodesStatus, currentNode}} renderItem={renderRow} /> } ); } return ( _onClickLeaf({e, item})}> {item.name} ); }; const onSearch = () => { searchInputRef.current.blur(); Keyboard.dismiss(); let defaultData = _.cloneDeep(data); let result = filterSearchData(defaultData, searchValue); setTreeData(result); }; const renderSearchBar = () => { return ( setSearchValue(text)} ref={searchInputRef} /> ); }; return ( { searchAble ? renderSearchBar() : null } i.toString()} style={{ flex: 1, marginVertical: 5, paddingHorizontal: 15, }} onEndReachedThreshold={0.01} getItemLayout={(data, index) => ( {length: 30, offset: 30 * index, index} )} {...props} data={treeData} extraData={{currentNode, nodesStatus}} renderItem={renderRow} /> ); }; export default TreeSelect; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', }, textName: { fontSize: 14, marginLeft: 5, }, contentContainer: { paddingBottom: 20, backgroundColor: 'white', }, collapseIcon: { width: 0, height: 0, marginRight: 2, borderStyle: 'solid', }, });