@uiw/react-native
Version:
UIW for React Native
139 lines (137 loc) • 3.98 kB
JavaScript
import React, { useMemo } from 'react';
import { usePropsValue } from '../utils/hooks';
import { getTreeDeep } from '../utils/tree-select';
import { View, Text, TouchableOpacity, ScrollView } from 'react-native';
import { style } from './styles';
const defaultProps = {
options: [],
fieldNames: {},
defaultValue: [],
activeColor: '#5847FF'
};
export const TreeSelect = p => {
const props = {
...defaultProps,
...p
};
const labelName = props.fieldNames.label || 'label';
const valueName = props.fieldNames.value || 'value';
const childrenName = props.fieldNames.children || 'children';
const [value, setValue] = usePropsValue({
value: props.value,
defaultValue: props.defaultValue
});
const [deep, optionsMap, optionsParentMap] = useMemo(() => {
const deep = getTreeDeep(props.options, childrenName);
const optionsMap = new Map();
const optionsParentMap = new Map();
function traverse(current, children) {
children.forEach(item => {
optionsParentMap.set(item[valueName], current);
optionsMap.set(item[valueName], item);
if (item[childrenName]) {
traverse(item, item[childrenName]);
}
});
}
traverse(undefined, props.options);
return [deep, optionsMap, optionsParentMap];
}, [props.options]);
const onItemSelect = node => {
// 找到父级节点
const parentNodes = [];
let current = node;
while (current) {
parentNodes.unshift(current);
const next = optionsParentMap.get(current[valueName]);
current = next;
}
const values = parentNodes.map(i => i[valueName]);
setValue(values);
props.onChange?.(values, {
options: parentNodes
});
};
// item样式
const activeStyles = (index, isActive, isLast) => {
let styles;
// 选中第一排
if (isActive && index === 0) {
styles = {
...style.active_first_item
};
}
// 未选中第一排
if (!isActive && index === 0) {
styles = {
...style.not_active_first_item
};
}
// 选中后排
if (isActive && index !== 0) {
styles = {
...style.active_nth_item,
borderColor: props.activeColor,
marginRight: isLast ? 10 : 0
};
}
// 未选中后排
if (!isActive && index !== 0) {
styles = {
...style.not_active_nth_item,
marginRight: isLast ? 10 : 0
};
}
return styles;
};
const renderItems = (columnOptions = [], index) => {
return columnOptions.map(item => {
const isActive = item[valueName] === value[index];
const active_font_color = index === 0 ? '#333' : props.activeColor;
// 是否是最后一列
const isLast = deep - 1 === index;
return <TouchableOpacity key={item[valueName]} onPress={() => {
if (!isActive) {
onItemSelect(item);
}
}} style={[style.item, {
...activeStyles(index, isActive, isLast)
}]}>
<Text style={isActive ? {
color: active_font_color,
fontWeight: 'bold'
} : {
color: '#666'
}}>
{item[labelName]}
</Text>
</TouchableOpacity>;
});
};
const renderColumns = () => {
const columns = [];
for (let i = 0; i < deep; i++) {
let width = `${100 / deep}%`;
// 两列的第一列宽度为 33.33,两列的第二列为 66.67%
if (deep === 2 && i === 0) {
width = `33.33%`;
}
if (deep === 2 && i === 1) {
width = `66.67%`;
}
const column = <ScrollView key={i} style={{
width,
flex: 1,
backgroundColor: i === 0 ? '#f6f7f9' : '#fff'
}}>
{renderItems(i === 0 ? props.options : optionsMap.get(value[i - 1])?.[childrenName], i)}
</ScrollView>;
columns.push(column);
}
return columns;
};
return <View style={{
flex: 1,
flexDirection: 'row'
}}>{renderColumns()}</View>;
};