/* eslint-disable */ import React from "react"; import { List } from "../list"; import { DropdownBox } from "../dropdown"; import { TagSearchBoxContext } from "./TagSearchBoxContext"; export interface Value { /** * 唯一标识 */ key?: string; /** * 属性展示值 */ name: string; } export interface AttributeRenderProps { /** * 当前输入值 */ inputValue: string; /** * 监听某些操作键位被按下 * @since 2.7.0 */ onOperationalKeyDown?: ( listener: (key: "ArrowDown" | "ArrowUp" | "Enter" | "Tab") => void ) => void; /** * 确认选择 */ onSelect: (value: Value[]) => void; /** * 取消 */ onCancel: () => void; } export type AttributeType = "input" | "single" | "multiple" | "render"; export type AttributeTypeOptions = | ["input", {}] | ["render", {}] | [ "single", { /** * 是否启用搜索 * @default false * @since 2.5.0 */ searchable?: boolean; /** * 无可选项时提示内容 */ emptyText?: React.ReactNode; // /** // * 是否启用虚拟滚动 // */ // virtual?: boolean; // /** // * 列表宽度,启用虚拟滚动后无法自适应宽度 // */ // listWidth?: number; } ] | [ "multiple", { /** * 是否开启全选 * @default true */ all?: boolean; /** * 是否启用搜索 * @default false * @since 2.5.0 */ searchable?: boolean; /** * 无可选项时提示内容 */ emptyText?: React.ReactNode; // /** // * 是否启用虚拟滚动 // */ // virtual?: boolean; // /** // * 列表宽度,启用虚拟滚动后无法自适应宽度 // */ // listWidth?: number; } ]; export interface AttributeValue { /** * 为资源属性需求值的类型 * * 可使用数组形式进行详细配置,如: * * ```js * ["multiple", { all: true }] * ``` * * 其中: * *`"single"`: * - `searchable` 是否启用搜索 * - `emptyText` 无可选项时提示内容 * - `virtual` 是否启用虚拟滚动 * - `listWidth` 列表宽度,启用虚拟滚动后无法自适应宽度 * *`"multiple"`: * - `all` 是否启用全选 * - `searchable` 是否启用搜索 * - `emptyText` 无可选项时提示内容 * - `virtual` 是否启用虚拟滚动 * - `listWidth` 列表宽度,启用虚拟滚动后无法自适应宽度 * * @docType "input" | "single" | "multiple" | "render" | [AttributeType, AttributeOptions] */ type: AttributeType | AttributeTypeOptions; /** * 属性的唯一标识,会在结果中返回 */ key: string; /** * 资源属性值名称 */ name: string; /** * 资源属性可用值 * @docType Value[] | (() => Value[]) | (() => Promise) */ values?: Value[] | (() => Value[]) | (() => Promise); /** * 该属性是否可重复选择 * @default false */ reusable?: boolean; /** * 该属性是否可移除 * @default true */ removeable?: boolean; /** * 自定义渲染 */ render?: (props: AttributeRenderProps) => React.ReactNode; } export interface AttributeSelectProps { attributes: AttributeValue[]; inputValue: string; onSelect?: (attribute: AttributeValue) => void; maxHeight: number; } export interface AttributeSelectState { select: number; lastInputValue: string; } const keys = { "8": "backspace", "9": "tab", "13": "enter", "37": "left", "38": "up", "39": "right", "40": "down", }; export class AttributeSelect extends React.Component< AttributeSelectProps, AttributeSelectState > { static contextType = TagSearchBoxContext; state: AttributeSelectState = { select: -1, lastInputValue: this.props.inputValue, }; static getDerivedStateFromProps( props: AttributeSelectProps, state: AttributeSelectState ) { if (state.lastInputValue !== props.inputValue) { return { select: -1, lastInputValue: props.inputValue }; } return null; } // 父组件调用 handleKeyDown = (keyCode: number): boolean => { if (!keys[keyCode]) return; const { onSelect } = this.props; const { select } = this.state; switch (keys[keyCode]) { case "enter": case "tab": if (select < 0) break; if (onSelect) { onSelect(this.getAttribute(select)); } return false; case "up": this.move(-1); break; case "down": this.move(1); break; } }; getUseableList(): Array { const { attributes, inputValue } = this.props; const { disableAttributesFilter } = this.context; if (disableAttributesFilter) { return attributes; } // 获取冒号前字符串模糊查询 const fuzzyValue = /(.*?)(:|:).*/.test(inputValue) ? RegExp.$1 : inputValue; return attributes.filter( item => item.name.includes(inputValue) || item.name.includes(fuzzyValue) ); } getAttribute(select: number): AttributeValue { const list = this.getUseableList(); if (select < list.length) { return list[select]; } } move = (step: number): void => { const { select } = this.state; const list = this.getUseableList(); if (list.length <= 0) return; this.setState({ select: (select + step + list.length) % list.length }); }; handleClick = (e: React.MouseEvent, index: number): void => { e.stopPropagation(); e.nativeEvent.stopPropagation(); if (this.props.onSelect) { this.props.onSelect(this.getAttribute(index)); } }; render() { const { attributesSelectTips } = this.context; const { select } = this.state; const { maxHeight } = this.props; const list = this.getUseableList().map((item, index) => { return ( this.handleClick(e, index)} > {item.name} ); }); if (list.length === 0) return null; return ( e.stopPropagation()}> {attributesSelectTips && ( {attributesSelectTips} )} {list} ); } }