import TransBtn from '../TransBtn'; import type { LabelValueType, RawValueType, CustomTagProps, DefaultValueType, DisplayLabelValueType, } from '../interface/generator'; import type { RenderNode } from '../interface'; import type { InnerSelectorProps } from './interface'; import Input from './Input'; import type { VNodeChild, Ref, PropType } from 'vue'; import { computed, defineComponent, onMounted, ref, watch } from 'vue'; import classNames from '../../_util/classNames'; import pickAttrs from '../../_util/pickAttrs'; import PropTypes from '../../_util/vue-types'; import type { VueNode } from '../../_util/type'; import Overflow from '../../vc-overflow'; type SelectorProps = InnerSelectorProps & { // Icon removeIcon?: RenderNode; // Tags maxTagCount?: number | 'responsive'; maxTagTextLength?: number; maxTagPlaceholder?: VNodeChild | ((omittedValues: LabelValueType[]) => VNodeChild); tokenSeparators?: string[]; tagRender?: (props: CustomTagProps) => VNodeChild; onToggleOpen: any; // Motion choiceTransitionName?: string; // Event onSelect: (value: RawValueType, option: { selected: boolean }) => void; }; const props = { id: PropTypes.string, prefixCls: PropTypes.string, values: PropTypes.array, open: PropTypes.looseBool, searchValue: PropTypes.string, inputRef: PropTypes.any, placeholder: PropTypes.any, disabled: PropTypes.looseBool, mode: PropTypes.string, showSearch: PropTypes.looseBool, autofocus: PropTypes.looseBool, autocomplete: PropTypes.string, accessibilityIndex: PropTypes.number, tabindex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), removeIcon: PropTypes.VNodeChild, choiceTransitionName: PropTypes.string, maxTagCount: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), maxTagTextLength: PropTypes.number, maxTagPlaceholder: PropTypes.any.def( () => (omittedValues: LabelValueType[]) => `+ ${omittedValues.length} ...`, ), tagRender: PropTypes.func, onToggleOpen: { type: Function as PropType<(open?: boolean) => void> }, onSelect: PropTypes.func, onInputChange: PropTypes.func, onInputPaste: PropTypes.func, onInputKeyDown: PropTypes.func, onInputMouseDown: PropTypes.func, onInputCompositionStart: PropTypes.func, onInputCompositionEnd: PropTypes.func, }; const onPreventMouseDown = (event: MouseEvent) => { event.preventDefault(); event.stopPropagation(); }; const SelectSelector = defineComponent({ name: 'MultipleSelectSelector', inheritAttrs: false, props: props as any, setup(props) { const measureRef = ref(); const inputWidth = ref(0); const focused = ref(false); const selectionPrefixCls = computed(() => `${props.prefixCls}-selection`); // ===================== Search ====================== const inputValue = computed(() => props.open || props.mode === 'tags' ? props.searchValue : '', ); const inputEditable: Ref = computed( () => props.mode === 'tags' || ((props.showSearch && (props.open || focused.value)) as boolean), ); // We measure width and set to the input immediately onMounted(() => { watch( inputValue, () => { inputWidth.value = measureRef.value.scrollWidth; }, { flush: 'post', immediate: true }, ); }); // ===================== Render ====================== // >>> Render Selector Node. Includes Item & Rest function defaultRenderSelector( content: VueNode, itemDisabled: boolean, closable?: boolean, onClose?: (e: MouseEvent) => void, ) { return ( {content} {closable && ( × )} ); } function customizeRenderSelector( value: DefaultValueType, content: VueNode, itemDisabled: boolean, closable: boolean, onClose: (e: MouseEvent) => void, ) { const onMouseDown = (e: MouseEvent) => { onPreventMouseDown(e); props.onToggleOpen(!open); }; return ( {props.tagRender({ label: content, value, disabled: itemDisabled, closable, onClose, })} ); } function renderItem({ disabled: itemDisabled, label, value }: DisplayLabelValueType) { const closable = !props.disabled && !itemDisabled; let displayLabel = label; if (typeof props.maxTagTextLength === 'number') { if (typeof label === 'string' || typeof label === 'number') { const strLabel = String(displayLabel); if (strLabel.length > props.maxTagTextLength) { displayLabel = `${strLabel.slice(0, props.maxTagTextLength)}...`; } } } const onClose = (event?: MouseEvent) => { if (event) event.stopPropagation(); props.onSelect(value, { selected: false }); }; return typeof props.tagRender === 'function' ? customizeRenderSelector(value, displayLabel, itemDisabled, closable, onClose) : defaultRenderSelector(displayLabel, itemDisabled, closable, onClose); } function renderRest(omittedValues: DisplayLabelValueType[]) { const { maxTagPlaceholder = (omittedValues: LabelValueType[]) => `+ ${omittedValues.length} ...`, } = props; const content = typeof maxTagPlaceholder === 'function' ? maxTagPlaceholder(omittedValues) : maxTagPlaceholder; return defaultRenderSelector(content, false); } return () => { const { id, prefixCls, values, open, inputRef, placeholder, disabled, autofocus, autocomplete, accessibilityIndex, tabindex, onInputChange, onInputPaste, onInputKeyDown, onInputMouseDown, onInputCompositionStart, onInputCompositionEnd, } = props; // >>> Input Node const inputNode = (
(focused.value = true)} onBlur={() => (focused.value = false)} /> {/* Measure Node */} {inputValue.value} 
); // >>> Selections const selectionNode = ( ); return ( <> {selectionNode} {!values.length && !inputValue.value && ( {placeholder} )} ); }; }, }); export default SelectSelector;