mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
refactor(hooks): [useFocusController] add disabled attribute (#21032)
refactor(hooks): [useFocusController] add disabled prop Co-authored-by: warmthsea <2586244885@qq.com>
This commit is contained in:
@@ -180,9 +180,7 @@ const triggerRef = ref()
|
||||
const inputRef = ref()
|
||||
|
||||
const { isFocused, handleFocus, handleBlur } = useFocusController(triggerRef, {
|
||||
beforeFocus() {
|
||||
return colorDisabled.value
|
||||
},
|
||||
disabled: colorDisabled,
|
||||
beforeBlur(event) {
|
||||
return popper.value?.isFocusInsideContent(event)
|
||||
},
|
||||
|
||||
@@ -138,9 +138,7 @@ export function useInputTag({ props, emit, formItem }: UseInputTagOptions) {
|
||||
}
|
||||
|
||||
const { wrapperRef, isFocused } = useFocusController(inputRef, {
|
||||
beforeFocus() {
|
||||
return disabled.value
|
||||
},
|
||||
disabled,
|
||||
afterBlur() {
|
||||
if (props.saveOnBlur) {
|
||||
handleAddTag()
|
||||
|
||||
@@ -256,9 +256,7 @@ const _ref = computed(() => input.value || textarea.value)
|
||||
const { wrapperRef, isFocused, handleFocus, handleBlur } = useFocusController(
|
||||
_ref,
|
||||
{
|
||||
beforeFocus() {
|
||||
return inputDisabled.value
|
||||
},
|
||||
disabled: inputDisabled,
|
||||
afterBlur() {
|
||||
if (props.validateEvent) {
|
||||
elFormItem?.validate?.('blur').catch((err) => debugWarn(err))
|
||||
|
||||
@@ -192,9 +192,7 @@ const handleInputKeyDown = (event: KeyboardEvent | Event) => {
|
||||
}
|
||||
|
||||
const { wrapperRef } = useFocusController(elInputRef, {
|
||||
beforeFocus() {
|
||||
return disabled.value
|
||||
},
|
||||
disabled,
|
||||
afterFocus() {
|
||||
syncAfterCursorMove()
|
||||
},
|
||||
|
||||
@@ -102,10 +102,10 @@ const useSelect = (props: SelectV2Props, emit: SelectV2EmitFn) => {
|
||||
afterComposition: (e) => onInput(e),
|
||||
})
|
||||
|
||||
const selectDisabled = computed(() => props.disabled || !!elForm?.disabled)
|
||||
|
||||
const { wrapperRef, isFocused, handleBlur } = useFocusController(inputRef, {
|
||||
beforeFocus() {
|
||||
return selectDisabled.value
|
||||
},
|
||||
disabled: selectDisabled,
|
||||
afterFocus() {
|
||||
if (props.automaticDropdown && !expanded.value) {
|
||||
expanded.value = true
|
||||
@@ -138,8 +138,6 @@ const useSelect = (props: SelectV2Props, emit: SelectV2EmitFn) => {
|
||||
// the controller of the expanded popup
|
||||
const expanded = ref(false)
|
||||
|
||||
const selectDisabled = computed(() => props.disabled || elForm?.disabled)
|
||||
|
||||
const needStatusIcon = computed(() => elForm?.statusIcon ?? false)
|
||||
|
||||
const popupHeight = computed(() => {
|
||||
|
||||
@@ -92,6 +92,15 @@ export const useSelect = (props: SelectProps, emit: SelectEmits) => {
|
||||
const tagMenuRef = ref<HTMLElement>()
|
||||
const collapseItemRef = ref<HTMLElement>()
|
||||
const scrollbarRef = ref<ScrollbarInstance>()
|
||||
// the controller of the expanded popup
|
||||
const expanded = ref(false)
|
||||
const hoverOption = ref()
|
||||
|
||||
const { form, formItem } = useFormItem()
|
||||
const { inputId } = useFormItemInputId(props, {
|
||||
formItemContext: formItem,
|
||||
})
|
||||
const { valueOnClear, isEmptyValue } = useEmptyValues(props)
|
||||
|
||||
const {
|
||||
isComposing,
|
||||
@@ -102,10 +111,10 @@ export const useSelect = (props: SelectProps, emit: SelectEmits) => {
|
||||
afterComposition: (e) => onInput(e),
|
||||
})
|
||||
|
||||
const selectDisabled = computed(() => props.disabled || !!form?.disabled)
|
||||
|
||||
const { wrapperRef, isFocused, handleBlur } = useFocusController(inputRef, {
|
||||
beforeFocus() {
|
||||
return selectDisabled.value
|
||||
},
|
||||
disabled: selectDisabled,
|
||||
afterFocus() {
|
||||
if (props.automaticDropdown && !expanded.value) {
|
||||
expanded.value = true
|
||||
@@ -127,18 +136,6 @@ export const useSelect = (props: SelectProps, emit: SelectEmits) => {
|
||||
},
|
||||
})
|
||||
|
||||
// the controller of the expanded popup
|
||||
const expanded = ref(false)
|
||||
const hoverOption = ref()
|
||||
|
||||
const { form, formItem } = useFormItem()
|
||||
const { inputId } = useFormItemInputId(props, {
|
||||
formItemContext: formItem,
|
||||
})
|
||||
const { valueOnClear, isEmptyValue } = useEmptyValues(props)
|
||||
|
||||
const selectDisabled = computed(() => props.disabled || form?.disabled)
|
||||
|
||||
const hasModelValue = computed(() => {
|
||||
return isArray(props.modelValue)
|
||||
? props.modelValue.length > 0
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useAttrs, useFocusController, useNamespace } from '@element-plus/hooks'
|
||||
import { timePickerRangeTriggerProps } from './props'
|
||||
|
||||
@@ -50,7 +50,7 @@ defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
|
||||
defineProps(timePickerRangeTriggerProps)
|
||||
const props = defineProps(timePickerRangeTriggerProps)
|
||||
const emit = defineEmits([
|
||||
'mouseenter',
|
||||
'mouseleave',
|
||||
@@ -71,7 +71,9 @@ const nsRange = useNamespace('range')
|
||||
const inputRef = ref<HTMLInputElement>()
|
||||
const endInputRef = ref<HTMLInputElement>()
|
||||
|
||||
const { wrapperRef, isFocused } = useFocusController(inputRef)
|
||||
const { wrapperRef, isFocused } = useFocusController(inputRef, {
|
||||
disabled: computed(() => props.disabled),
|
||||
})
|
||||
|
||||
const handleClick = (evt: MouseEvent) => {
|
||||
emit('click', evt)
|
||||
|
||||
@@ -246,9 +246,14 @@ const pickerActualVisible = ref(false)
|
||||
const valueOnOpen = ref<TimePickerDefaultProps['modelValue'] | null>(null)
|
||||
let hasJustTabExitedInput = false
|
||||
|
||||
const pickerDisabled = computed(() => {
|
||||
return props.disabled || !!form?.disabled
|
||||
})
|
||||
|
||||
const { isFocused, handleFocus, handleBlur } = useFocusController(inputRef, {
|
||||
disabled: pickerDisabled,
|
||||
beforeFocus() {
|
||||
return props.readonly || pickerDisabled.value
|
||||
return props.readonly
|
||||
},
|
||||
afterFocus() {
|
||||
pickerVisible.value = true
|
||||
@@ -385,10 +390,6 @@ const handleClose = () => {
|
||||
pickerVisible.value = false
|
||||
}
|
||||
|
||||
const pickerDisabled = computed(() => {
|
||||
return props.disabled || form?.disabled
|
||||
})
|
||||
|
||||
const parsedValue = computed(() => {
|
||||
let dayOrDays: DayOrDays
|
||||
if (valueIsEmpty.value) {
|
||||
|
||||
@@ -225,4 +225,34 @@ describe('useFocusController', () => {
|
||||
expect(wrapper.find('span').text()).toBe('false')
|
||||
expect(blurHandler).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('It will decide whether to trigger focus and blur based on the disabled state', async () => {
|
||||
const disabled = ref(true)
|
||||
const wrapper = mount({
|
||||
emits: ['focus', 'blur'],
|
||||
setup() {
|
||||
const targetRef = ref()
|
||||
const { isFocused, wrapperRef } = useFocusController(targetRef, {
|
||||
disabled,
|
||||
})
|
||||
|
||||
return () => (
|
||||
<div ref={wrapperRef}>
|
||||
<input ref={targetRef} />
|
||||
<span>{String(isFocused.value)}</span>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
await wrapper.find('input').trigger('focus')
|
||||
expect(wrapper.emitted()).not.toHaveProperty('focus')
|
||||
expect(wrapper.find('span').text()).toBe('false')
|
||||
|
||||
disabled.value = false
|
||||
await nextTick()
|
||||
await wrapper.find('input').trigger('focus')
|
||||
expect(wrapper.emitted()).toHaveProperty('focus')
|
||||
expect(wrapper.find('span').text()).toBe('true')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
import { getCurrentInstance, onMounted, ref, shallowRef, watch } from 'vue'
|
||||
import {
|
||||
getCurrentInstance,
|
||||
onMounted,
|
||||
ref,
|
||||
shallowRef,
|
||||
unref,
|
||||
watch,
|
||||
} from 'vue'
|
||||
import { useEventListener } from '@vueuse/core'
|
||||
import { isElement, isFocusable, isFunction } from '@element-plus/utils'
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { useFormDisabled } from '@element-plus/components/form/src/hooks/use-form-common-props' // TODO: remove this
|
||||
|
||||
import type { ShallowRef } from 'vue'
|
||||
import type { MaybeRef } from '@vueuse/core'
|
||||
|
||||
interface UseFocusControllerOptions {
|
||||
disabled?: MaybeRef<boolean>
|
||||
/**
|
||||
* return true to cancel focus
|
||||
* @param event FocusEvent
|
||||
@@ -24,6 +31,7 @@ interface UseFocusControllerOptions {
|
||||
export function useFocusController<T extends { focus: () => void }>(
|
||||
target: ShallowRef<T | undefined>,
|
||||
{
|
||||
disabled,
|
||||
beforeFocus,
|
||||
afterFocus,
|
||||
beforeBlur,
|
||||
@@ -33,12 +41,12 @@ export function useFocusController<T extends { focus: () => void }>(
|
||||
const instance = getCurrentInstance()!
|
||||
const { emit } = instance
|
||||
const wrapperRef = shallowRef<HTMLElement>()
|
||||
const disabled = useFormDisabled()
|
||||
const isFocused = ref(false)
|
||||
|
||||
const handleFocus = (event: FocusEvent) => {
|
||||
const cancelFocus = isFunction(beforeFocus) ? beforeFocus(event) : false
|
||||
if (cancelFocus || isFocused.value) return
|
||||
if (unref(disabled) || isFocused.value || cancelFocus) return
|
||||
|
||||
isFocused.value = true
|
||||
emit('focus', event)
|
||||
afterFocus?.()
|
||||
@@ -47,9 +55,10 @@ export function useFocusController<T extends { focus: () => void }>(
|
||||
const handleBlur = (event: FocusEvent) => {
|
||||
const cancelBlur = isFunction(beforeBlur) ? beforeBlur(event) : false
|
||||
if (
|
||||
cancelBlur ||
|
||||
unref(disabled) ||
|
||||
(event.relatedTarget &&
|
||||
wrapperRef.value?.contains(event.relatedTarget as Node))
|
||||
wrapperRef.value?.contains(event.relatedTarget as Node)) ||
|
||||
cancelBlur
|
||||
)
|
||||
return
|
||||
|
||||
@@ -60,17 +69,17 @@ export function useFocusController<T extends { focus: () => void }>(
|
||||
|
||||
const handleClick = (event: Event) => {
|
||||
if (
|
||||
(wrapperRef.value?.contains(document.activeElement) &&
|
||||
wrapperRef.value !== document.activeElement) ||
|
||||
unref(disabled) ||
|
||||
isFocusable(event.target as HTMLElement) ||
|
||||
disabled.value
|
||||
(wrapperRef.value?.contains(document.activeElement) &&
|
||||
wrapperRef.value !== document.activeElement)
|
||||
)
|
||||
return
|
||||
|
||||
target.value?.focus()
|
||||
}
|
||||
|
||||
watch([wrapperRef, disabled], ([el, disabled]) => {
|
||||
watch([wrapperRef, () => unref(disabled)], ([el, disabled]) => {
|
||||
if (!el) return
|
||||
if (disabled) {
|
||||
el.removeAttribute('tabindex')
|
||||
|
||||
Reference in New Issue
Block a user