Skip to content

Commit

Permalink
fix(inputs): normalise inheriting readonly and disabled from form
Browse files Browse the repository at this point in the history
fixes #20730
  • Loading branch information
KaelWD committed Nov 27, 2024
1 parent 54e430f commit b7df7f0
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export const VAutocomplete = genericComponent<new <
: typeof props.counterValue === 'number' ? props.counterValue
: model.value.length
})
const form = useForm()
const form = useForm(props)
const { filteredItems, getMatches } = useFilter(props, items, () => isPristine.value ? '' : search.value)

const displayItems = computed(() => {
Expand All @@ -192,7 +192,7 @@ export const VAutocomplete = genericComponent<new <

const menuDisabled = computed(() => (
(props.hideNoData && !displayItems.value.length) ||
props.readonly || form?.isReadonly.value
form.isReadonly.value || form.isDisabled.value
))

const listRef = ref<VList>()
Expand Down Expand Up @@ -224,7 +224,7 @@ export const VAutocomplete = genericComponent<new <
}
}
function onKeydown (e: KeyboardEvent) {
if (props.readonly || form?.isReadonly.value) return
if (form.isReadonly.value) return

const selectionStart = vTextFieldRef.value.selectionStart
const length = model.value.length
Expand Down Expand Up @@ -450,7 +450,7 @@ export const VAutocomplete = genericComponent<new <
props.class,
]}
style={ props.style }
readonly={ props.readonly }
readonly={ form.isReadonly.value }
placeholder={ isDirty ? undefined : props.placeholder }
onClick:clear={ onClear }
onMousedown:control={ onMousedownControl }
Expand Down
8 changes: 4 additions & 4 deletions packages/vuetify/src/components/VCombobox/VCombobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export const VCombobox = genericComponent<new <
return props.multiple ? transformed : (transformed[0] ?? null)
}
)
const form = useForm()
const form = useForm(props)

const hasChips = computed(() => !!(props.chips || slots.chip))
const hasSelectionSlot = computed(() => hasChips.value || !!slots.selection)
Expand Down Expand Up @@ -243,7 +243,7 @@ export const VCombobox = genericComponent<new <

const menuDisabled = computed(() => (
(props.hideNoData && !displayItems.value.length) ||
props.readonly || form?.isReadonly.value
form.isReadonly.value || form.isDisabled.value
))

const listRef = ref<VList>()
Expand Down Expand Up @@ -276,7 +276,7 @@ export const VCombobox = genericComponent<new <
}
// eslint-disable-next-line complexity
function onKeydown (e: KeyboardEvent) {
if (isComposingIgnoreKey(e) || props.readonly || form?.isReadonly.value) return
if (isComposingIgnoreKey(e) || form.isReadonly.value) return

const selectionStart = vTextFieldRef.value.selectionStart
const length = model.value.length
Expand Down Expand Up @@ -494,7 +494,7 @@ export const VCombobox = genericComponent<new <
props.class,
]}
style={ props.style }
readonly={ props.readonly }
readonly={ form.isReadonly.value }
placeholder={ isDirty ? undefined : props.placeholder }
onClick:clear={ onClear }
onMousedown:control={ onMousedownControl }
Expand Down
6 changes: 3 additions & 3 deletions packages/vuetify/src/components/VSelect/VSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ export const VSelect = genericComponent<new <
: typeof props.counterValue === 'number' ? props.counterValue
: model.value.length
})
const form = useForm()
const form = useForm(props)
const selectedValues = computed(() => model.value.map(selection => selection.value))
const isFocused = shallowRef(false)
const label = computed(() => menu.value ? props.closeText : props.openText)
Expand All @@ -184,7 +184,7 @@ export const VSelect = genericComponent<new <

const menuDisabled = computed(() => (
(props.hideNoData && !displayItems.value.length) ||
props.readonly || form?.isReadonly.value
form.isReadonly.value || form.isDisabled.value
))

