vuetify icon indicating copy to clipboard operation
vuetify copied to clipboard

fix(vfield): standardize descendants to fix spacing

Open kelliedlynch opened this issue 3 years ago • 9 comments

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 (master for bug fixes and documentation updates, dev for new features and backwards compatible changes and next for 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)

kelliedlynch avatar Nov 19 '22 23:11 kelliedlynch

Looking good but there are some alignment issues for filled inputs:

image

Easiest way to see this is by building Vuetify and running the docs locally and check out the Form & input controls

johnleider avatar Nov 22 '22 16:11 johnleider

Whoops--accidentally deleted something I shouldn't in my final cleanup. I'll have the fix up shortly.

kelliedlynch avatar Nov 22 '22 18:11 kelliedlynch

This is what I see when I compare your branch to next:

next 15980

johnleider avatar Nov 29 '22 19:11 johnleider

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.

kelliedlynch avatar Nov 29 '22 21:11 kelliedlynch

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:

image

johnleider avatar Nov 30 '22 18:11 johnleider

Got it, thanks for the clarification. Should be fixed now.

kelliedlynch avatar Dec 02 '22 20:12 kelliedlynch

Prefix and suffix are broken: Screenshot_20221207_030905 Underlined has too much space below the label and value: Screenshot_20221207_031127

KaelWD avatar Dec 06 '22 16:12 KaelWD

Are you still planning to work on this?

johnleider avatar Apr 04 '23 16:04 johnleider

Thanks for the reminder, I will get back on this.

kelliedlynch avatar Apr 13 '23 15:04 kelliedlynch