fix(vfield): standardize descendants to fix spacing
Description
Standardized the sizing and spacing of VInput-VField descendants to fix multiple bugs relating to field resizing and text alignment. All field variants now have identical sizing whether they contain input or not. This required changing the size of VChip elements inside a VField. Fixes #15980, #16048, and #15948
Motivation and Context
Fixes #15980, #16048, and #15948, as well as several other unreported spacing and resizing issues. Fields should no longer resize on input, and cursors and text will stay in one position regardless of field state.
How Has This Been Tested?
Comprehensive visual testing (see markup below). Duplicated in Codepen link to compare with current version: https://codepen.io/kelliedlynch/pen/qBKXBPN
Markup:
<template>
<v-app>
<v-container fluid>
<v-row class="justify-start" style="background-color:azure">
<v-col cols="2">
<v-checkbox
label="Prepend Icon"
v-model="prependIcon"
density="none"
/>
<v-checkbox
label="Prepend Inner Icon"
v-model="prependInnerIcon"
density="none"
/>
</v-col>
<v-col cols="2">
<v-checkbox
label="Append Icon"
v-model="appendIcon"
density="none"
/>
<v-checkbox
label="Append Inner Icon"
v-model="appendInnerIcon"
density="none"
/>
</v-col>
<v-col cols="2">
<v-select
label="Variant"
:items="variants"
v-model="variant"
density="compact"
/>
</v-col>
<v-col cols="2">
<v-select
label="Category of Field"
:items="pages"
v-model="page"
density="compact"
/>
</v-col>
</v-row>
<v-row class="flex-wrap" style="margin-top:12px">
<!-- TEXT FIELDS -->
<v-col cols="2" class="pa-1" >
<v-container v-for="showLabel in showLabels" class="pa-0" >
<v-container v-for="density in densities" class="pa-0">
<v-text-field
:label="showLabel ? 'Text Field Labeled ' + ' ' + density + ' Dirty' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
:model-value="showLabel ? 'Text Labeled ' + density + ' Dirty' : 'Text No-label ' + density + ' Dirty'"
:placeholder="!showLabel ? 'Text No-Label ' + density + ' Dirty' : ''"
clearable
:prepend-icon="prependIcon ? 'mdi-map-marker' : ''"
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
:append-inner-icon="appendInnerIcon ? 'mdi-map-marker' : ''"
></v-text-field>
</v-container>
<v-container v-for="density in densities" class="pa-0">
<v-text-field
:label="showLabel ? 'Text Field Labeled' + ' ' + density + ' Clean' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
:placeholder="!showLabel ? 'Text No-Label ' + density + ' Clean' : ''"
clearable
:prepend-icon="prependIcon ? 'mdi-map-marker' : ''"
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
:append-inner-icon="appendInnerIcon ? 'mdi-map-marker' : ''"
></v-text-field>
</v-container>
</v-container>
</v-col>
<!-- SINGLE-LINE TEXT FIELDS -->
<v-col cols="2" class="pa-1" v-if="page == 'Text Fields'">
<v-container v-for="density in densities" class="pa-0">
<v-text-field
:label="'Text Field Single Line ' + ' ' + density + ' Dirty'"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
:model-value="'Text Single Line ' + density + ' Dirty'"
clearable
:prepend-icon="prependIcon ? 'mdi-map-marker' : ''"
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
:append-inner-icon="appendInnerIcon ? 'mdi-map-marker' : ''"
single-line
></v-text-field>
</v-container>
<v-container v-for="density in densities" class="pa-0">
<v-text-field
:label="'Text Field Single Line ' + ' ' + density + ' Clean'"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
clearable
:prepend-icon="prependIcon ? 'mdi-map-marker' : ''"
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
:append-inner-icon="appendInnerIcon ? 'mdi-map-marker' : ''"
single-line
></v-text-field>
</v-container>
</v-col>
<!-- TEXTAREAS -->
<v-col cols="2" class="pa-1" v-if="page == 'Textareas'" v-for="showLabel in showLabels">
<v-container v-for="density in densities" class="pa-0">
<VTextarea
:label="showLabel ? 'Textarea Labeled ' + ' ' + density + ' Dirty' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
:model-value="showLabel ? 'Text Labeled ' + density + ' Dirty' : 'Text No-label ' + density + ' Dirty'"
:placeholder="!showLabel ? 'Text No-Label ' + density + ' Dirty' : ''"
clearable
auto-grow
rows="1"
:prepend-icon="prependIcon ? 'mdi-map-marker' : ''"
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
:append-inner-icon="appendInnerIcon ? 'mdi-map-marker' : ''"
></VTextarea>
</v-container>
<v-container v-for="density in densities" class="pa-0">
<v-textarea
:label="showLabel ? 'Textarea Labeled' + ' ' + density + ' Clean' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
:placeholder="!showLabel ? 'Text No-Label ' + density + ' Clean' : ''"
clearable
auto-grow
rows="1"
:prepend-icon="prependIcon ? 'mdi-map-marker' : ''"
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
:append-inner-icon="appendInnerIcon ? 'mdi-map-marker' : ''"
></v-textarea>
</v-container>
</v-col>
<!-- SELECT WITH CHIPS -->
<v-col cols="2" class="pa-1" v-if="page == 'Selects'">
<v-container v-for="showLabel in showLabels" class="pa-0" >
<v-container v-for="density in densities" class="pa-0">
<v-select
:label="showLabel ? 'Select Chips' + ' ' + density + ' Dirty' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
:items="items"
v-model="selected"
chips
closable-chips
multiple
clearable
:prepend-icon="prependIcon ? 'mdi-map-marker' : ''"
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
></v-select>
</v-container>
<v-container v-for="density in densities" class="pa-0">
<v-select
:label="showLabel ? 'Select Chips' + ' ' + density + ' Clean' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
:items="items"
v-model="unselected"
chips
closable-chips
multiple
clearable
:prepend-icon="prependIcon ? 'mdi-map-marker' : ''"
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
></v-select>
</v-container>
</v-container>
</v-col>
<!-- SELECT WITHOUT CHIPS -->
<v-col cols="2" class="pa-1" v-if="page == 'Selects'">
<v-container v-for="showLabel in showLabels" class="pa-0" >
<v-container v-for="density in densities" class="pa-0">
<v-select
:label="showLabel ? 'Select No-chips' + ' ' + density + ' Dirty' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
:items="items"
v-model="selected"
multiple
clearable
:prepend-icon="prependIcon ? 'mdi-map-marker' : ''"
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
></v-select>
</v-container>
<v-container v-for="density in densities" class="pa-0">
<v-select
:label="showLabel ? 'Select No-chips' + ' ' + density + ' Clean' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
:items="items"
v-model="unselected"
multiple
clearable
:prepend-icon="prependIcon ? 'mdi-map-marker' : ''"
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
></v-select>
</v-container>
</v-container>
</v-col>
<!-- COMBOBOX WITH CHIPS -->
<v-col cols="2" class="pa-1" v-if="page == 'Combos'">
<v-container v-for="showLabel in showLabels" class="pa-0" >
<v-container v-for="density in densities" class="pa-0">
<v-combobox
:label="showLabel ? 'Combobox Chips' + ' ' + density + ' Dirty' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
:items="items"
v-model="selected"
chips
closable-chips
multiple
clearable
:prepend-icon="prependIcon ? 'mdi-map-marker' : ''"
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
></v-combobox>
</v-container>
<v-container v-for="density in densities" class="pa-0">
<v-combobox
:label="showLabel ? 'Combobox Chips' + ' ' + density + ' Clean' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
:items="items"
v-model="unselected"
chips
closable-chips
multiple
clearable
:prepend-icon="prependIcon ? 'mdi-map-marker' : ''"
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
></v-combobox>
</v-container>
</v-container>
</v-col>
<!-- COMBOBOX WITHOUT CHIPS -->
<v-col cols="2" class="pa-1" v-if="page == 'Combos'">
<v-container v-for="showLabel in showLabels" class="pa-0" >
<v-container v-for="density in densities" class="pa-0">
<v-combobox
:label="showLabel ? 'Combobox No-chips' + ' ' + density + ' Dirty' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
:items="items"
v-model="selected"
multiple
clearable
:prepend-icon="prependIcon ? 'mdi-map-marker' : ''"
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
></v-combobox>
</v-container>
<v-container v-for="density in densities" class="pa-0">
<v-combobox
:label="showLabel ? 'Combobox No-chips' + ' ' + density + ' Clean' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
:items="items"
v-model="unselected"
multiple
clearable
:prepend-icon="prependIcon ? 'mdi-map-marker' : ''"
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
></v-combobox>
</v-container>
</v-container>
</v-col>
<!-- AUTOCOMPLETE WITH CHIPS -->
<v-col cols="2" class="pa-1" v-if="page == 'Combos'">
<v-container v-for="showLabel in showLabels" class="pa-0" >
<v-container v-for="density in densities" class="pa-0">
<v-autocomplete
:label="showLabel ? 'Autocomplete Chips' + ' ' + density + ' Dirty' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
:items="items"
v-model="selected"
chips
closable-chips
multiple
clearable
:prepend-icon="prependIcon ? 'mdi-map-marker' : ''"
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
></v-autocomplete>
</v-container>
<v-container v-for="density in densities" class="pa-0">
<v-autocomplete
:label="showLabel ? 'Autocomplete Chips' + ' ' + density + ' Clean' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
:items="items"
v-model="unselected"
chips
closable-chips
multiple
clearable
:prepend-icon="prependIcon ? 'mdi-map-marker' : ''"
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
></v-autocomplete>
</v-container>
</v-container>
</v-col>
<!-- AUTOCOMPLETE WITHOUT CHIPS -->
<v-col cols="2" class="pa-1" v-if="page == 'Combos'">
<v-container v-for="showLabel in showLabels" class="pa-0" >
<v-container v-for="density in densities" class="pa-0">
<v-autocomplete
:label="showLabel ? 'Autocomplete No-chips' + ' ' + density + ' Dirty' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
:items="items"
v-model="selected"
multiple
clearable
:prepend-icon="prependIcon ? 'mdi-map-marker' : ''"
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
></v-autocomplete>
</v-container>
<v-container v-for="density in densities" class="pa-0">
<v-autocomplete
:label="showLabel ? 'Autocomplete No-chips' + ' ' + density + ' Clean' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
:items="items"
v-model="unselected"
multiple
clearable
:prepend-icon="prependIcon ? 'mdi-map-marker' : ''"
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
></v-autocomplete>
</v-container>
</v-container>
</v-col>
<!-- FILE INPUT WITH CHIPS -->
<v-col cols="2" class="pa-1" v-if="page == 'Text Fields'">
<v-container v-for="showLabel in showLabels" class="pa-0" >
<v-container v-for="density in densities" class="pa-0">
<v-file-input
:label="showLabel ? 'File Input Chips' + ' ' + density + ' Dirty' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
chips
closable-chips
multiple
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
:append-inner-icon="appendInnerIcon ? 'mdi-map-marker' : ''"
></v-file-input>
</v-container>
<v-container v-for="density in densities" class="pa-0">
<v-file-input
:label="showLabel ? 'File Input Chips' + ' ' + density + ' Clean' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
chips
closable-chips
multiple
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
:append-inner-icon="appendInnerIcon ? 'mdi-map-marker' : ''"
></v-file-input>
</v-container>
</v-container>
</v-col>
<!-- FILE INPUT WITHOUT CHIPS -->
<v-col cols="2" class="pa-1" v-if="page == 'Text Fields'" >
<v-container v-for="showLabel in showLabels" class="pa-0" >
<v-container v-for="density in densities" class="pa-0">
<v-file-input
:label="showLabel ? 'File Input No-chips' + ' ' + density + ' Dirty' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
multiple
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
:append-inner-icon="appendInnerIcon ? 'mdi-map-marker' : ''"
></v-file-input>
</v-container>
<v-container v-for="density in densities" class="pa-0">
<v-file-input
:label="showLabel ? 'File Input No-chips' + ' ' + density + ' Clean' : ''"
:variant="variant.toLowerCase()"
:density="density.toLowerCase()"
multiple
:prepend-inner-icon="prependInnerIcon ? 'mdi-map-marker' : ''"
:append-icon="appendIcon ? 'mdi-map-marker' : ''"
:append-inner-icon="appendInnerIcon ? 'mdi-map-marker' : ''"
></v-file-input>
</v-container>
</v-container>
</v-col>
</v-row>
</v-container>
</v-app>
</template>
<script>
export default {
name: 'Playground',
setup () {
},
data: () => {
return {
showLabels: [
true, false
],
prependIcon: false,
prependInnerIcon: false,
appendIcon: false,
appendInnerIcon: false,
pages: [
"Text Fields",
"Selects",
"Combos",
"Textareas"
],
page: "Text Fields",
variants: [
"Filled",
"Outlined",
"Solo",
"Plain",
"Underlined",
],
variant: "Filled",
usesChips: [
true, false
],
items: [
"lorem",
"ipsum dolor sit amet",
"consectetur",
"adipiscing elit",
"ut enim ad minim veniam",
"quis nostrud",
"exercitation ullamco laboris"],
selected: [
"lorem",
],
unselected: [],
densities: [
"Compact",
"Comfortable",
"Default",
],
labeled: [
"Labeled",
"Unlabeled",
],
isFieldClean: [
true, false
]
}
}
}
</script>
Types of changes
- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [x] Improvement/refactoring (non-breaking change that doesn't add any features but makes things better)
Checklist:
- [x] The PR title is no longer than 64 characters.
- [x] The PR is submitted to the correct branch (
masterfor bug fixes and documentation updates,devfor new features and backwards compatible changes andnextfor non-backwards compatible changes). - [x] My code follows the code style of this project.
- [ ] I've added relevant changes to the documentation (applies to new features and breaking changes in core library)
Looking good but there are some alignment issues for filled inputs:

Easiest way to see this is by building Vuetify and running the docs locally and check out the Form & input controls
Whoops--accidentally deleted something I shouldn't in my final cleanup. I'll have the fix up shortly.
This is what I see when I compare your branch to next:

Can you clarify the issue? The only difference I see is that next has inconsistent sizing, and mine doesn't. From looking at constants and defaults in next, it looks like the larger size (42/48/56px) should be the correct one, and that's the size that allows enough room for field elements.
Can you clarify the issue? The only difference I see is that next has inconsistent sizing, and mine doesn't. From looking at constants and defaults in next, it looks like the larger size (42/48/56px) should be the correct one, and that's the size that allows enough room for field elements.
The label is too close to the top of the input and looks offset improperly. The label should be closer to the input text:

Got it, thanks for the clarification. Should be fixed now.
Prefix and suffix are broken:
Underlined has too much space below the label and value:

Are you still planning to work on this?
Thanks for the reminder, I will get back on this.