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