From 82f28ce3d04b50f6b7270d731a3824a1066462fa Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Tue, 4 Jun 2024 21:20:10 +0800 Subject: [PATCH] fix: select input --- components/_util/BaseInput.tsx | 31 ++++-- components/_util/BaseInputInner.tsx | 96 +++++++++++++++++++ components/vc-select/Selector/Input.tsx | 2 +- .../vc-select/Selector/MultipleSelector.tsx | 32 +++++-- .../vc-select/Selector/SingleSelector.tsx | 17 ++-- components/vc-select/Selector/index.tsx | 2 +- 6 files changed, 152 insertions(+), 28 deletions(-) create mode 100644 components/_util/BaseInputInner.tsx diff --git a/components/_util/BaseInput.tsx b/components/_util/BaseInput.tsx index 4650d80e6..923b24871 100644 --- a/components/_util/BaseInput.tsx +++ b/components/_util/BaseInput.tsx @@ -1,6 +1,8 @@ import type { PropType } from 'vue'; -import { defineComponent, shallowRef, ref, watch } from 'vue'; +import { computed, defineComponent, shallowRef, ref, watch } from 'vue'; import PropTypes from './vue-types'; +import type { BaseInputInnerExpose } from './BaseInputInner'; +import BaseInputInner from './BaseInputInner'; export interface BaseInputExpose { focus: () => void; @@ -30,6 +32,8 @@ const BaseInput = defineComponent({ default: 'input', }, size: PropTypes.string, + style: PropTypes.style, + class: PropTypes.string, }, emits: [ 'change', @@ -40,9 +44,11 @@ const BaseInput = defineComponent({ 'compositionstart', 'compositionend', 'keyup', + 'paste', + 'mousedown', ], setup(props, { emit, attrs, expose }) { - const inputRef = shallowRef(null); + const inputRef = shallowRef(null); const renderValue = ref(); const isComposing = ref(false); watch( @@ -115,19 +121,26 @@ const BaseInput = defineComponent({ expose({ focus, blur, - input: inputRef, + input: computed(() => inputRef.value?.input), setSelectionRange, select, - getSelectionStart: () => inputRef.value?.selectionStart, - getSelectionEnd: () => inputRef.value?.selectionEnd, - getScrollTop: () => inputRef.value?.scrollTop, + getSelectionStart: () => inputRef.value?.getSelectionStart(), + getSelectionEnd: () => inputRef.value?.getSelectionEnd(), + getScrollTop: () => inputRef.value?.getScrollTop(), }); + const handleMousedown = (e: MouseEvent) => { + emit('mousedown', e); + }; + const handlePaste = (e: ClipboardEvent) => { + emit('paste', e); + }; return () => { - const { tag: Tag, ...restProps } = props; + const { tag: Tag, style, ...restProps } = props; return ( - ); }; diff --git a/components/_util/BaseInputInner.tsx b/components/_util/BaseInputInner.tsx new file mode 100644 index 000000000..10423d7a4 --- /dev/null +++ b/components/_util/BaseInputInner.tsx @@ -0,0 +1,96 @@ +import type { PropType } from 'vue'; +import { defineComponent, shallowRef } from 'vue'; +import PropTypes from './vue-types'; + +export interface BaseInputInnerExpose { + focus: () => void; + blur: () => void; + input: HTMLInputElement | HTMLTextAreaElement | null; + setSelectionRange: ( + start: number, + end: number, + direction?: 'forward' | 'backward' | 'none', + ) => void; + select: () => void; + getSelectionStart: () => number | null; + getSelectionEnd: () => number | null; + getScrollTop: () => number | null; + setScrollTop: (scrollTop: number) => void; +} +const BaseInputInner = defineComponent({ + compatConfig: { MODE: 3 }, + // inheritAttrs: false, + props: { + disabled: PropTypes.looseBool, + type: PropTypes.string, + value: PropTypes.any, + tag: { + type: String as PropType<'input' | 'textarea'>, + default: 'input', + }, + size: PropTypes.string, + onChange: Function as PropType<(e: Event) => void>, + onInput: Function as PropType<(e: Event) => void>, + onBlur: Function as PropType<(e: Event) => void>, + onFocus: Function as PropType<(e: Event) => void>, + onKeydown: Function as PropType<(e: Event) => void>, + onCompositionstart: Function as PropType<(e: Event) => void>, + onCompositionend: Function as PropType<(e: Event) => void>, + onKeyup: Function as PropType<(e: Event) => void>, + onPaste: Function as PropType<(e: Event) => void>, + onMousedown: Function as PropType<(e: Event) => void>, + }, + emits: [ + 'change', + 'input', + 'blur', + 'keydown', + 'focus', + 'compositionstart', + 'compositionend', + 'keyup', + 'paste', + 'mousedown', + ], + setup(props, { expose }) { + const inputRef = shallowRef(null); + + const focus = () => { + if (inputRef.value) { + inputRef.value.focus(); + } + }; + const blur = () => { + if (inputRef.value) { + inputRef.value.blur(); + } + }; + const setSelectionRange = ( + start: number, + end: number, + direction?: 'forward' | 'backward' | 'none', + ) => { + inputRef.value?.setSelectionRange(start, end, direction); + }; + + const select = () => { + inputRef.value?.select(); + }; + expose({ + focus, + blur, + input: inputRef, + setSelectionRange, + select, + getSelectionStart: () => inputRef.value?.selectionStart, + getSelectionEnd: () => inputRef.value?.selectionEnd, + getScrollTop: () => inputRef.value?.scrollTop, + }); + return () => { + const { tag: Tag, value, ...restProps } = props; + return ; + }; + }, +}); + +export default BaseInputInner; diff --git a/components/vc-select/Selector/Input.tsx b/components/vc-select/Selector/Input.tsx index b750e1a62..a621f169a 100644 --- a/components/vc-select/Selector/Input.tsx +++ b/components/vc-select/Selector/Input.tsx @@ -48,7 +48,6 @@ const Input = defineComponent({ setup(props) { let blurTimeout = null; const VCSelectContainerEvent = inject('VCSelectContainerEvent') as any; - return () => { const { prefixCls, @@ -97,6 +96,7 @@ const Input = defineComponent({ ref: inputRef, disabled, tabindex, + lazy: false, autocomplete: autocomplete || 'off', autofocus, class: classNames(`${prefixCls}-selection-search-input`, inputNode?.props?.class), diff --git a/components/vc-select/Selector/MultipleSelector.tsx b/components/vc-select/Selector/MultipleSelector.tsx index 8daad360d..bcd76d7d1 100644 --- a/components/vc-select/Selector/MultipleSelector.tsx +++ b/components/vc-select/Selector/MultipleSelector.tsx @@ -2,7 +2,7 @@ import TransBtn from '../TransBtn'; import type { InnerSelectorProps } from './interface'; import Input from './Input'; import type { Ref, PropType } from 'vue'; -import { computed, defineComponent, onMounted, shallowRef, watch } from 'vue'; +import { ref, watchEffect, computed, defineComponent, onMounted, shallowRef, watch } from 'vue'; import classNames from '../../_util/classNames'; import pickAttrs from '../../_util/pickAttrs'; import PropTypes from '../../_util/vue-types'; @@ -24,6 +24,8 @@ type SelectorProps = InnerSelectorProps & { tagRender?: (props: CustomTagProps) => VueNode; onToggleOpen: any; + compositionStatus: boolean; + // Motion choiceTransitionName?: string; @@ -46,7 +48,7 @@ const props = { autocomplete: String, activeDescendantId: String, tabindex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - + compositionStatus: Boolean, removeIcon: PropTypes.any, choiceTransitionName: String, @@ -91,11 +93,14 @@ const SelectSelector = defineComponent({ () => props.mode === 'tags' || ((props.showSearch && (props.open || focused.value)) as boolean), ); - + const targetValue = ref(''); + watchEffect(() => { + targetValue.value = inputValue.value; + }); // We measure width and set to the input immediately onMounted(() => { watch( - inputValue, + targetValue, () => { inputWidth.value = measureRef.value.scrollWidth; }, @@ -202,6 +207,14 @@ const SelectSelector = defineComponent({ return defaultRenderSelector(content, content, false); } + const handleInput = (e: Event) => { + const composing = (e.target as any).composing; + targetValue.value = (e.target as any).value; + if (!composing) { + props.onInputChange(e); + } + }; + return () => { const { id, @@ -215,14 +228,13 @@ const SelectSelector = defineComponent({ autocomplete, activeDescendantId, tabindex, - onInputChange, + compositionStatus, onInputPaste, onInputKeyDown, onInputMouseDown, onInputCompositionStart, onInputCompositionEnd, } = props; - // >>> Input Node const inputNode = (
({ autocomplete={autocomplete} editable={inputEditable.value} activeDescendantId={activeDescendantId} - value={inputValue.value} + value={targetValue.value} onKeydown={onInputKeyDown} onMousedown={onInputMouseDown} - onChange={onInputChange} + onChange={handleInput} onPaste={onInputPaste} onCompositionstart={onInputCompositionStart} onCompositionend={onInputCompositionEnd} @@ -256,7 +268,7 @@ const SelectSelector = defineComponent({ {/* Measure Node */} - {inputValue.value}  + {targetValue.value} 
); @@ -277,7 +289,7 @@ const SelectSelector = defineComponent({ return ( <> {selectionNode} - {!values.length && !inputValue.value && ( + {!values.length && !inputValue.value && !compositionStatus && ( {placeholder} )} diff --git a/components/vc-select/Selector/SingleSelector.tsx b/components/vc-select/Selector/SingleSelector.tsx index 50aa6ea42..58aba8cb5 100644 --- a/components/vc-select/Selector/SingleSelector.tsx +++ b/components/vc-select/Selector/SingleSelector.tsx @@ -1,7 +1,7 @@ import pickAttrs from '../../_util/pickAttrs'; import Input from './Input'; import type { InnerSelectorProps } from './interface'; -import { Fragment, Ref, computed, defineComponent, shallowRef, watch } from 'vue'; +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'; @@ -10,8 +10,6 @@ interface SelectorProps extends InnerSelectorProps { inputElement: VueNode; activeValue: string; optionLabelRender: Function; - - // placeholder compositionStatus: boolean; } const props = { @@ -92,6 +90,13 @@ const SingleSelector = defineComponent({ ); }; + const handleInput = (e: Event) => { + const composing = (e.target as any).composing; + if (!composing) { + inputChanged.value = true; + props.onInputChange(e); + } + }; return () => { const { @@ -109,7 +114,6 @@ const SingleSelector = defineComponent({ optionLabelRender, onInputKeyDown, onInputMouseDown, - onInputChange, onInputPaste, onInputCompositionStart, onInputCompositionEnd, @@ -153,10 +157,7 @@ const SingleSelector = defineComponent({ value={inputValue.value} onKeydown={onInputKeyDown} onMousedown={onInputMouseDown} - onChange={e => { - inputChanged.value = true; - onInputChange(e as any); - }} + onChange={handleInput} onPaste={onInputPaste} onCompositionstart={onInputCompositionStart} onCompositionend={onInputCompositionEnd} diff --git a/components/vc-select/Selector/index.tsx b/components/vc-select/Selector/index.tsx index 4ee018bea..ccc121b08 100644 --- a/components/vc-select/Selector/index.tsx +++ b/components/vc-select/Selector/index.tsx @@ -124,7 +124,7 @@ const Selector = defineComponent({ } as any, setup(props, { expose }) { const inputRef = createRef(); - let compositionStatus = ref(false); + const compositionStatus = ref(false); // ====================== Input ====================== const [getInputMouseDown, setInputMouseDown] = useLock(0);