mirror of
https://gitee.com/element-plus/element-plus.git
synced 2024-11-29 17:58:08 +08:00
refactor(hooks): rewrite composition as a composable function (#14240)
* refactor(hooks): rewrite composition as a composable function * fix(components): [select] avoid navigateOptions when composing Chinese * fix: error * chore: change afterComposition
This commit is contained in:
parent
9fe6eab4c7
commit
233d38b631
@ -209,14 +209,13 @@ import ElTag from '@element-plus/components/tag'
|
||||
import ElIcon from '@element-plus/components/icon'
|
||||
import { useFormItem, useFormSize } from '@element-plus/components/form'
|
||||
import { ClickOutside as vClickoutside } from '@element-plus/directives'
|
||||
import { useEmptyValues, useLocale, useNamespace } from '@element-plus/hooks'
|
||||
import {
|
||||
debugWarn,
|
||||
focusNode,
|
||||
getSibling,
|
||||
isClient,
|
||||
isKorean,
|
||||
} from '@element-plus/utils'
|
||||
useComposition,
|
||||
useEmptyValues,
|
||||
useLocale,
|
||||
useNamespace,
|
||||
} from '@element-plus/hooks'
|
||||
import { debugWarn, focusNode, getSibling, isClient } from '@element-plus/utils'
|
||||
import {
|
||||
CHANGE_EVENT,
|
||||
EVENT_CODE,
|
||||
@ -271,6 +270,12 @@ const nsInput = useNamespace('input')
|
||||
const { t } = useLocale()
|
||||
const { form, formItem } = useFormItem()
|
||||
const { valueOnClear } = useEmptyValues(props)
|
||||
const { isComposing, handleComposition } = useComposition({
|
||||
afterComposition(event) {
|
||||
const text = (event.target as HTMLInputElement)?.value
|
||||
handleInput(text)
|
||||
},
|
||||
})
|
||||
|
||||
const tooltipRef: Ref<TooltipInstance | null> = ref(null)
|
||||
const input: Ref<InputInstance | null> = ref(null)
|
||||
@ -286,7 +291,6 @@ const searchInputValue = ref('')
|
||||
const presentTags: Ref<Tag[]> = ref([])
|
||||
const allPresentTags: Ref<Tag[]> = ref([])
|
||||
const suggestions: Ref<CascaderNode[]> = ref([])
|
||||
const isOnComposition = ref(false)
|
||||
|
||||
const cascaderStyle = computed<StyleValue>(() => {
|
||||
return attrs.style as StyleValue
|
||||
@ -297,9 +301,7 @@ const inputPlaceholder = computed(
|
||||
() => props.placeholder || t('el.cascader.placeholder')
|
||||
)
|
||||
const currentPlaceholder = computed(() =>
|
||||
searchInputValue.value ||
|
||||
presentTags.value.length > 0 ||
|
||||
isOnComposition.value
|
||||
searchInputValue.value || presentTags.value.length > 0 || isComposing.value
|
||||
? ''
|
||||
: inputPlaceholder.value
|
||||
)
|
||||
@ -538,19 +540,8 @@ const handleExpandChange = (value: CascaderValue) => {
|
||||
emit('expandChange', value)
|
||||
}
|
||||
|
||||
const handleComposition = (event: CompositionEvent) => {
|
||||
const text = (event.target as HTMLInputElement)?.value
|
||||
if (event.type === 'compositionend') {
|
||||
isOnComposition.value = false
|
||||
nextTick(() => handleInput(text))
|
||||
} else {
|
||||
const lastCharacter = text[text.length - 1] || ''
|
||||
isOnComposition.value = !isKorean(lastCharacter)
|
||||
}
|
||||
}
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (isOnComposition.value) return
|
||||
if (isComposing.value) return
|
||||
|
||||
switch (e.code) {
|
||||
case EVENT_CODE.enter:
|
||||
|
@ -179,11 +179,11 @@ import {
|
||||
ValidateComponentsMap,
|
||||
debugWarn,
|
||||
isClient,
|
||||
isKorean,
|
||||
isObject,
|
||||
} from '@element-plus/utils'
|
||||
import {
|
||||
useAttrs,
|
||||
useComposition,
|
||||
useCursor,
|
||||
useDeprecated,
|
||||
useFocusController,
|
||||
@ -256,7 +256,6 @@ const input = shallowRef<HTMLInputElement>()
|
||||
const textarea = shallowRef<HTMLTextAreaElement>()
|
||||
|
||||
const hovering = ref(false)
|
||||
const isComposing = ref(false)
|
||||
const passwordVisible = ref(false)
|
||||
const countStyle = ref<StyleValue>()
|
||||
const textareaCalcStyle = shallowRef(props.inputStyle)
|
||||
@ -435,25 +434,12 @@ const handleChange = (event: Event) => {
|
||||
emit('change', (event.target as TargetElement).value)
|
||||
}
|
||||
|
||||
const handleCompositionStart = (event: CompositionEvent) => {
|
||||
emit('compositionstart', event)
|
||||
isComposing.value = true
|
||||
}
|
||||
|
||||
const handleCompositionUpdate = (event: CompositionEvent) => {
|
||||
emit('compositionupdate', event)
|
||||
const text = (event.target as HTMLInputElement)?.value
|
||||
const lastCharacter = text[text.length - 1] || ''
|
||||
isComposing.value = !isKorean(lastCharacter)
|
||||
}
|
||||
|
||||
const handleCompositionEnd = (event: CompositionEvent) => {
|
||||
emit('compositionend', event)
|
||||
if (isComposing.value) {
|
||||
isComposing.value = false
|
||||
handleInput(event)
|
||||
}
|
||||
}
|
||||
const {
|
||||
isComposing,
|
||||
handleCompositionStart,
|
||||
handleCompositionUpdate,
|
||||
handleCompositionEnd,
|
||||
} = useComposition({ emit, afterComposition: handleInput })
|
||||
|
||||
const handlePasswordVisible = () => {
|
||||
passwordVisible.value = !passwordVisible.value
|
||||
|
@ -1,33 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import { ref } from 'vue'
|
||||
import { isFunction } from '@vue/shared'
|
||||
import { isKorean } from '@element-plus/utils'
|
||||
|
||||
export function useInput(handleInput: (event: InputEvent) => void) {
|
||||
const isComposing = ref(false)
|
||||
|
||||
const handleCompositionStart = () => {
|
||||
isComposing.value = true
|
||||
}
|
||||
|
||||
const handleCompositionUpdate = (event) => {
|
||||
const text = event.target.value
|
||||
const lastCharacter = text[text.length - 1] || ''
|
||||
isComposing.value = !isKorean(lastCharacter)
|
||||
}
|
||||
|
||||
const handleCompositionEnd = (event) => {
|
||||
if (isComposing.value) {
|
||||
isComposing.value = false
|
||||
if (isFunction(handleInput)) {
|
||||
handleInput(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
handleCompositionStart,
|
||||
handleCompositionUpdate,
|
||||
handleCompositionEnd,
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ import {
|
||||
} from 'lodash-unified'
|
||||
import { useResizeObserver } from '@vueuse/core'
|
||||
import {
|
||||
useComposition,
|
||||
useEmptyValues,
|
||||
useFocusController,
|
||||
useLocale,
|
||||
@ -40,7 +41,6 @@ import {
|
||||
|
||||
import { ArrowDown } from '@element-plus/icons-vue'
|
||||
import { useAllowCreate } from './useAllowCreate'
|
||||
import { useInput } from './useInput'
|
||||
import { useProps } from './useProps'
|
||||
|
||||
import type ElTooltip from '@element-plus/components/tooltip'
|
||||
@ -94,6 +94,15 @@ const useSelect = (props: ISelectV2Props, emit) => {
|
||||
const tagMenuRef = ref<HTMLElement>(null)
|
||||
const collapseItemRef = ref<HTMLElement>(null)
|
||||
|
||||
const {
|
||||
isComposing,
|
||||
handleCompositionStart,
|
||||
handleCompositionEnd,
|
||||
handleCompositionUpdate,
|
||||
} = useComposition({
|
||||
afterComposition: (e) => onInput(e),
|
||||
})
|
||||
|
||||
const { wrapperRef, isFocused, handleFocus, handleBlur } = useFocusController(
|
||||
inputRef,
|
||||
{
|
||||
@ -356,11 +365,6 @@ const useSelect = (props: ISelectV2Props, emit) => {
|
||||
selectNewOption,
|
||||
clearAllNewOption,
|
||||
} = useAllowCreate(props, states)
|
||||
const {
|
||||
handleCompositionStart,
|
||||
handleCompositionUpdate,
|
||||
handleCompositionEnd,
|
||||
} = useInput((e) => onInput(e))
|
||||
|
||||
// methods
|
||||
const toggleMenu = () => {
|
||||
@ -385,7 +389,7 @@ const useSelect = (props: ISelectV2Props, emit) => {
|
||||
const debouncedOnInputChange = lodashDebounce(onInputChange, debounce.value)
|
||||
|
||||
const handleQueryChange = (val: string) => {
|
||||
if (states.previousQuery === val) {
|
||||
if (states.previousQuery === val || isComposing.value) {
|
||||
return
|
||||
}
|
||||
states.previousQuery = val
|
||||
@ -619,7 +623,8 @@ const useSelect = (props: ISelectV2Props, emit) => {
|
||||
!['forward', 'backward'].includes(direction) ||
|
||||
selectDisabled.value ||
|
||||
options.length <= 0 ||
|
||||
optionsAllDisabled.value
|
||||
optionsAllDisabled.value ||
|
||||
isComposing.value
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import {
|
||||
scrollIntoView,
|
||||
} from '@element-plus/utils'
|
||||
import {
|
||||
useComposition,
|
||||
useEmptyValues,
|
||||
useFocusController,
|
||||
useId,
|
||||
@ -45,7 +46,6 @@ import {
|
||||
useFormSize,
|
||||
} from '@element-plus/components/form'
|
||||
|
||||
import { useInput } from '../../select-v2/src/useInput'
|
||||
import type ElTooltip from '@element-plus/components/tooltip'
|
||||
import type { ISelectProps, SelectOptionProxy } from './token'
|
||||
|
||||
@ -91,6 +91,15 @@ export const useSelect = (props: ISelectProps, emit) => {
|
||||
handleScroll: () => void
|
||||
} | null>(null)
|
||||
|
||||
const {
|
||||
isComposing,
|
||||
handleCompositionStart,
|
||||
handleCompositionUpdate,
|
||||
handleCompositionEnd,
|
||||
} = useComposition({
|
||||
afterComposition: (e) => onInput(e),
|
||||
})
|
||||
|
||||
const { wrapperRef, isFocused, handleFocus, handleBlur } = useFocusController(
|
||||
inputRef,
|
||||
{
|
||||
@ -341,7 +350,7 @@ export const useSelect = (props: ISelectProps, emit) => {
|
||||
})
|
||||
|
||||
const handleQueryChange = (val: string) => {
|
||||
if (states.previousQuery === val) {
|
||||
if (states.previousQuery === val || isComposing.value) {
|
||||
return
|
||||
}
|
||||
states.previousQuery = val
|
||||
@ -631,12 +640,6 @@ export const useSelect = (props: ISelectProps, emit) => {
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
handleCompositionStart,
|
||||
handleCompositionUpdate,
|
||||
handleCompositionEnd,
|
||||
} = useInput((e) => onInput(e))
|
||||
|
||||
const popperRef = computed(() => {
|
||||
return tooltipRef.value?.popperRef?.contentRef
|
||||
})
|
||||
@ -733,7 +736,12 @@ export const useSelect = (props: ISelectProps, emit) => {
|
||||
expanded.value = true
|
||||
return
|
||||
}
|
||||
if (states.options.size === 0 || filteredOptionsCount.value === 0) return
|
||||
if (
|
||||
states.options.size === 0 ||
|
||||
states.filteredOptionsCount === 0 ||
|
||||
isComposing.value
|
||||
)
|
||||
return
|
||||
|
||||
if (!optionsAllDisabled.value) {
|
||||
if (direction === 'next') {
|
||||
|
@ -27,5 +27,6 @@ export * from './use-cursor'
|
||||
export * from './use-ordered-children'
|
||||
export * from './use-size'
|
||||
export * from './use-focus-controller'
|
||||
export * from './use-composition'
|
||||
export * from './use-empty-values'
|
||||
export * from './use-aria'
|
||||
|
50
packages/hooks/use-composition/index.ts
Normal file
50
packages/hooks/use-composition/index.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { nextTick, ref } from 'vue'
|
||||
import { isKorean } from '@element-plus/utils'
|
||||
|
||||
interface UseCompositionOptions {
|
||||
afterComposition: (event: CompositionEvent) => void
|
||||
emit?: ((event: 'compositionstart', evt: CompositionEvent) => void) &
|
||||
((event: 'compositionupdate', evt: CompositionEvent) => void) &
|
||||
((event: 'compositionend', evt: CompositionEvent) => void)
|
||||
}
|
||||
|
||||
export function useComposition({
|
||||
afterComposition,
|
||||
emit,
|
||||
}: UseCompositionOptions) {
|
||||
const isComposing = ref(false)
|
||||
|
||||
const handleCompositionStart = (event: CompositionEvent) => {
|
||||
emit?.('compositionstart', event)
|
||||
isComposing.value = true
|
||||
}
|
||||
|
||||
const handleCompositionUpdate = (event: CompositionEvent) => {
|
||||
emit?.('compositionupdate', event)
|
||||
const text = (event.target as HTMLInputElement)?.value
|
||||
const lastCharacter = text[text.length - 1] || ''
|
||||
isComposing.value = !isKorean(lastCharacter)
|
||||
}
|
||||
|
||||
const handleCompositionEnd = (event: CompositionEvent) => {
|
||||
emit?.('compositionend', event)
|
||||
if (isComposing.value) {
|
||||
isComposing.value = false
|
||||
nextTick(() => afterComposition(event))
|
||||
}
|
||||
}
|
||||
|
||||
const handleComposition = (event: CompositionEvent) => {
|
||||
event.type === 'compositionend'
|
||||
? handleCompositionEnd(event)
|
||||
: handleCompositionUpdate(event)
|
||||
}
|
||||
|
||||
return {
|
||||
isComposing,
|
||||
handleComposition,
|
||||
handleCompositionStart,
|
||||
handleCompositionUpdate,
|
||||
handleCompositionEnd,
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user