From 53d6bb9138be4eb19a85f0722253a7aba3065cc9 Mon Sep 17 00:00:00 2001 From: Yuchao Date: Tue, 7 May 2024 01:58:09 +1000 Subject: [PATCH] fix(VNumberInput): use VTextField as the base component (#19714) fixes #19659 fixes #19757 --- .../src/labs/VNumberInput/VNumberInput.tsx | 184 +++++++----------- 1 file changed, 72 insertions(+), 112 deletions(-) diff --git a/packages/vuetify/src/labs/VNumberInput/VNumberInput.tsx b/packages/vuetify/src/labs/VNumberInput/VNumberInput.tsx index ea9b8cced49..3fa97c4cf7a 100644 --- a/packages/vuetify/src/labs/VNumberInput/VNumberInput.tsx +++ b/packages/vuetify/src/labs/VNumberInput/VNumberInput.tsx @@ -5,27 +5,24 @@ import './VNumberInput.sass' import { VBtn } from '../../components/VBtn' import { VDefaultsProvider } from '../../components/VDefaultsProvider' import { VDivider } from '../../components/VDivider' -import { filterFieldProps, makeVFieldProps, VField } from '@/components/VField/VField' -import { makeVInputProps, VInput } from '@/components/VInput/VInput' +import { makeVTextFieldProps, VTextField } from '@/components/VTextField/VTextField' // Composables -import { makeFocusProps, useFocus } from '@/composables/focus' import { useProxiedModel } from '@/composables/proxiedModel' // Utilities -import { computed, ref, watchEffect } from 'vue' -import { clamp, filterInputAttrs, genericComponent, getDecimals, only, propsFactory, useRender } from '@/util' +import { computed, watchEffect } from 'vue' +import { clamp, genericComponent, getDecimals, omit, propsFactory, useRender } from '@/util' // Types import type { PropType } from 'vue' -import type { VFieldSlots } from '@/components/VField/VField' -import type { VInputSlots } from '@/components/VInput/VInput' +import type { VTextFieldSlots } from '@/components/VTextField/VTextField' type ControlSlot = { click: () => void } -type VNumberInputSlots = Omit & { +type VNumberInputSlots = Omit & { increment: ControlSlot decrement: ControlSlot } @@ -52,31 +49,7 @@ const makeVNumberInputProps = propsFactory({ default: 1, }, - ...only(makeVInputProps(), [ - 'density', - 'disabled', - 'focused', - 'hideDetails', - 'hint', - 'label', - 'persistentHint', - 'readonly', - ]), - ...only(makeVFieldProps(), [ - 'baseColor', - 'bgColor', - 'class', - 'color', - 'disabled', - 'error', - 'loading', - 'reverse', - 'rounded', - 'style', - 'theme', - 'variant', - ]), - ...makeFocusProps(), + ...omit(makeVTextFieldProps(), ['appendInnerIcon', 'prependInnerIcon']), }, 'VNumberInput') export const VNumberInput = genericComponent()({ @@ -86,11 +59,6 @@ export const VNumberInput = genericComponent()({ props: { ...makeVNumberInputProps(), - - modelValue: { - type: Number, - default: undefined, - }, }, emits: { @@ -99,8 +67,6 @@ export const VNumberInput = genericComponent()({ setup (props, { attrs, emit, slots }) { const model = useProxiedModel(props, 'modelValue') - const { isFocused, focus, blur } = useFocus(props) - const inputRef = ref() const stepDecimals = computed(() => getDecimals(props.step)) const modelDecimals = computed(() => model.value != null ? getDecimals(model.value) : 0) @@ -120,10 +86,6 @@ export const VNumberInput = genericComponent()({ } }) - function onFocus () { - if (!isFocused.value) focus() - } - const controlVariant = computed(() => { return props.hideInput ? 'stacked' : props.controlVariant }) @@ -156,7 +118,7 @@ export const VNumberInput = genericComponent()({ function onKeydown (e: KeyboardEvent) { if ( - ['Enter', 'ArrowLeft', 'ArrowRight', 'Backspace'].includes(e.key) || + ['Enter', 'ArrowLeft', 'ArrowRight', 'Backspace', 'Tab'].includes(e.key) || e.ctrlKey ) return @@ -177,15 +139,12 @@ export const VNumberInput = genericComponent()({ } } - function onInput (e: Event) { - const el = e.target as HTMLInputElement - model.value = el.value ? +(el.value) : undefined + function onModelUpdate (v: string) { + model.value = v ? +(v) : undefined } useRender(() => { - const fieldProps = filterFieldProps(props) - const [rootAttrs, inputAttrs] = filterInputAttrs(attrs) - const { modelValue: _, ...inputProps } = VInput.filterProps(props) + const { modelValue: _, ...textFieldProps } = VTextField.filterProps(props) function controlNode () { const defaultHeight = controlVariant.value === 'stacked' ? 'auto' : '100%' @@ -201,6 +160,7 @@ export const VNumberInput = genericComponent()({ name="decrement-btn" icon="$expand" size="small" + tabindex="-1" onClick={ onClickDown } /> ) : ( @@ -236,6 +196,7 @@ export const VNumberInput = genericComponent()({ icon="$collapse" onClick={ onClickUp } size="small" + tabindex="-1" /> ) : ( ()({ return !props.hideInput && !props.inset ? : undefined } + const appendInnerControl = + controlVariant.value === 'split' + ? ( +
+ + + +
+ ) : (!props.reverse + ? <>{ dividerNode() }{ controlNode() } + : undefined) + + const hasAppendInner = slots['append-inner'] || appendInnerControl + + const prependInnerControl = + controlVariant.value === 'split' + ? ( +
+ + + +
+ ) : (props.reverse + ? <>{ controlNode() }{ dividerNode() } + : undefined) + + const hasPrependInner = slots['prepend-inner'] || prependInnerControl + return ( - ()({ }, props.class, ]} - { ...rootAttrs } - { ...inputProps } - focused={ isFocused.value } + { ...textFieldProps } style={ props.style } > {{ ...slots, - default: () => ( - - {{ - ...slots, - default: ({ - props: { class: fieldClass, ...slotProps }, - }) => ( - - ), - 'append-inner': controlVariant.value === 'split' ? () => ( -
- - - -
- ) : (!props.reverse - ? () => <>{ dividerNode() }{ controlNode() } - : undefined), - 'prepend-inner': controlVariant.value === 'split' ? () => ( -
- - - -
- ) : (props.reverse - ? () => <>{ controlNode() }{ dividerNode() } - : undefined), - }} -
- ), + 'append-inner': hasAppendInner ? (...args) => ( + <> + { slots['append-inner']?.(...args) } + { appendInnerControl } + + ) : undefined, + 'prepend-inner': hasPrependInner ? (...args) => ( + <> + { prependInnerControl } + { slots['prepend-inner']?.(...args) } + + ) : undefined, }} -
+ ) }) },