Bläddra i källkod

Form: label-width supports width auto (#14944)

hetech 6 år sedan
förälder
incheckning
a408b3dd3f

+ 2 - 2
examples/docs/en-US/form.md

@@ -593,7 +593,7 @@ All components in a Form inherit their `size` attribute from that Form. Similarl
 | rules | validation rules of form | object | — | — |
 | inline | whether the form is inline | boolean | — | false |
 | label-position | position of label. If set to 'left' or 'right', `label-width` prop is also required | string | left / right / top | right |
-| label-width | width of label, and all its direct child form items will inherit this value | string | — | — |
+| label-width    | width of label, e.g. '50px'. All its direct child form items will inherit this value. Width `auto` is supported.        | string | — | — |
 | label-suffix | suffix of the label | string | — | — |
 | hide-required-asterisk | whether required fields should have a red asterisk (star) beside their labels | boolean | — | false |
 | show-message  | whether to show the error message | boolean | — | true |
@@ -623,7 +623,7 @@ All components in a Form inherit their `size` attribute from that Form. Similarl
 | ---- | ----| ---- | ---- | ---- |
 | prop | a key of `model`. In the use of validate and resetFields method, the attribute is required | string | keys of model that passed to `form` |
 | label | label | string | — | — |
-| label-width | width of label, e.g. '50px' | string | — | — |
+| label-width | width of label, e.g. '50px'. Width `auto` is supported. | string | — | — |
 | required | whether the field is required or not, will be determined by validation rules if omitted | boolean |  — | false |
 | rules | validation rules of form | object | — | — |
 | error | field error message, set its value and the field will validate error and show this message immediately | string | — | — |

+ 2 - 2
examples/docs/es/form.md

@@ -607,7 +607,7 @@ Todos los componentes de un formulario heredan su atributo `size`. De manera sim
 | rules                   | Reglas de validación                     | object  | —                     | —           |
 | inline                  | Si el form es inline                     | boolean | —                     | false       |
 | label-position          | Posicion de la etiqueta                  | string  | left / right / top    | right       |
-| label-width             | ancho de la etiqueta, y todos los form items directos descendientes heredarán este valor | string  | —                     | —           |
+| label-width             | width of label, e.g. '50px'. All its direct child form items will inherit this value. Width `auto` is supported.        | string | — | — |
 | label-suffix            | sufijo de la etiqueta                    | string  | —                     | —           |
 | hide-required-asterisk       | si los campos obligatorios deben tener un asterisco rojo (estrella) al lado de sus etiquetas | boolean | — | false |
 | show-message            | si mostrar o no el mensaje de error      | boolean | —                     | true        |
@@ -638,7 +638,7 @@ Todos los componentes de un formulario heredan su atributo `size`. De manera sim
 | -------------- | ------------------------------------------------------------ | ------- | ------------------------------------------- | ----------- |
 | prop           | un clave del modelo. En el uso del método validate and resetFields, el atributo es obligatorio. | string  | Clave del modelo que se ha pasado a  `form` |             |
 | label          | etiqueta                                                     | string  | —                                           | —           |
-| label-width    | ancho de la etiqueta, e.g. '50px'                            | string  | —                                           | —           |
+| label-width    | ancho de la etiqueta, e.g. '50px'. Width `auto` is supported | string  | —                                           | —           |
 | required       | si el campo es obligatorio o no, estará determinado por las reglas de validación si se omite. | boolean | —                                           | false       |
 | rules          | reglas de validacion del form                                | object  | —                                           | —           |
 | error          | mensaje de error de campo, establezca su valor y el campo validará el error y mostrará este mensaje inmediatamente. | string  | —                                           | —           |

+ 2 - 2
examples/docs/fr-FR/form.md

@@ -592,7 +592,7 @@ Tout les composants d'un formulaire héritent leur attribut `size` de ce formula
 | rules | Règles de validation du formulaire. | object | — | — |
 | inline | Si le formulaire est horizontal. | boolean | — | false |
 | label-position | Position des labels. Si 'left' ou 'right', `label-width` est aussi requis. | string | left / right / top | right |
-| label-width | Largeur des labels, tout les enfants directs hériteront de cette valeur. | string | — | — |
+| label-width | Largeur des labels, tout les enfants directs hériteront de cette valeur. Width `auto` is supported. | string | — | — |
 | label-suffix | Suffixe de labels. | string | — | — |
 | hide-required-asterisk | Si les champs obligatoires doivent avoir une astérisque rouge (étoile) à coté de leurs labels. | boolean | — | false |
 | show-message  | Si le message d'erreur doit apparaître. | boolean | — | true |
@@ -623,7 +623,7 @@ Tout les composants d'un formulaire héritent leur attribut `size` de ce formula
 | ---- | ----| ---- | ---- | ---- |
 | prop | Une des clés de `model`. Utilisés par les méthodes validate et resetFields. Requis. | string | Clés du model passé à `form`. |
 | label | Le label. | string | — | — |
-| label-width | Largeur du label, e.g. '50px'. | string | — | — |
+| label-width | Largeur du label, e.g. '50px'. Width `auto` is supported. | string | — | — |
 | required | Si le champ est requis ou non. Si omis, sera déterminé par les règles de validation. | boolean |  — | false |
 | rules | Règles de validation du formulaire. | object | — | — |
 | error | Message d'erreur du champ. Si il est modifié, le champ l'affichera immédiatement. | string | — | — |

+ 2 - 2
examples/docs/zh-CN/form.md

@@ -588,7 +588,7 @@ W3C 标准中有如下[规定](https://www.w3.org/MarkUp/html-spec/html-spec_8.h
 | rules    | 表单验证规则 | object | — | — |
 | inline    | 行内表单模式 | boolean | — | false |
 | label-position | 表单域标签的位置,如果值为 left 或者 right 时,则需要设置 `label-width` | string |  right/left/top            | right |
-| label-width | 表单域标签的宽度,作为 Form 直接子元素的 form-item 会继承该值 | string | — | — |
+| label-width | 表单域标签的宽度,例如 '50px'。作为 Form 直接子元素的 form-item 会继承该值。支持 `auto`。 | string | — | — |
 | label-suffix | 表单域标签的后缀 | string | — | — |
 | hide-required-asterisk | 是否显示必填字段的标签旁边的红色星号 | boolean | — | false |
 | show-message  | 是否显示校验错误信息 | boolean | — | true |
@@ -618,7 +618,7 @@ W3C 标准中有如下[规定](https://www.w3.org/MarkUp/html-spec/html-spec_8.h
 |---------- |-------------- |---------- |--------------------------------  |-------- |
 | prop    | 表单域 model 字段,在使用 validate、resetFields 方法的情况下,该属性是必填的 | string    | 传入 Form 组件的 `model` 中的字段 | — |
 | label | 标签文本 | string | — | — |
-| label-width | 表单域标签的的宽度,例如 '50px' | string |       —       | — |
+| label-width | 表单域标签的的宽度,例如 '50px'。支持 `auto`。 | string |       —       | — |
 | required | 是否必填,如不设置,则会根据校验规则自动生成 | boolean | — | false |
 | rules    | 表单验证规则 | object | — | — |
 | error    | 表单域验证错误信息, 设置该值会使表单验证状态变为`error`,并显示该错误信息 | string | — | — |

+ 24 - 6
packages/form/src/form-item.vue

@@ -9,9 +9,13 @@
     },
     sizeClass ? 'el-form-item--' + sizeClass : ''
   ]">
-    <label :for="labelFor" class="el-form-item__label" :style="labelStyle" v-if="label || $slots.label">
-      <slot name="label">{{label + form.labelSuffix}}</slot>
-    </label>
+    <label-wrap
+      :is-auto-width="labelStyle && labelStyle.width === 'auto'"
+      :update-all="form.labelWidth === 'auto'">
+      <label :for="labelFor" class="el-form-item__label" :style="labelStyle" v-if="label || $slots.label">
+        <slot name="label">{{label + form.labelSuffix}}</slot>
+      </label>
+    </label-wrap>
     <div class="el-form-item__content" :style="contentStyle">
       <slot></slot>
       <transition name="el-zoom-in-top">
@@ -39,7 +43,7 @@
   import emitter from 'element-ui/src/mixins/emitter';
   import objectAssign from 'element-ui/src/utils/merge';
   import { noop, getPropByPath } from 'element-ui/src/utils/util';
-
+  import LabelWrap from './label-wrap';
   export default {
     name: 'ElFormItem',
 
@@ -77,6 +81,10 @@
       },
       size: String
     },
