import React from 'react'; import Overlay from '../../components/Overlay'; import PopOver from '../../components/PopOver'; import { OptionsControl, OptionsControlProps, Option, FormOptionsControl } from './Options'; import {Icon} from '../../components/icons'; import TreeSelector from '../../components/Tree'; // @ts-ignore import matchSorter from 'match-sorter'; import debouce from 'lodash/debounce'; import find from 'lodash/find'; import {Api} from '../../types'; import {isEffectiveApi} from '../../utils/api'; import Spinner from '../../components/Spinner'; import ResultBox from '../../components/ResultBox'; import {autobind, getTreeAncestors} from '../../utils/helper'; import {findDOMNode} from 'react-dom'; /** * Tree 下拉选择框。 * 文档:https://baidu.gitee.io/amis/docs/components/form/tree */ export interface TreeSelectControlSchema extends FormOptionsControl { type: 'tree-select'; /** * 是否隐藏顶级 */ hideRoot?: boolean; /** * 顶级选项的名称 */ rootLabel?: string; /** * 顶级选项的值 */ rootValue?: any; /** * 显示图标 */ showIcon?: boolean; /** * 父子之间是否完全独立。 */ cascade?: boolean; /** * 选父级的时候是否把子节点的值也包含在内。 */ withChildren?: boolean; /** * 选父级的时候,是否只把子节点的值包含在内 */ onlyChildren?: boolean; /** * 顶级节点是否可以创建子节点 */ rootCreatable?: boolean; } export interface TreeSelectProps extends OptionsControlProps { placeholder?: any; autoComplete?: Api; } export interface TreeSelectState { isOpened: boolean; isFocused: boolean; inputValue: string; } export default class TreeSelectControl extends React.Component< TreeSelectProps, TreeSelectState > { static defaultProps = { placeholder: 'Select.placeholder', optionsPlaceholder: 'placeholder.noData', multiple: false, clearable: true, rootLabel: '顶级', rootValue: '', showIcon: true, joinValues: true, extractValue: false, delimiter: ',', resetValue: '' }; container: React.RefObject = React.createRef(); input: React.RefObject = React.createRef(); cache: { [propName: string]: any; } = {}; target: HTMLElement | null; targetRef = (ref: any) => (this.target = ref ? (findDOMNode(ref) as HTMLElement) : null); constructor(props: TreeSelectProps) { super(props); this.state = { inputValue: '', isOpened: false, isFocused: false }; this.open = this.open.bind(this); this.close = this.close.bind(this); this.handleChange = this.handleChange.bind(this); this.clearValue = this.clearValue.bind(this); this.handleFocus = this.handleFocus.bind(this); this.handleBlur = this.handleBlur.bind(this); this.handleKeyPress = this.handleKeyPress.bind(this); this.handleInputChange = this.handleInputChange.bind(this); this.handleInputKeyDown = this.handleInputKeyDown.bind(this); this.loadRemote = debouce(this.loadRemote.bind(this), 250, { trailing: true, leading: false }); } componentDidMount() { this.loadRemote(''); } open(fn?: () => void) { if (this.props.disabled) { return; } this.setState( { isOpened: true }, fn ); } close() { this.setState( { isOpened: false, inputValue: this.props.multiple ? this.state.inputValue : '' }, () => this.loadRemote(this.state.inputValue) ); } handleFocus() { this.setState({ isFocused: true }); } handleBlur() { this.setState({ isFocused: false }); } handleKeyPress(e: React.KeyboardEvent) { if (e.key === ' ') { this.handleOutClick(e as any); e.preventDefault(); } } validate(): any { const {value, minLength, maxLength, delimiter, translate: __} = this.props; let curValue = Array.isArray(value) ? value : (value ? String(value) : '').split(delimiter || ','); if (minLength && curValue.length < minLength) { return __( '已选择数量低于设定的最小个数${minLength},请选择更多的选项。', {minLength} ); } else if (maxLength && curValue.length > maxLength) { return __( '已选择数量超出设定的最大个数{{maxLength}},请取消选择超出的选项。', {maxLength} ); } } removeItem(index: number, e?: React.MouseEvent) { const { selectedOptions, joinValues, extractValue, delimiter, valueField, onChange, disabled } = this.props; e && e.stopPropagation(); if (disabled) { return; } const items = selectedOptions.concat(); items.splice(index, 1); let value: any = items; if (joinValues) { value = items .map((item: any) => item[valueField || 'value']) .join(delimiter || ','); } else if (extractValue) { value = items.map((item: any) => item[valueField || 'value']); } onChange(value); } handleChange(value: any) { const {onChange, multiple} = this.props; if (!multiple) { this.close(); } multiple || !this.state.inputValue ? onChange(value) : this.setState( { inputValue: '' }, () => onChange(value) ); } handleInputChange(value: string) { const {autoComplete, data} = this.props; this.setState( { inputValue: value }, isEffectiveApi(autoComplete, data) ? () => this.loadRemote(this.state.inputValue) : undefined ); } handleInputKeyDown(event: React.KeyboardEvent) { const inputValue = this.state.inputValue; const {multiple, selectedOptions} = this.props; if ( event.key === 'Backspace' && !inputValue && selectedOptions.length && multiple ) { this.removeItem(selectedOptions.length - 1); } } clearValue() { const {onChange, resetValue} = this.props; onChange(typeof resetValue === 'undefined' ? '' : resetValue); } filterOptions(options: Array