const computedMenuProps = computed(() => {
Expand Down Expand Up @@ -215,7 +215,7 @@ export const VSelect = genericComponent<new <
}
}
function onKeydown (e: KeyboardEvent) {
if (!e.key || props.readonly || form?.isReadonly.value) return
if (!e.key || form.isReadonly.value) return

if (['Enter', ' ', 'ArrowDown', 'ArrowUp', 'Home', 'End'].includes(e.key)) {
e.preventDefault()
Expand Down
9 changes: 7 additions & 2 deletions packages/vuetify/src/composables/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ export function createForm (props: FormProps) {
}
}

export function useForm () {
return inject(FormKey, null)
export function useForm (props?: { readonly: boolean | null, disabled: boolean | null }) {
const form = inject(FormKey, null)
return {
...form,
isReadonly: computed(() => !!(props?.readonly ?? form?.isReadonly.value)),
isDisabled: computed(() => !!(props?.disabled ?? form?.isDisabled.value)),
}
}
22 changes: 10 additions & 12 deletions packages/vuetify/src/composables/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,22 +83,20 @@ export function useValidation (
) {
const model = useProxiedModel(props, 'modelValue')
const validationModel = computed(() => props.validationValue === undefined ? model.value : props.validationValue)
const form = useForm()
const form = useForm(props)
const internalErrorMessages = ref<string[]>([])
const isPristine = shallowRef(true)
const isDirty = computed(() => !!(
wrapInArray(model.value === '' ? null : model.value).length ||
wrapInArray(validationModel.value === '' ? null : validationModel.value).length
))
const isDisabled = computed(() => !!(props.disabled ?? form?.isDisabled.value))
const isReadonly = computed(() => !!(props.readonly ?? form?.isReadonly.value))
const errorMessages = computed(() => {
return props.errorMessages?.length
? wrapInArray(props.errorMessages).concat(internalErrorMessages.value).slice(0, Math.max(0, +props.maxErrors))
: internalErrorMessages.value
})
const validateOn = computed(() => {
let value = (props.validateOn ?? form?.validateOn.value) || 'input'
let value = (props.validateOn ?? form.validateOn?.value) || 'input'
if (value === 'lazy') value = 'input lazy'
if (value === 'eager') value = 'input eager'
const set = new Set(value?.split(' ') ?? [])
Expand All @@ -125,16 +123,16 @@ export function useValidation (
return {
[`${name}--error`]: isValid.value === false,
[`${name}--dirty`]: isDirty.value,
[`${name}--disabled`]: isDisabled.value,
[`${name}--readonly`]: isReadonly.value,
[`${name}--disabled`]: form.isDisabled.value,
[`${name}--readonly`]: form.isReadonly.value,
}
})

const vm = getCurrentInstance('validation')
const uid = computed(() => props.name ?? unref(id))

onBeforeMount(() => {
form?.register({
form.register?.({
id: uid.value,
vm,
validate,
Expand All @@ -144,14 +142,14 @@ export function useValidation (
})

onBeforeUnmount(() => {
form?.unregister(uid.value)
form.unregister?.(uid.value)
})

onMounted(async () => {
if (!validateOn.value.lazy) {
await validate(!validateOn.value.eager)
}
form?.update(uid.value, isValid.value, errorMessages.value)
form.update?.(uid.value, isValid.value, errorMessages.value)
})

useToggleScope(() => validateOn.value.input || (validateOn.value.invalidInput && isValid.value === false), () => {
Expand All @@ -175,7 +173,7 @@ export function useValidation (
})

watch([isValid, errorMessages], () => {
form?.update(uid.value, isValid.value, errorMessages.value)
form.update?.(uid.value, isValid.value, errorMessages.value)
})

async function reset () {
Expand Down Expand Up @@ -228,8 +226,8 @@ export function useValidation (
return {
errorMessages,
isDirty,
isDisabled,
isReadonly,
isDisabled: form.isDisabled,
isReadonly: form.isReadonly,
isPristine,
isValid,
isValidating,
Expand Down
6 changes: 3 additions & 3 deletions packages/vuetify/src/labs/VNumberInput/VNumberInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ export const VNumberInput = genericComponent<VNumberInputSlots>()({
const stepDecimals = computed(() => getDecimals(props.step))
const modelDecimals = computed(() => typeof model.value === 'number' ? getDecimals(model.value) : 0)

const form = useForm()
const form = useForm(props)
const controlsDisabled = computed(() => (
props.disabled || props.readonly || form?.isReadonly.value
form.isDisabled.value || form.isReadonly.value
))

const canIncrease = computed(() => {
Expand All @@ -119,7 +119,7 @@ export const VNumberInput = genericComponent<VNumberInputSlots>()({
const decrementSlotProps = computed(() => ({ click: onClickDown }))

onMounted(() => {
if (!props.readonly && !props.disabled) {
if (!controlsDisabled.value) {
clampModel()
}
})
Expand Down

0 comments on commit b7df7f0

Please sign in to comment.