+    components: {
+      // use this component to calculate auto width
+      LabelWrap
+    },
     watch: {
       error: {
         immediate: true,
@@ -108,7 +116,13 @@
         if (this.form.labelPosition === 'top' || this.form.inline) return ret;
         if (!label && !this.labelWidth && this.isNested) return ret;
         const labelWidth = this.labelWidth || this.form.labelWidth;
-        if (labelWidth) {
+        if (labelWidth === 'auto') {
+          if (this.labelWidth === 'auto') {
+            ret.marginLeft = this.computedLabelWidth;
+          } else if (this.form.labelWidth === 'auto') {
+            ret.marginLeft = this.elForm.autoLabelWidth;
+          }
+        } else {
           ret.marginLeft = labelWidth;
         }
         return ret;
@@ -167,7 +181,8 @@
         validateMessage: '',
         validateDisabled: false,
         validator: {},
-        isNested: false
+        isNested: false,
+        computedLabelWidth: ''
       };
     },
     methods: {
@@ -261,6 +276,9 @@
         }
 
         this.validate('change');
+      },
+      updateComputedLabelWidth(width) {
+        this.computedLabelWidth = width ? `${width}px` : '';
       }
     },
     mounted() {

+ 28 - 1
packages/form/src/form.vue

@@ -54,9 +54,16 @@
         }
       }
     },
