ant-design-vue/components/vc-select/Selector/SingleSelector.tsx

220 lines
6.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import pickAttrs from '../../_util/pickAttrs';
import Input from './Input';
import type { InnerSelectorProps } from './interface';
import { Fragment, computed, defineComponent, shallowRef, watch } from 'vue';
import PropTypes from '../../_util/vue-types';
import type { VueNode } from '../../_util/type';
import useInjectLegacySelectContext from '../../vc-tree-select/LegacyContext';
interface SelectorProps extends InnerSelectorProps {
inputElement: VueNode;
activeValue: string;
optionLabelRender: Function;
compositionStatus: boolean;
}
const props = {
inputElement: PropTypes.any,
id: String,
prefixCls: String,
values: PropTypes.array,
open: { type: Boolean, default: undefined },
searchValue: String,
inputRef: PropTypes.any,
placeholder: PropTypes.any,
compositionStatus: { type: Boolean, default: undefined },
disabled: { type: Boolean, default: undefined },
mode: String,
showSearch: { type: Boolean, default: undefined },
autofocus: { type: Boolean, default: undefined },
autocomplete: String,
activeDescendantId: String,
tabindex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
activeValue: String,
backfill: { type: Boolean, default: undefined },
optionLabelRender: Function,
onInputChange: Function,
onInputPaste: Function,
onInputKeyDown: Function,
onInputMouseDown: Function,
onInputCompositionStart: Function,
onInputCompositionEnd: Function,
};
const SingleSelector = defineComponent<SelectorProps>({
name: 'SingleSelector',
setup(props) {
const inputChanged = shallowRef(false);
// 本地输入状态管理
const hasInput = shallowRef(false);
const combobox = computed(() => props.mode === 'combobox');
const inputEditable = computed(() => combobox.value || props.showSearch);
const inputValue = computed(() => {
let inputValue: string = props.searchValue || '';
if (combobox.value && props.activeValue && !inputChanged.value) {
inputValue = props.activeValue;
}
return inputValue;
});
const legacyTreeSelectContext = useInjectLegacySelectContext();
watch(
[combobox, () => props.activeValue],
() => {
if (combobox.value) {
inputChanged.value = false;
}
},
{ immediate: true },
);
// 监听下拉框关闭(失焦)时重置输入状态
watch(
() => props.open,
newOpen => {
if (!newOpen) {
hasInput.value = false;
}
},
);
// 监听选择完成values变化时重置输入状态
watch(
() => props.values,
() => {
if (props.values && props.values.length > 0) {
hasInput.value = false;
}
},
{ deep: true },
);
// Not show text when closed expect combobox mode
const hasTextInput = computed(() => {
// 只有在本地输入状态为true时才认为有文本输入
if (!hasInput.value) {
return false;
}
return props.mode !== 'combobox' && !props.open && !props.showSearch
? false
: !!inputValue.value || props.compositionStatus;
});
const title = computed(() => {
const item = props.values[0];
return item && (typeof item.label === 'string' || typeof item.label === 'number')
? item.label.toString()
: undefined;
});
const renderPlaceholder = () => {
if (props.values[0]) {
return null;
}
const hiddenStyle = hasTextInput.value ? { visibility: 'hidden' as const } : undefined;
return (
<span class={`${props.prefixCls}-selection-placeholder`} style={hiddenStyle}>
{props.placeholder}
</span>
);
};
const handleInput = (e: Event) => {
const composing = (e.target as any).composing;
// 用户输入时设置输入状态为true
hasInput.value = true;
if (!composing) {
inputChanged.value = true;
props.onInputChange(e);
}
};
return () => {
const {
inputElement,
prefixCls,
id,
values,
inputRef,
disabled,
autofocus,
autocomplete,
activeDescendantId,
open,
tabindex,
optionLabelRender,
onInputKeyDown,
onInputMouseDown,
onInputPaste,
onInputCompositionStart,
onInputCompositionEnd,
} = props;
const item = values[0];
let titleNode = null;
// custom tree-select title by slot
// For TreeSelect
if (item && legacyTreeSelectContext.customSlots) {
const key = item.key ?? item.value;
const originData = legacyTreeSelectContext.keyEntities[key]?.node || {};
titleNode =
legacyTreeSelectContext.customSlots[originData.slots?.title] ||
legacyTreeSelectContext.customSlots.title ||
item.label;
if (typeof titleNode === 'function') {
titleNode = titleNode(originData);
}
// else if (treeSelectContext.value.slots.titleRender) {
// // 因历史 title 是覆盖逻辑,新增 titleRender所有的 title 都走一遍 titleRender
// titleNode = treeSelectContext.value.slots.titleRender(item.option?.data || {});
// }
} else {
titleNode = optionLabelRender && item ? optionLabelRender(item.option) : item?.label;
}
return (
<>
<span class={`${prefixCls}-selection-search`}>
<Input
inputRef={inputRef}
prefixCls={prefixCls}
id={id}
open={open}
inputElement={inputElement}
disabled={disabled}
autofocus={autofocus}
autocomplete={autocomplete}
editable={inputEditable.value}
activeDescendantId={activeDescendantId}
value={inputValue.value}
onKeydown={onInputKeyDown}
onMousedown={onInputMouseDown}
onChange={handleInput}
onPaste={onInputPaste}
onCompositionstart={onInputCompositionStart}
onCompositionend={onInputCompositionEnd}
tabindex={tabindex}
attrs={pickAttrs(props, true)}
/>
</span>
{/* Display value */}
{!combobox.value && item && !hasTextInput.value && (
<span class={`${prefixCls}-selection-item`} title={title.value}>
<Fragment key={item.key ?? item.value}>{titleNode}</Fragment>
</span>
)}
{/* Display placeholder */}
{renderPlaceholder()}
</>
);
};
},
});
SingleSelector.props = props;
SingleSelector.inheritAttrs = false;
export default SingleSelector;