element-plus
Version:
A Component Library for Vue3.0
442 lines (432 loc) • 16.5 kB
JavaScript
import { defineComponent, ref, inject, watch, onMounted, onUpdated, onBeforeUnmount, nextTick, h, Fragment, getCurrentInstance, computed, reactive, toRefs, provide, resolveComponent, openBlock, createBlock, createVNode, withCtx, renderSlot, createTextVNode, toDisplayString, createCommentVNode, Transition } from 'vue';
import AsyncValidator from 'async-validator';
import { addResizeListener, removeResizeListener } from '../utils/resize-event';
import { useGlobalConfig, getPropByPath } from '../utils/util';
import { isValidComponentSize } from '../utils/validators';
import mitt from 'mitt';
/**
* Make a map and return a function for checking if a key
* is in that map.
* IMPORTANT: all calls of this function must be prefixed with
* \/\*#\_\_PURE\_\_\*\/
* So that rollup can tree-shake them if necessary.
*/
const EMPTY_OBJ = (process.env.NODE_ENV !== 'production')
? Object.freeze({})
: {};
const EMPTY_ARR = (process.env.NODE_ENV !== 'production') ? Object.freeze([]) : [];
const NOOP = () => { };
const elFormKey = 'elForm';
const elFormItemKey = 'elFormItem';
const elFormEvents = {
addField: 'el.form.addField',
removeField: 'el.form.removeField',
};
var LabelWrap = defineComponent({
name: 'ElLabelWrap',
props: {
isAutoWidth: Boolean,
updateAll: Boolean,
},
setup(props, { slots }) {
const el = ref(null);
const elForm = inject(elFormKey);
const elFormItem = inject(elFormItemKey);
const computedWidth = ref(0);
watch(computedWidth, (val, oldVal) => {
if (props.updateAll) {
elForm.registerLabelWidth(val, oldVal);
elFormItem.updateComputedLabelWidth(val);
}
});
const getLabelWidth = () => {
var _a;
if ((_a = el.value) === null || _a === void 0 ? void 0 : _a.firstElementChild) {
const width = window.getComputedStyle(el.value.firstElementChild)
.width;
return Math.ceil(parseFloat(width));
}
else {
return 0;
}
};
const updateLabelWidth = (action = 'update') => {
nextTick(() => {
if (slots.default && props.isAutoWidth) {
if (action === 'update') {
computedWidth.value = getLabelWidth();
}
else if (action === 'remove') {
elForm.deregisterLabelWidth(computedWidth.value);
}
}
});
};
const updateLabelWidthFn = () => updateLabelWidth('update');
onMounted(() => {
addResizeListener(el.value.firstElementChild, updateLabelWidthFn);
updateLabelWidthFn();
});
onUpdated(updateLabelWidthFn);
onBeforeUnmount(() => {
updateLabelWidth('remove');
removeResizeListener(el.value.firstElementChild, updateLabelWidthFn);
});
function render() {
var _a, _b;
if (!slots)
return null;
if (props.isAutoWidth) {
const autoLabelWidth = elForm.autoLabelWidth;
const style = {};
if (autoLabelWidth && autoLabelWidth !== 'auto') {
const marginLeft = parseInt(autoLabelWidth, 10) - computedWidth.value;
if (marginLeft) {
style.marginLeft = marginLeft + 'px';
}
}
return h('div', {
ref: el,
class: ['el-form-item__label-wrap'],
style,
}, (_a = slots.default) === null || _a === void 0 ? void 0 : _a.call(slots));
}
else {
return h(Fragment, { ref: el }, (_b = slots.default) === null || _b === void 0 ? void 0 : _b.call(slots));
}
}
return render;
},
});
var script = defineComponent({
name: 'ElFormItem',
componentName: 'ElFormItem',
components: {
LabelWrap,
},
props: {
label: String,
labelWidth: String,
prop: String,
required: {
type: Boolean,
default: undefined,
},
rules: [Object, Array],
error: String,
validateStatus: String,
for: String,
inlineMessage: {
type: [String, Boolean],
default: '',
},
showMessage: {
type: Boolean,
default: true,
},
size: {
types: String,
validator: isValidComponentSize,
},
},
setup(props) {
const formItemMitt = mitt();
const $ELEMENT = useGlobalConfig();
const elForm = inject(elFormKey, {});
const validateState = ref('');
const validateMessage = ref('');
const validateDisabled = ref(false);
const computedLabelWidth = ref('');
const vm = getCurrentInstance();
const isNested = computed(() => {
let parent = vm.parent;
while (parent && parent.type.name !== 'ElForm') {
if (parent.type.name === 'ElFormItem') {
return true;
}
parent = parent.parent;
}
return false;
});
let initialValue = undefined;
watch(() => props.error, val => {
validateMessage.value = val;
validateState.value = val ? 'error' : '';
}, {
immediate: true,
});
watch(() => props.validateStatus, val => {
validateState.value = val;
});
const labelFor = computed(() => props.for || props.prop);
const labelStyle = computed(() => {
if (elForm.labelPosition === 'top')
return {};
const labelWidth = props.labelWidth || elForm.labelWidth;
if (labelWidth) {
return {
width: labelWidth,
};
}
return {};
});
const contentStyle = computed(() => {
if (elForm.labelPosition === 'top' || elForm.inline) {
return {};
}
if (!props.label && !props.labelWidth && isNested.value) {
return {};
}
const labelWidth = props.labelWidth || elForm.labelWidth;
const ret = {};
if (labelWidth === 'auto') {
if (props.labelWidth === 'auto') {
ret.marginLeft = computedLabelWidth.value;
}
else if (elForm.labelWidth === 'auto') {
ret.marginLeft = elForm.autoLabelWidth;
}
}
else {
ret.marginLeft = labelWidth;
}
return ret;
});
const fieldValue = computed(() => {
const model = elForm.model;
if (!model || !props.prop) {
return;
}
let path = props.prop;
if (path.indexOf(':') !== -1) {
path = path.replace(/:/, '.');
}
return getPropByPath(model, path, true).v;
});
const isRequired = computed(() => {
let rules = getRules();
let required = false;
if (rules && rules.length) {
rules.every(rule => {
if (rule.required) {
required = true;
return false;
}
return true;
});
}
return required;
});
const elFormItemSize = computed(() => props.size || elForm.size);
const sizeClass = computed(() => {
return elFormItemSize.value || $ELEMENT.size;
});
const validate = (trigger, callback = NOOP) => {
validateDisabled.value = false;
const rules = getFilteredRule(trigger);
if ((!rules || rules.length === 0) && props.required === undefined) {
callback();
return;
}
validateState.value = 'validating';
const descriptor = {};
if (rules && rules.length > 0) {
rules.forEach(rule => {
delete rule.trigger;
});
}
descriptor[props.prop] = rules;
const validator = new AsyncValidator(descriptor);
const model = {};
model[props.prop] = fieldValue.value;
validator.validate(model, { firstFields: true }, (errors, invalidFields) => {
var _a;
validateState.value = !errors ? 'success' : 'error';
validateMessage.value = errors ? errors[0].message : '';
callback(validateMessage.value, invalidFields);
(_a = elForm.emit) === null || _a === void 0 ? void 0 : _a.call(elForm, 'validate', props.prop, !errors, validateMessage.value || null);
});
};
const clearValidate = () => {
validateState.value = '';
validateMessage.value = '';
validateDisabled.value = false;
};
const resetField = () => {
validateState.value = '';
validateMessage.value = '';
let model = elForm.model;
let value = fieldValue.value;
let path = props.prop;
if (path.indexOf(':') !== -1) {
path = path.replace(/:/, '.');
}
let prop = getPropByPath(model, path, true);
validateDisabled.value = true;
if (Array.isArray(value)) {
prop.o[prop.k] = [].concat(initialValue);
}
else {
prop.o[prop.k] = initialValue;
}
nextTick(() => {
validateDisabled.value = false;
});
};
const getRules = () => {
const formRules = elForm.rules;
const selfRules = props.rules;
const requiredRule = props.required !== undefined ? { required: !!props.required } : [];
const prop = getPropByPath(formRules, props.prop || '', false);
const normalizedRule = formRules
? (prop.o[props.prop || ''] || prop.v)
: [];
return [].concat(selfRules || normalizedRule || []).concat(requiredRule);
};
const getFilteredRule = trigger => {
const rules = getRules();
return rules
.filter(rule => {
if (!rule.trigger || trigger === '')
return true;
if (Array.isArray(rule.trigger)) {
return rule.trigger.indexOf(trigger) > -1;
}
else {
return rule.trigger === trigger;
}
})
.map(rule => (Object.assign({}, rule)));
};
const onFieldBlur = () => {
validate('blur');
};
const onFieldChange = () => {
if (validateDisabled.value) {
validateDisabled.value = false;
return;
}
validate('change');
};
const updateComputedLabelWidth = width => {
computedLabelWidth.value = width ? `${width}px` : '';
};
const addValidateEvents = () => {
const rules = getRules();
if (rules.length || props.required !== undefined) {
formItemMitt.on('el.form.blur', onFieldBlur);
formItemMitt.on('el.form.change', onFieldChange);
}
};
const removeValidateEvents = () => {
formItemMitt.off('el.form.blur', onFieldBlur);
formItemMitt.off('el.form.change', onFieldChange);
};
const elFormItem = reactive(Object.assign(Object.assign({}, toRefs(props)), { size: sizeClass, validateState,
removeValidateEvents,
addValidateEvents,
resetField,
clearValidate,
validate,
formItemMitt,
updateComputedLabelWidth }));
onMounted(() => {
var _a;
if (props.prop) {
(_a = elForm.formMitt) === null || _a === void 0 ? void 0 : _a.emit(elFormEvents.addField, elFormItem);
let value = fieldValue.value;
initialValue = Array.isArray(value)
? [...value] : value;
addValidateEvents();
}
});
onBeforeUnmount(() => {
var _a;
(_a = elForm.formMitt) === null || _a === void 0 ? void 0 : _a.emit(elFormEvents.removeField, elFormItem);
});
provide(elFormItemKey, elFormItem);
const formItemClass = computed(() => [
{
'el-form-item--feedback': elForm.statusIcon,
'is-error': validateState.value === 'error',
'is-validating': validateState.value === 'validating',
'is-success': validateState.value === 'success',
'is-required': isRequired.value || props.required,
'is-no-asterisk': elForm.hideRequiredAsterisk,
},
sizeClass.value ? 'el-form-item--' + sizeClass.value : '',
]);
const shouldShowError = computed(() => {
return validateState.value === 'error' && props.showMessage && elForm.showMessage;
});
return {
formItemClass,
shouldShowError,
elForm,
labelStyle,
contentStyle,
validateMessage,
labelFor,
};
},
});
function render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_LabelWrap = resolveComponent("LabelWrap");
return (openBlock(), createBlock("div", {
class: ["el-form-item", _ctx.formItemClass]
}, [
createVNode(_component_LabelWrap, {
"is-auto-width": _ctx.labelStyle.width === 'auto',
"update-all": _ctx.elForm.labelWidth === 'auto'
}, {
default: withCtx(() => [
(_ctx.label || _ctx.$slots.label)
? (openBlock(), createBlock("label", {
key: 0,
for: _ctx.labelFor,
class: "el-form-item__label",
style: _ctx.labelStyle
}, [
renderSlot(_ctx.$slots, "label", {}, () => [
createTextVNode(toDisplayString(_ctx.label + _ctx.elForm.labelSuffix), 1 /* TEXT */)
])
], 12 /* STYLE, PROPS */, ["for"]))
: createCommentVNode("v-if", true)
]),
_: 1 /* STABLE */
}, 8 /* PROPS */, ["is-auto-width", "update-all"]),
createVNode("div", {
class: "el-form-item__content",
style: _ctx.contentStyle
}, [
renderSlot(_ctx.$slots, "default"),
createVNode(Transition, { name: "el-zoom-in-top" }, {
default: withCtx(() => [
(_ctx.shouldShowError)
? renderSlot(_ctx.$slots, "error", {
key: 0,
error: _ctx.validateMessage
}, () => [
createVNode("div", {
class: ["el-form-item__error", {
'el-form-item__error--inline':
typeof _ctx.inlineMessage === 'boolean'
? _ctx.inlineMessage
: _ctx.elForm.inlineMessage || false
}]
}, toDisplayString(_ctx.validateMessage), 3 /* TEXT, CLASS */)
])
: createCommentVNode("v-if", true)
]),
_: 1 /* STABLE */
})
], 4 /* STYLE */)
], 2 /* CLASS */))
}
script.render = render;
script.__file = "packages/form/src/form-item.vue";
script.install = (app) => {
app.component(script.name, script);
};
const _FormItem = script;
export default _FormItem;