+    computed: {
+      autoLabelWidth() {
+        const max = Math.max(...this.potentialLabelWidthArr);
+        return max ? `${max}px` : '';
+      }
+    },
     data() {
       return {
-        fields: []
+        fields: [],
+        potentialLabelWidthArr: [] // use this array to calculate auto width
       };
     },
     created() {
@@ -142,6 +149,26 @@
         fields.forEach(field => {
           field.validate('', cb);
         });
+      },
+      getLabelWidthIndex(width) {
+        const index = this.potentialLabelWidthArr.indexOf(width);
+        // it's impossible
+        if (index === -1) {
+          throw new Error('[ElementForm]unpected width ', width);
+        }
+        return index;
+      },
+      registerLabelWidth(val, oldVal) {
+        if (val && oldVal) {
+          const index = this.getLabelWidthIndex(oldVal);
+          this.potentialLabelWidthArr.splice(index, 1, val);
+        } else if (val) {
+          this.potentialLabelWidthArr.push(val);
+        }
+      },
+      deregisterLabelWidth(val) {
+        const index = this.getLabelWidthIndex(val);
+        this.potentialLabelWidthArr.splice(index, 1);
       }
     }
   };

+ 71 - 0
packages/form/src/label-wrap.vue

@@ -0,0 +1,71 @@
+<script>
+
+export default {
+  props: {
+    isAutoWidth: Boolean,
+    updateAll: Boolean
+  },
+
+  inject: ['elForm', 'elFormItem'],
+
+  render() {
+    const slots = this.$slots.default;
+    if (!slots) return null;
+    if (this.isAutoWidth) {
+      return (<div class="el-form-item__label-wrap">
+        { slots }
+      </div>);
+    } else {
+      return slots[0];
+    }
+  },
+
+  methods: {
+    getLabelWidth() {
+      if (this.$el && this.$el.firstElementChild) {
+        const computedWidth = window.getComputedStyle(this.$el.firstElementChild).width;
+        return Math.ceil(parseFloat(computedWidth));
+      } else {
+        return 0;
+      }
+    },
+    updateLabelWidth(action = 'update') {
+      if (this.$slots.default && this.isAutoWidth && this.$el.firstElementChild) {
+        if (action === 'update') {
+          this.computedWidth = this.getLabelWidth();
+        } else if (action === 'remove') {
+          this.elForm.deregisterLabelWidth(this.computedWidth);
+        }
+      }
+    }
+  },
+
+  watch: {
+    computedWidth(val, oldVal) {
+      if (this.updateAll) {
+        this.elForm.registerLabelWidth(val, oldVal);
+        this.elFormItem.updateComputedLabelWidth(val);
+      }
+    }
+  },
+
+  data() {
+    return {
+      computedWidth: 0
+    };
+  },
+
+  mounted() {
+    this.updateLabelWidth('update');
+  },
+
+  // Is this necessary?
+  // updated() {
+  //   this.updateLabelWidth('update');
+  // },
+
+  beforeDestroy() {
+    this.updateLabelWidth('remove');
+  }
+};
+</script>

+ 8 - 0
packages/theme-chalk/src/form.scss

@@ -84,6 +84,14 @@
     }
   }
 
+  @include e(label-wrap) {
+    float: left;
+    .el-form-item__label {
+      display: inline-block;
+      float: none;
+    }
+  }
+
   @include e(label) {
     text-align: right;
     vertical-align: middle;

+ 38 - 1
test/unit/specs/form.spec.js

@@ -1,4 +1,4 @@
-import { createVue, destroyVM } from '../util';
+import { createVue, destroyVM, waitImmediate } from '../util';
 
 const DELAY = 50;
 
@@ -43,6 +43,43 @@ describe('Form', () => {
     expect(vm.$el.querySelector('.el-form-item__content').style.marginLeft).to.equal('80px');
     done();
   });
+  it('auto label width', async() => {
+    vm = createVue({
+      template: `
+        <el-form ref="form" :model="form" label-width="auto">
+          <el-form-item label="活动名称">
+            <el-input v-model="form.name"></el-input>
+          </el-form-item>
+          <el-form-item label="活动备注信息" v-if="display">
+            <el-input v-model="form.name"></el-input>
+          </el-form-item>
+        </el-form>
+      `,
+      data() {
+        return {
+          display: true,
+          form: {
+            name: '',
+            intro: ''
+          }
+        };
+      }
+    }, true);
+
+    await waitImmediate();
+
+    const formItems = vm.$el.querySelectorAll('.el-form-item__content');
+    const marginLeft = parseInt(formItems[0].style.marginLeft, 10);
+    const marginLeft1 = parseInt(formItems[1].style.marginLeft, 10);
+    expect(marginLeft === marginLeft1).to.be.true;
+
+    vm.display = false;
+    await waitImmediate();
+
+    const formItem = vm.$el.querySelector('.el-form-item__content');
+    const newMarginLeft = parseInt(formItem.style.marginLeft, 10);
+    expect(newMarginLeft < marginLeft).to.be.true;
+  });
   it('inline form', done => {
     vm = createVue({
       template: `