refactor: form

sendya-refactor/v3/rate
tangjinzhou 2021-05-30 21:39:25 +08:00
parent 0a2940e4ad
commit 3d2a04d23d
13 changed files with 457 additions and 488 deletions

View File

@ -13,11 +13,13 @@ const useProvideSize = <T = SizeType>(props: Record<any, any>): ComputedRef<T> =
return size; return size;
}; };
const useInjectSize = <T = SizeType>(): ComputedRef<T> => { const useInjectSize = <T = SizeType>(props?: Record<any, any>): ComputedRef<T> => {
const size: ComputedRef<T> = inject( const size: ComputedRef<T> = props
sizeProvider, ? computed(() => props.size)
computed(() => ('default' as unknown) as T), : inject(
); sizeProvider,
computed(() => ('default' as unknown) as T),
);
return size; return size;
}; };

View File

@ -1,8 +1,9 @@
import { useInjectFormItemPrefix } from './context'; import { useInjectFormItemPrefix } from './context';
import { VueNode } from '../_util/type'; import { VueNode } from '../_util/type';
import { computed, defineComponent, ref, watch } from '@vue/runtime-core'; import { defineComponent, ref, watch } from '@vue/runtime-core';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import Transition, { getTransitionProps } from '../_util/transition'; import Transition, { getTransitionProps } from '../_util/transition';
import useConfigInject from '../_util/hooks/useConfigInject';
export interface ErrorListProps { export interface ErrorListProps {
errors?: VueNode[]; errors?: VueNode[];
@ -12,29 +13,53 @@ export interface ErrorListProps {
onDomErrorVisibleChange?: (visible: boolean) => void; onDomErrorVisibleChange?: (visible: boolean) => void;
} }
export default defineComponent<ErrorListProps>({ export default defineComponent({
name: 'ErrorList', name: 'ErrorList',
props: ['errors', 'help', 'onDomErrorVisibleChange'],
setup(props) { setup(props) {
const { prefixCls: rootPrefixCls } = useConfigInject('', props);
const { prefixCls, status } = useInjectFormItemPrefix(); const { prefixCls, status } = useInjectFormItemPrefix();
const visible = computed(() => props.errors && props.errors.length); const visible = ref(!!(props.errors && props.errors.length));
const innerStatus = ref(status.value); const innerStatus = ref(status.value);
let timeout = ref();
watch([() => props.errors, () => props.help], () => {
window.clearTimeout(timeout.value);
if (props.help) {
visible.value = !!(props.errors && props.errors.length);
} else {
timeout.value = window.setTimeout(() => {
visible.value = !!(props.errors && props.errors.length);
});
}
});
// Memo status in same visible // Memo status in same visible
watch([() => visible, () => status], () => { watch([visible, status], () => {
if (visible.value && status.value) { if (visible.value && status.value) {
innerStatus.value = status.value; innerStatus.value = status.value;
} }
}); });
watch(
visible,
() => {
if (visible.value) {
props.onDomErrorVisibleChange?.(true);
}
},
{ immediate: true, flush: 'post' },
);
return () => { return () => {
const baseClassName = `${prefixCls.value}-item-explain`; const baseClassName = `${prefixCls.value}-item-explain`;
const transitionProps = getTransitionProps('show-help', { const transitionProps = getTransitionProps(`${rootPrefixCls.value}-show-help`, {
onAfterLeave: () => props.onDomErrorVisibleChange?.(false), onAfterLeave: () => {
props.onDomErrorVisibleChange?.(false);
},
}); });
return ( return (
<Transition {...transitionProps}> <Transition {...transitionProps}>
{visible ? ( {visible.value ? (
<div <div
class={classNames(baseClassName, { class={classNames(baseClassName, {
[`${baseClassName}-${innerStatus}`]: innerStatus, [`${baseClassName}-${innerStatus.value}`]: innerStatus.value,
})} })}
key="help" key="help"
> >

View File

@ -1,18 +1,16 @@
import { import {
defineComponent, defineComponent,
inject,
provide,
PropType, PropType,
computed, computed,
ExtractPropTypes, ExtractPropTypes,
HTMLAttributes, HTMLAttributes,
watch,
ref,
} from 'vue'; } from 'vue';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import warning from '../_util/warning'; import warning from '../_util/warning';
import FormItem from './FormItem'; import FormItem, { FieldExpose } from './FormItem';
import { getSlot } from '../_util/props-util';
import { defaultConfigProvider, SizeType } from '../config-provider';
import { getNamePath, containsNamePath } from './utils/valueUtil'; import { getNamePath, containsNamePath } from './utils/valueUtil';
import { defaultValidateMessages } from './utils/messages'; import { defaultValidateMessages } from './utils/messages';
import { allPromiseFinish } from './utils/asyncUtil'; import { allPromiseFinish } from './utils/asyncUtil';
@ -23,6 +21,10 @@ import initDefaultProps from '../_util/props-util/initDefaultProps';
import { tuple, VueNode } from '../_util/type'; import { tuple, VueNode } from '../_util/type';
import { ColProps } from '../grid/Col'; import { ColProps } from '../grid/Col';
import { InternalNamePath, NamePath, ValidateErrorEntity, ValidateOptions } from './interface'; import { InternalNamePath, NamePath, ValidateErrorEntity, ValidateOptions } from './interface';
import { useInjectSize } from '../_util/hooks/useSize';
import useConfigInject from '../_util/hooks/useConfigInject';
import { useProvideForm } from './context';
import { SizeType } from '../config-provider';
export type RequiredMark = boolean | 'optional'; export type RequiredMark = boolean | 'optional';
export type FormLayout = 'horizontal' | 'inline' | 'vertical'; export type FormLayout = 'horizontal' | 'inline' | 'vertical';
@ -61,7 +63,7 @@ export const formProps = {
colon: PropTypes.looseBool, colon: PropTypes.looseBool,
labelAlign: PropTypes.oneOf(tuple('left', 'right')), labelAlign: PropTypes.oneOf(tuple('left', 'right')),
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
requiredMark: { type: [String, Boolean] as PropType<RequiredMark> }, requiredMark: { type: [String, Boolean] as PropType<RequiredMark | ''>, default: undefined },
/** @deprecated Will warning in future branch. Pls use `requiredMark` instead. */ /** @deprecated Will warning in future branch. Pls use `requiredMark` instead. */
hideRequiredMark: PropTypes.looseBool, hideRequiredMark: PropTypes.looseBool,
model: PropTypes.object, model: PropTypes.object,
@ -93,92 +95,88 @@ const Form = defineComponent({
colon: true, colon: true,
}), }),
Item: FormItem, Item: FormItem,
setup(props) { emits: ['finishFailed', 'submit', 'finish'],
return { setup(props, { emit, slots, expose }) {
configProvider: inject('configProvider', defaultConfigProvider), const size = useInjectSize(props);
fields: [], const { prefixCls, direction, form: contextForm } = useConfigInject('form', props);
form: undefined, const requiredMark = computed(() => props.requiredMark === '' || props.requiredMark);
lastValidatePromise: null, const mergedRequiredMark = computed(() => {
vertical: computed(() => props.layout === 'vertical'), if (requiredMark.value !== undefined) {
return requiredMark.value;
}
if (contextForm && contextForm.value?.requiredMark !== undefined) {
return contextForm.value.requiredMark;
}
if (props.hideRequiredMark) {
return false;
}
return true;
});
const formClassName = computed(() =>
classNames(prefixCls.value, {
[`${prefixCls.value}-${props.layout}`]: true,
[`${prefixCls.value}-hide-required-mark`]: mergedRequiredMark.value === false,
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
[`${prefixCls.value}-${size.value}`]: size.value,
}),
);
const lastValidatePromise = ref();
const fields: Record<string, FieldExpose> = {};
const addField = (eventKey: string, field: FieldExpose) => {
fields[eventKey] = field;
}; };
}, const removeField = (eventKey: string) => {
watch: { delete fields[eventKey];
rules() { };
if (this.validateOnRuleChange) {
this.validateFields(); const getFieldsByNameList = (nameList: NamePath) => {
}
},
},
created() {
provide('FormContext', this);
},
methods: {
addField(field: any) {
if (field) {
this.fields.push(field);
}
},
removeField(field: any) {
if (field.fieldName) {
this.fields.splice(this.fields.indexOf(field), 1);
}
},
handleSubmit(e: Event) {
e.preventDefault();
e.stopPropagation();
this.$emit('submit', e);
const res = this.validateFields();
res
.then(values => {
this.$emit('finish', values);
})
.catch(errors => {
this.handleFinishFailed(errors);
});
},
getFieldsByNameList(nameList: NamePath) {
const provideNameList = !!nameList; const provideNameList = !!nameList;
const namePathList = provideNameList ? toArray(nameList).map(getNamePath) : []; const namePathList = provideNameList ? toArray(nameList).map(getNamePath) : [];
if (!provideNameList) { if (!provideNameList) {
return this.fields; return Object.values(fields);
} else { } else {
return this.fields.filter( return Object.values(fields).filter(
field => namePathList.findIndex(namePath => isEqualName(namePath, field.fieldName)) > -1, field =>
namePathList.findIndex(namePath => isEqualName(namePath, field.fieldName.value)) > -1,
); );
} }
}, };
resetFields(name: NamePath) { const resetFields = (name: NamePath) => {
if (!this.model) { if (!props.model) {
warning(false, 'Form', 'model is required for resetFields to work.'); warning(false, 'Form', 'model is required for resetFields to work.');
return; return;
} }
this.getFieldsByNameList(name).forEach(field => { getFieldsByNameList(name).forEach(field => {
field.resetField(); field.resetField();
}); });
}, };
clearValidate(name: NamePath) { const clearValidate = (name: NamePath) => {
this.getFieldsByNameList(name).forEach(field => { getFieldsByNameList(name).forEach(field => {
field.clearValidate(); field.clearValidate();
}); });
}, };
handleFinishFailed(errorInfo: ValidateErrorEntity) { const handleFinishFailed = (errorInfo: ValidateErrorEntity) => {
const { scrollToFirstError } = this; const { scrollToFirstError } = props;
this.$emit('finishFailed', errorInfo); emit('finishFailed', errorInfo);
if (scrollToFirstError && errorInfo.errorFields.length) { if (scrollToFirstError && errorInfo.errorFields.length) {
let scrollToFieldOptions: Options = {}; let scrollToFieldOptions: Options = {};
if (typeof scrollToFirstError === 'object') { if (typeof scrollToFirstError === 'object') {
scrollToFieldOptions = scrollToFirstError; scrollToFieldOptions = scrollToFirstError;
} }
this.scrollToField(errorInfo.errorFields[0].name, scrollToFieldOptions); scrollToField(errorInfo.errorFields[0].name, scrollToFieldOptions);
} }
}, };
validate(...args: any[]) { const validate = (...args: any[]) => {
return this.validateField(...args); return validateField(...args);
}, };
scrollToField(name: NamePath, options = {}) { const scrollToField = (name: NamePath, options = {}) => {
const fields = this.getFieldsByNameList(name); const fields = getFieldsByNameList(name);
if (fields.length) { if (fields.length) {
const fieldId = fields[0].fieldId; const fieldId = fields[0].fieldId.value;
const node = fieldId ? document.getElementById(fieldId) : null; const node = fieldId ? document.getElementById(fieldId) : null;
if (node) { if (node) {
@ -189,12 +187,12 @@ const Form = defineComponent({
}); });
} }
} }
}, };
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
getFieldsValue(nameList: NamePath[] | true = true) { const getFieldsValue = (nameList: NamePath[] | true = true) => {
const values: any = {}; const values: any = {};
this.fields.forEach(({ fieldName, fieldValue }) => { Object.values(fields).forEach(({ fieldName, fieldValue }) => {
values[fieldName] = fieldValue; values[fieldName.value] = fieldValue.value;
}); });
if (nameList === true) { if (nameList === true) {
return values; return values;
@ -205,14 +203,14 @@ const Form = defineComponent({
); );
return res; return res;
} }
}, };
validateFields(nameList?: NamePath[], options?: ValidateOptions) { const validateFields = (nameList?: NamePath[], options?: ValidateOptions) => {
warning( warning(
!(nameList instanceof Function), !(nameList instanceof Function),
'Form', 'Form',
'validateFields/validateField/validate not support callback, please use promise instead', 'validateFields/validateField/validate not support callback, please use promise instead',
); );
if (!this.model) { if (!props.model) {
warning(false, 'Form', 'model is required for validateFields to work.'); warning(false, 'Form', 'model is required for validateFields to work.');
return Promise.reject('Form `model` is required for validateFields to work.'); return Promise.reject('Form `model` is required for validateFields to work.');
} }
@ -227,25 +225,25 @@ const Form = defineComponent({
errors: string[]; errors: string[];
}>[] = []; }>[] = [];
this.fields.forEach(field => { Object.values(fields).forEach(field => {
// Add field if not provide `nameList` // Add field if not provide `nameList`
if (!provideNameList) { if (!provideNameList) {
namePathList.push(field.getNamePath()); namePathList.push(field.namePath.value);
} }
// Skip if without rule // Skip if without rule
if (!field.getRules().length) { if (!field.rules?.value.length) {
return; return;
} }
const fieldNamePath = field.getNamePath(); const fieldNamePath = field.namePath.value;
// Add field validate rule in to promise list // Add field validate rule in to promise list
if (!provideNameList || containsNamePath(namePathList, fieldNamePath)) { if (!provideNameList || containsNamePath(namePathList, fieldNamePath)) {
const promise = field.validateRules({ const promise = field.validateRules({
validateMessages: { validateMessages: {
...defaultValidateMessages, ...defaultValidateMessages,
...this.validateMessages, ...props.validateMessages,
}, },
...options, ...options,
}); });
@ -265,21 +263,21 @@ const Form = defineComponent({
}); });
const summaryPromise = allPromiseFinish(promiseList); const summaryPromise = allPromiseFinish(promiseList);
this.lastValidatePromise = summaryPromise; lastValidatePromise.value = summaryPromise;
const returnPromise = summaryPromise const returnPromise = summaryPromise
.then(() => { .then(() => {
if (this.lastValidatePromise === summaryPromise) { if (lastValidatePromise.value === summaryPromise) {
return Promise.resolve(this.getFieldsValue(namePathList)); return Promise.resolve(getFieldsValue(namePathList));
} }
return Promise.reject([]); return Promise.reject([]);
}) })
.catch(results => { .catch(results => {
const errorList = results.filter(result => result && result.errors.length); const errorList = results.filter(result => result && result.errors.length);
return Promise.reject({ return Promise.reject({
values: this.getFieldsValue(namePathList), values: getFieldsValue(namePathList),
errorFields: errorList, errorFields: errorList,
outOfDate: this.lastValidatePromise !== summaryPromise, outOfDate: lastValidatePromise.value !== summaryPromise,
}); });
}); });
@ -287,29 +285,65 @@ const Form = defineComponent({
returnPromise.catch(e => e); returnPromise.catch(e => e);
return returnPromise; return returnPromise;
}, };
validateField(...args: any[]) { const validateField = (...args: any[]) => {
return this.validateFields(...args); return validateFields(...args);
}, };
},
render() { const handleSubmit = (e: Event) => {
const { prefixCls: customizePrefixCls, hideRequiredMark, layout, handleSubmit, size } = this; e.preventDefault();
const getPrefixCls = this.configProvider.getPrefixCls; e.stopPropagation();
const prefixCls = getPrefixCls('form', customizePrefixCls); emit('submit', e);
const { class: className, ...restProps } = this.$attrs; const res = validateFields();
res
.then(values => {
emit('finish', values);
})
.catch(errors => {
handleFinishFailed(errors);
});
};
const formClassName = classNames(prefixCls, className, { expose({
[`${prefixCls}-${layout}`]: true, resetFields,
// [`${prefixCls}-rtl`]: direction === 'rtl', clearValidate,
[`${prefixCls}-${size}`]: size, validateFields,
[`${prefixCls}-hide-required-mark`]: hideRequiredMark, getFieldsValue,
validate,
scrollToField,
}); });
return (
<form onSubmit={handleSubmit} class={formClassName} {...restProps}> useProvideForm({
{getSlot(this)} model: computed(() => props.model),
</form> name: computed(() => props.name),
labelAlign: computed(() => props.labelAlign),
labelCol: computed(() => props.labelCol),
wrapperCol: computed(() => props.wrapperCol),
vertical: computed(() => props.layout === 'vertical'),
colon: computed(() => props.colon),
requiredMark: mergedRequiredMark,
validateTrigger: computed(() => props.validateTrigger),
rules: computed(() => props.rules),
addField,
removeField,
});
watch(
() => props.rules,
() => {
if (props.validateOnRuleChange) {
validateFields();
}
},
); );
return () => {
return (
<form onSubmit={handleSubmit} class={formClassName.value}>
{slots.default?.()}
</form>
);
};
}, },
}); });

View File

@ -1,50 +1,47 @@
import { import {
inject,
provide,
PropType, PropType,
defineComponent, defineComponent,
computed, computed,
nextTick, nextTick,
ExtractPropTypes, ExtractPropTypes,
ref,
watchEffect,
onBeforeUnmount,
ComputedRef,
} from 'vue'; } from 'vue';
import cloneDeep from 'lodash-es/cloneDeep'; import cloneDeep from 'lodash-es/cloneDeep';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import classNames from '../_util/classNames';
import { getTransitionProps, Transition } from '../_util/transition';
import Row from '../grid/Row'; import Row from '../grid/Row';
import Col, { ColProps } from '../grid/Col'; import { ColProps } from '../grid/Col';
import hasProp, { import { isValidElement, flattenChildren } from '../_util/props-util';
findDOMNode,
getComponent,
getOptionProps,
getEvents,
isValidElement,
getSlot,
} from '../_util/props-util';
import BaseMixin from '../_util/BaseMixin'; import BaseMixin from '../_util/BaseMixin';
import { defaultConfigProvider } from '../config-provider';
import { cloneElement } from '../_util/vnode'; import { cloneElement } from '../_util/vnode';
import CheckCircleFilled from '@ant-design/icons-vue/CheckCircleFilled'; import { validateRules as validateRulesUtil } from './utils/validateUtil';
import ExclamationCircleFilled from '@ant-design/icons-vue/ExclamationCircleFilled';
import CloseCircleFilled from '@ant-design/icons-vue/CloseCircleFilled';
import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined';
import { validateRules } from './utils/validateUtil';
import { getNamePath } from './utils/valueUtil'; import { getNamePath } from './utils/valueUtil';
import { toArray } from './utils/typeUtil'; import { toArray } from './utils/typeUtil';
import { warning } from '../vc-util/warning'; import { warning } from '../vc-util/warning';
import find from 'lodash-es/find'; import find from 'lodash-es/find';
import { tuple, VueNode } from '../_util/type'; import { tuple } from '../_util/type';
import { ValidateOptions } from './interface'; import { InternalNamePath, RuleObject, ValidateOptions } from './interface';
import useConfigInject from '../_util/hooks/useConfigInject';
import { useInjectForm } from './context';
import FormItemLabel from './FormItemLabel';
import FormItemInput from './FormItemInput';
import { ValidationRule } from './Form';
const ValidateStatuses = tuple('success', 'warning', 'error', 'validating', ''); const ValidateStatuses = tuple('success', 'warning', 'error', 'validating', '');
export type ValidateStatus = typeof ValidateStatuses[number]; export type ValidateStatus = typeof ValidateStatuses[number];
const iconMap = { export interface FieldExpose {
success: CheckCircleFilled, fieldValue: ComputedRef<any>;
warning: ExclamationCircleFilled, fieldId: ComputedRef<any>;
error: CloseCircleFilled, fieldName: ComputedRef<any>;
validating: LoadingOutlined, resetField: () => void;
}; clearValidate: () => void;
namePath: ComputedRef<InternalNamePath>;
rules?: ComputedRef<ValidationRule[]>;
validateRules: (options: ValidateOptions) => Promise<void> | Promise<string[]>;
}
function getPropByPath(obj: any, namePathList: any, strict?: boolean) { function getPropByPath(obj: any, namePathList: any, strict?: boolean) {
let tempObj = obj; let tempObj = obj;
@ -103,15 +100,25 @@ export const formItemProps = {
export type FormItemProps = Partial<ExtractPropTypes<typeof formItemProps>>; export type FormItemProps = Partial<ExtractPropTypes<typeof formItemProps>>;
let indexGuid = 0;
export default defineComponent({ export default defineComponent({
name: 'AFormItem', name: 'AFormItem',
mixins: [BaseMixin], mixins: [BaseMixin],
inheritAttrs: false, inheritAttrs: false,
__ANT_NEW_FORM_ITEM: true, __ANT_NEW_FORM_ITEM: true,
props: formItemProps, props: formItemProps,
setup(props) { slots: ['help', 'label', 'extra'],
const FormContext = inject('FormContext', {}) as any; setup(props, { slots }) {
warning(props.prop === undefined, `\`prop\` is deprecated. Please use \`name\` instead.`);
const eventKey = `form-item-${++indexGuid}`;
const { prefixCls } = useConfigInject('form', props);
const formContext = useInjectForm();
const fieldName = computed(() => props.name || props.prop); const fieldName = computed(() => props.name || props.prop);
const errors = ref([]);
const validateMessage = ref('');
const validateDisabled = ref(false);
const domErrorVisible = ref(false);
const inputRef = ref();
const namePath = computed(() => { const namePath = computed(() => {
const val = fieldName.value; const val = fieldName.value;
return getNamePath(val); return getNamePath(val);
@ -123,26 +130,30 @@ export default defineComponent({
} else if (!namePath.value.length) { } else if (!namePath.value.length) {
return undefined; return undefined;
} else { } else {
const formName = FormContext.name; const formName = formContext.name.value;
const mergedId = namePath.value.join('_'); const mergedId = namePath.value.join('_');
return formName ? `${formName}_${mergedId}` : mergedId; return formName ? `${formName}_${mergedId}` : mergedId;
} }
}); });
const fieldValue = computed(() => { const fieldValue = computed(() => {
const model = FormContext.model; const model = formContext.model.value;
if (!model || !fieldName.value) { if (!model || !fieldName.value) {
return; return;
} }
return getPropByPath(model, namePath.value, true).v; return getPropByPath(model, namePath.value, true).v;
}); });
const initialValue = ref(cloneDeep(fieldValue.value));
const mergedValidateTrigger = computed(() => { const mergedValidateTrigger = computed(() => {
let validateTrigger = let validateTrigger =
props.validateTrigger !== undefined ? props.validateTrigger : FormContext.validateTrigger; props.validateTrigger !== undefined
? props.validateTrigger
: formContext.validateTrigger.value;
validateTrigger = validateTrigger === undefined ? 'change' : validateTrigger; validateTrigger = validateTrigger === undefined ? 'change' : validateTrigger;
return toArray(validateTrigger); return toArray(validateTrigger);
}); });
const getRules = () => { const rulesRef = computed<ValidationRule[]>(() => {
let formRules = FormContext.rules; let formRules = formContext.rules.value;
const selfRules = props.rules; const selfRules = props.rules;
const requiredRule = const requiredRule =
props.required !== undefined props.required !== undefined
@ -156,9 +167,9 @@ export default defineComponent({
} else { } else {
return rules.concat(requiredRule); return rules.concat(requiredRule);
} }
}; });
const isRequired = computed(() => { const isRequired = computed(() => {
const rules = getRules(); const rules = rulesRef.value;
let isRequired = false; let isRequired = false;
if (rules && rules.length) { if (rules && rules.length) {
rules.every(rule => { rules.every(rule => {
@ -171,360 +182,234 @@ export default defineComponent({
} }
return isRequired || props.required; return isRequired || props.required;
}); });
return {
isFormItemChildren: inject('isFormItemChildren', false),
configProvider: inject('configProvider', defaultConfigProvider),
FormContext,
fieldId,
fieldName,
namePath,
isRequired,
getRules,
fieldValue,
mergedValidateTrigger,
};
},
data() {
warning(!hasProp(this, 'prop'), `\`prop\` is deprecated. Please use \`name\` instead.`);
return {
validateState: this.validateStatus,
validateMessage: '',
validateDisabled: false,
validator: {},
helpShow: false,
errors: [],
initialValue: undefined,
};
},
watch: {
validateStatus(val) {
this.validateState = val;
},
},
created() {
provide('isFormItemChildren', true);
},
mounted() {
if (this.fieldName) {
const { addField } = this.FormContext;
addField && addField(this);
this.initialValue = cloneDeep(this.fieldValue);
}
},
beforeUnmount() {
const { removeField } = this.FormContext;
removeField && removeField(this);
},
methods: {
getNamePath() {
const { fieldName } = this;
const { prefixName = [] } = this.FormContext;
return fieldName !== undefined ? [...prefixName, ...this.namePath] : []; const validateState = ref();
}, watchEffect(() => {
validateRules(options: ValidateOptions) { validateState.value = props.validateStatus;
const { validateFirst = false, messageVariables } = this.$props; });
const validateRules = (options: ValidateOptions) => {
const { validateFirst = false, messageVariables } = props;
const { triggerName } = options || {}; const { triggerName } = options || {};
const namePath = this.getNamePath();
let filteredRules = this.getRules(); let filteredRules = rulesRef.value;
if (triggerName) { if (triggerName) {
filteredRules = filteredRules.filter(rule => { filteredRules = filteredRules.filter(rule => {
const { trigger } = rule; const { trigger } = rule;
if (!trigger && !this.mergedValidateTrigger.length) { if (!trigger && !mergedValidateTrigger.value.length) {
return true; return true;
} }
const triggerList = toArray(trigger || this.mergedValidateTrigger); const triggerList = toArray(trigger || mergedValidateTrigger.value);
return triggerList.includes(triggerName); return triggerList.includes(triggerName);
}); });
} }
if (!filteredRules.length) { if (!filteredRules.length) {
return Promise.resolve(); return Promise.resolve();
} }
const promise = validateRules( const promise = validateRulesUtil(
namePath, namePath.value,
this.fieldValue, fieldValue.value,
filteredRules, filteredRules as RuleObject[],
options, options,
validateFirst, validateFirst,
messageVariables, messageVariables,
); );
this.validateState = 'validating'; validateState.value = 'validating';
this.errors = []; errors.value = [];
promise promise
.catch(e => e) .catch(e => e)
.then((errors = []) => { .then((ers = []) => {
if (this.validateState === 'validating') { if (validateState.value === 'validating') {
this.validateState = errors.length ? 'error' : 'success'; validateState.value = ers.length ? 'error' : 'success';
this.validateMessage = errors[0]; validateMessage.value = ers[0];
this.errors = errors; errors.value = ers;
} }
}); });
return promise; return promise;
}, };
onFieldBlur() {
this.validateRules({ triggerName: 'blur' }); const onFieldBlur = () => {
}, validateRules({ triggerName: 'blur' });
onFieldChange() { };
if (this.validateDisabled) { const onFieldChange = () => {
this.validateDisabled = false; if (validateDisabled.value) {
validateDisabled.value = false;
return; return;
} }
this.validateRules({ triggerName: 'change' }); validateRules({ triggerName: 'change' });
}, };
clearValidate() { const clearValidate = () => {
this.validateState = ''; validateState.value = '';
this.validateMessage = ''; validateMessage.value = '';
this.validateDisabled = false; validateDisabled.value = false;
}, };
resetField() {
this.validateState = ''; const resetField = () => {
this.validateMessage = ''; validateState.value = '';
const model = this.FormContext.model || {}; validateMessage.value = '';
const value = this.fieldValue; const model = formContext.model.value || {};
const prop = getPropByPath(model, this.namePath, true); const value = fieldValue.value;
this.validateDisabled = true; const prop = getPropByPath(model, namePath.value, true);
validateDisabled.value = true;
if (Array.isArray(value)) { if (Array.isArray(value)) {
prop.o[prop.k] = [].concat(this.initialValue); prop.o[prop.k] = [].concat(initialValue.value);
} else { } else {
prop.o[prop.k] = this.initialValue; prop.o[prop.k] = initialValue.value;
} }
// reset validateDisabled after onFieldChange triggered // reset validateDisabled after onFieldChange triggered
nextTick(() => { nextTick(() => {
this.validateDisabled = false; validateDisabled.value = false;
}); });
}, };
getHelpMessage() {
const help = getComponent(this, 'help');
return this.validateMessage || help; const onLabelClick = () => {
}, const id = fieldId.value;
if (!id || !inputRef.value) {
onLabelClick() {
const id = this.fieldId;
if (!id) {
return; return;
} }
const formItemNode = findDOMNode(this); const control = inputRef.value.$el.querySelector(`[id="${id}"]`);
const control = formItemNode.querySelector(`[id="${id}"]`);
if (control && control.focus) { if (control && control.focus) {
control.focus(); control.focus();
} }
}, };
formContext.addField(eventKey, {
fieldValue,
fieldId,
fieldName,
resetField,
clearValidate,
namePath,
validateRules,
rules: rulesRef,
});
onBeforeUnmount(() => {
formContext.removeField(eventKey);
});
// const onHelpAnimEnd = (_key: string, helpShow: boolean) => {
// this.helpShow = helpShow;
// if (!helpShow) {
// this.$forceUpdate();
// }
// };
const itemClassName = computed(() => ({
[`${prefixCls.value}-item`]: true,
onHelpAnimEnd(_key: string, helpShow: boolean) { // Status
this.helpShow = helpShow; [`${prefixCls.value}-item-has-feedback`]: validateState.value && props.hasFeedback,
if (!helpShow) { [`${prefixCls.value}-item-has-success`]: validateState.value === 'success',
this.$forceUpdate(); [`${prefixCls.value}-item-has-warning`]: validateState.value === 'warning',
} [`${prefixCls.value}-item-has-error`]: validateState.value === 'error',
}, [`${prefixCls.value}-item-is-validating`]: validateState.value === 'validating',
[`${prefixCls.value}-item-hidden`]: props.hidden,
renderHelp(prefixCls: string) { }));
const help = this.getHelpMessage(); return () => {
const children = help ? ( const help = props.help ?? slots.help?.();
<div class={`${prefixCls}-explain`} key="help"> const children = flattenChildren(slots.default?.());
{help} let firstChildren = children[0];
</div> if (fieldName.value && props.autoLink && isValidElement(firstChildren)) {
) : null; const originalEvents = firstChildren.props;
if (children) { const originalBlur = originalEvents.onBlur;
this.helpShow = !!children; const originalChange = originalEvents.onChange;
} firstChildren = cloneElement(firstChildren, {
const transitionProps = getTransitionProps('show-help', { ...(fieldId.value ? { id: fieldId.value } : undefined),
onAfterEnter: () => this.onHelpAnimEnd('help', true), onBlur: (...args: any[]) => {
onAfterLeave: () => this.onHelpAnimEnd('help', false), if (Array.isArray(originalChange)) {
}); for (let i = 0, l = originalChange.length; i < l; i++) {
return ( originalBlur[i](...args);
<Transition {...transitionProps} key="help"> }
{children} } else if (originalBlur) {
</Transition> originalBlur(...args);
); }
}, onFieldBlur();
},
renderExtra(prefixCls: string) { onChange: (...args: any[]) => {
const extra = getComponent(this, 'extra'); if (Array.isArray(originalChange)) {
return extra ? <div class={`${prefixCls}-extra`}>{extra}</div> : null; for (let i = 0, l = originalChange.length; i < l; i++) {
}, originalChange[i](...args);
}
renderValidateWrapper(prefixCls: string, c1: VueNode, c2: VueNode, c3: VueNode) { } else if (originalChange) {
const validateStatus = this.validateState; originalChange(...args);
}
let classes = `${prefixCls}-item-control`; onFieldChange();
if (validateStatus) { },
classes = classNames(`${prefixCls}-item-control`, {
'has-feedback': validateStatus && this.hasFeedback,
'has-success': validateStatus === 'success',
'has-warning': validateStatus === 'warning',
'has-error': validateStatus === 'error',
'is-validating': validateStatus === 'validating',
}); });
} }
const IconNode = validateStatus && iconMap[validateStatus];
const icon =
this.hasFeedback && IconNode ? (
<span class={`${prefixCls}-item-children-icon`}>
<IconNode />
</span>
) : null;
return ( return (
<div class={classes}> <Row
<span class={`${prefixCls}-item-children`}> class={[
{c1} itemClassName.value,
{icon} domErrorVisible.value || !!help ? `${prefixCls.value}-item-with-help` : '',
</span> ]}
{c2} key="row"
{c3} >
</div> {/* Label */}
); <FormItemLabel
}, {...props}
htmlFor={fieldId.value}
renderWrapper(prefixCls: string, children: VueNode) { required={isRequired.value}
const { wrapperCol: contextWrapperCol } = (this.isFormItemChildren requiredMark={formContext.requiredMark.value}
? {} prefixCls={prefixCls.value}
: this.FormContext) as any; onClick={onLabelClick}
const { wrapperCol } = this; label={props.label ?? slots.label?.()}
const mergedWrapperCol = wrapperCol || contextWrapperCol || {}; />
const { style, id, ...restProps } = mergedWrapperCol; {/* Input Group */}
const className = classNames(`${prefixCls}-item-control`, mergedWrapperCol.class); <FormItemInput
const colProps = { {...props}
...restProps, errors={errors.value}
class: className, prefixCls={prefixCls.value}
key: 'wrapper', status={validateState.value}
style, onDomErrorVisibleChange={(v: boolean) => (domErrorVisible.value = v)}
id, validateStatus={validateState.value}
}; ref={inputRef}
return <Col {...colProps}>{children}</Col>; help={help}
}, extra={props.extra ?? slots.extra?.()}
renderLabel(prefixCls: string) {
const {
vertical,
labelAlign: contextLabelAlign,
labelCol: contextLabelCol,
colon: contextColon,
} = this.FormContext;
const { labelAlign, labelCol, colon, fieldId, htmlFor } = this;
const label = getComponent(this, 'label');
const required = this.isRequired;
const mergedLabelCol = labelCol || contextLabelCol || {};
const mergedLabelAlign = labelAlign || contextLabelAlign;
const labelClsBasic = `${prefixCls}-item-label`;
const labelColClassName = classNames(
labelClsBasic,
mergedLabelAlign === 'left' && `${labelClsBasic}-left`,
mergedLabelCol.class,
);
const {
class: labelColClass,
style: labelColStyle,
id: labelColId,
...restProps
} = mergedLabelCol;
let labelChildren = label;
// Keep label is original where there should have no colon
const computedColon = colon === true || (contextColon !== false && colon !== false);
const haveColon = computedColon && !vertical;
// Remove duplicated user input colon
if (haveColon && typeof label === 'string' && label.trim() !== '') {
labelChildren = label.replace(/[:]\s*$/, '');
}
const labelClassName = classNames({
[`${prefixCls}-item-required`]: required,
[`${prefixCls}-item-no-colon`]: !computedColon,
});
const colProps = {
...restProps,
class: labelColClassName,
key: 'label',
style: labelColStyle,
id: labelColId,
};
return label ? (
<Col {...colProps}>
<label
for={htmlFor || fieldId}
class={labelClassName}
title={typeof label === 'string' ? label : ''}
onClick={this.onLabelClick}
> >
{labelChildren} {[firstChildren, children.slice(1)]}
</label> </FormItemInput>
</Col>
) : null;
},
renderChildren(prefixCls: string, child: VueNode) {
return [
this.renderLabel(prefixCls),
this.renderWrapper(
prefixCls,
this.renderValidateWrapper(
prefixCls,
child,
this.renderHelp(prefixCls),
this.renderExtra(prefixCls),
),
),
];
},
renderFormItem(child: any[]) {
const validateStatus = this.validateState;
const { prefixCls: customizePrefixCls, hidden, hasFeedback } = this.$props;
const { class: className, ...restProps } = this.$attrs as any;
const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('form', customizePrefixCls);
const children = this.renderChildren(prefixCls, child);
const itemClassName = {
[className]: className,
[`${prefixCls}-item`]: true,
[`${prefixCls}-item-with-help`]: this.helpShow,
// Status
[`${prefixCls}-item-has-feedback`]: validateStatus && hasFeedback,
[`${prefixCls}-item-has-success`]: validateStatus === 'success',
[`${prefixCls}-item-has-warning`]: validateStatus === 'warning',
[`${prefixCls}-item-has-error`]: validateStatus === 'error',
[`${prefixCls}-item-is-validating`]: validateStatus === 'validating',
[`${prefixCls}-item-hidden`]: hidden,
};
return (
<Row class={classNames(itemClassName)} key="row" {...restProps}>
{children}
</Row> </Row>
); );
}, };
},
render() {
const { autoLink } = getOptionProps(this);
const children = getSlot(this);
let firstChildren = children[0];
if (this.fieldName && autoLink && isValidElement(firstChildren)) {
const originalEvents = getEvents(firstChildren);
const originalBlur = originalEvents.onBlur;
const originalChange = originalEvents.onChange;
firstChildren = cloneElement(firstChildren, {
...(this.fieldId ? { id: this.fieldId } : undefined),
onBlur: (...args: any[]) => {
originalBlur && originalBlur(...args);
this.onFieldBlur();
},
onChange: (...args: any[]) => {
if (Array.isArray(originalChange)) {
for (let i = 0, l = originalChange.length; i < l; i++) {
originalChange[i](...args);
}
} else if (originalChange) {
originalChange(...args);
}
this.onFieldChange();
},
});
}
return this.renderFormItem([firstChildren, children.slice(1)]);
}, },
// data() {
// warning(!hasProp(this, 'prop'), `\`prop\` is deprecated. Please use \`name\` instead.`);
// return {
// validateState: this.validateStatus,
// validateMessage: '',
// validateDisabled: false,
// validator: {},
// helpShow: false,
// errors: [],
// initialValue: undefined,
// };
// },
// render() {
// const { autoLink } = getOptionProps(this);
// const children = getSlot(this);
// let firstChildren = children[0];
// if (this.fieldName && autoLink && isValidElement(firstChildren)) {
// const originalEvents = getEvents(firstChildren);
// const originalBlur = originalEvents.onBlur;
// const originalChange = originalEvents.onChange;
// firstChildren = cloneElement(firstChildren, {
// ...(this.fieldId ? { id: this.fieldId } : undefined),
// onBlur: (...args: any[]) => {
// originalBlur && originalBlur(...args);
// this.onFieldBlur();
// },
// onChange: (...args: any[]) => {
// if (Array.isArray(originalChange)) {
// for (let i = 0, l = originalChange.length; i < l; i++) {
// originalChange[i](...args);
// }
// } else if (originalChange) {
// originalChange(...args);
// }
// this.onFieldChange();
// },
// });
// }
// return this.renderFormItem([firstChildren, children.slice(1)]);
// },
}); });

View File

@ -3,7 +3,7 @@ import CloseCircleFilled from '@ant-design/icons-vue/CloseCircleFilled';
import CheckCircleFilled from '@ant-design/icons-vue/CheckCircleFilled'; import CheckCircleFilled from '@ant-design/icons-vue/CheckCircleFilled';
import ExclamationCircleFilled from '@ant-design/icons-vue/ExclamationCircleFilled'; import ExclamationCircleFilled from '@ant-design/icons-vue/ExclamationCircleFilled';
import Col, { ColProps } from '../grid/col'; import Col, { ColProps } from '../grid/Col';
import { useProvideForm, useInjectForm, useProvideFormItemPrefix } from './context'; import { useProvideForm, useInjectForm, useProvideFormItemPrefix } from './context';
import ErrorList from './ErrorList'; import ErrorList from './ErrorList';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
@ -11,7 +11,7 @@ import { ValidateStatus } from './FormItem';
import { VueNode } from '../_util/type'; import { VueNode } from '../_util/type';
import { computed, defineComponent, HTMLAttributes, onUnmounted } from 'vue'; import { computed, defineComponent, HTMLAttributes, onUnmounted } from 'vue';
interface FormItemInputMiscProps { export interface FormItemInputMiscProps {
prefixCls: string; prefixCls: string;
errors: VueNode[]; errors: VueNode[];
hasFeedback?: boolean; hasFeedback?: boolean;
@ -32,8 +32,20 @@ const iconMap: { [key: string]: any } = {
error: CloseCircleFilled, error: CloseCircleFilled,
validating: LoadingOutlined, validating: LoadingOutlined,
}; };
const FormItemInput = defineComponent<FormItemInputProps & FormItemInputMiscProps>({ const FormItemInput = defineComponent({
slots: ['help', 'extra', 'errors'], slots: ['help', 'extra', 'errors'],
inheritAttrs: false,
props: [
'prefixCls',
'errors',
'hasFeedback',
'validateStatus',
'onDomErrorVisibleChange',
'wrapperCol',
'help',
'extra',
'status',
],
setup(props, { slots }) { setup(props, { slots }) {
const formContext = useInjectForm(); const formContext = useInjectForm();
const { wrapperCol: contextWrapperCol } = formContext; const { wrapperCol: contextWrapperCol } = formContext;
@ -43,12 +55,15 @@ const FormItemInput = defineComponent<FormItemInputProps & FormItemInputMiscProp
delete subFormContext.labelCol; delete subFormContext.labelCol;
delete subFormContext.wrapperCol; delete subFormContext.wrapperCol;
useProvideForm(subFormContext); useProvideForm(subFormContext);
useProvideFormItemPrefix({ useProvideFormItemPrefix({
prefixCls: computed(() => props.prefixCls), prefixCls: computed(() => props.prefixCls),
status: computed(() => props.status), status: computed(() => props.status),
}); });
onUnmounted(() => {
props.onDomErrorVisibleChange(false);
});
return () => { return () => {
const { const {
prefixCls, prefixCls,
@ -67,10 +82,6 @@ const FormItemInput = defineComponent<FormItemInputProps & FormItemInputMiscProp
const className = classNames(`${baseClassName}-control`, mergedWrapperCol.class); const className = classNames(`${baseClassName}-control`, mergedWrapperCol.class);
onUnmounted(() => {
onDomErrorVisibleChange(false);
});
// Should provides additional icon if `hasFeedback` // Should provides additional icon if `hasFeedback`
const IconNode = validateStatus && iconMap[validateStatus]; const IconNode = validateStatus && iconMap[validateStatus];
const icon = const icon =

View File

@ -1,4 +1,4 @@
import Col, { ColProps } from '../grid/col'; import Col, { ColProps } from '../grid/Col';
import { FormLabelAlign } from './interface'; import { FormLabelAlign } from './interface';
import { useInjectForm } from './context'; import { useInjectForm } from './context';
import { RequiredMark } from './Form'; import { RequiredMark } from './Form';
@ -17,10 +17,14 @@ export interface FormItemLabelProps {
requiredMark?: RequiredMark; requiredMark?: RequiredMark;
required?: boolean; required?: boolean;
prefixCls: string; prefixCls: string;
onClick: Function;
} }
const FormItemLabel: FunctionalComponent<FormItemLabelProps> = (props, { slots }) => { const FormItemLabel: FunctionalComponent<FormItemLabelProps> = (props, { slots, emit, attrs }) => {
const { prefixCls, htmlFor, labelCol, labelAlign, colon, required, requiredMark } = props; const { prefixCls, htmlFor, labelCol, labelAlign, colon, required, requiredMark } = {
...props,
...attrs,
};
const [formLocale] = useLocaleReceiver('Form'); const [formLocale] = useLocaleReceiver('Form');
const label = props.label ?? slots.label?.(); const label = props.label ?? slots.label?.();
if (!label) return null; if (!label) return null;
@ -68,7 +72,6 @@ const FormItemLabel: FunctionalComponent<FormItemLabelProps> = (props, { slots }
</> </>
); );
} }
const labelClassName = classNames({ const labelClassName = classNames({
[`${prefixCls}-item-required`]: required, [`${prefixCls}-item-required`]: required,
[`${prefixCls}-item-required-mark-optional`]: requiredMark === 'optional', [`${prefixCls}-item-required-mark-optional`]: requiredMark === 'optional',
@ -80,6 +83,7 @@ const FormItemLabel: FunctionalComponent<FormItemLabelProps> = (props, { slots }
html-for={htmlFor} html-for={htmlFor}
class={labelClassName} class={labelClassName}
title={typeof label === 'string' ? label : ''} title={typeof label === 'string' ? label : ''}
onClick={e => emit('click', e)}
> >
{labelChildren} {labelChildren}
</label> </label>
@ -88,5 +92,6 @@ const FormItemLabel: FunctionalComponent<FormItemLabelProps> = (props, { slots }
}; };
FormItemLabel.displayName = 'FormItemLabel'; FormItemLabel.displayName = 'FormItemLabel';
FormItemLabel.inheritAttrs = false;
export default FormItemLabel; export default FormItemLabel;

View File

@ -1,10 +1,11 @@
import { inject, InjectionKey, provide, ComputedRef, computed } from 'vue'; import { inject, InjectionKey, provide, ComputedRef, computed } from 'vue';
import { ColProps } from '../grid'; import { ColProps } from '../grid';
import { RequiredMark } from './Form'; import { RequiredMark, ValidationRule } from './Form';
import { ValidateStatus } from './FormItem'; import { ValidateStatus, FieldExpose } from './FormItem';
import { FormLabelAlign } from './interface'; import { FormLabelAlign } from './interface';
export interface FormContextProps { export interface FormContextProps {
model?: ComputedRef<any>;
vertical: ComputedRef<boolean>; vertical: ComputedRef<boolean>;
name?: ComputedRef<string>; name?: ComputedRef<string>;
colon?: ComputedRef<boolean>; colon?: ComputedRef<boolean>;
@ -13,6 +14,10 @@ export interface FormContextProps {
wrapperCol?: ComputedRef<ColProps>; wrapperCol?: ComputedRef<ColProps>;
requiredMark?: ComputedRef<RequiredMark>; requiredMark?: ComputedRef<RequiredMark>;
//itemRef: (name: (string | number)[]) => (node: React.ReactElement) => void; //itemRef: (name: (string | number)[]) => (node: React.ReactElement) => void;
addField: (eventKey: string, field: FieldExpose) => void;
removeField: (eventKey: string) => void;
validateTrigger?: ComputedRef<string | string[]>;
rules?: ComputedRef<{ [k: string]: ValidationRule[] | ValidationRule }>;
} }
export const FormContextKey: InjectionKey<FormContextProps> = Symbol('formContextKey'); export const FormContextKey: InjectionKey<FormContextProps> = Symbol('formContextKey');
@ -25,6 +30,10 @@ export const useInjectForm = () => {
return inject(FormContextKey, { return inject(FormContextKey, {
labelAlign: computed(() => 'right' as FormLabelAlign), labelAlign: computed(() => 'right' as FormLabelAlign),
vertical: computed(() => false), vertical: computed(() => false),
// eslint-disable-next-line @typescript-eslint/no-unused-vars
addField: (_eventKey: string, _field: FieldExpose) => {},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
removeField: (_eventKey: string) => {},
}); });
}; };

View File

@ -1,7 +1,6 @@
import { inject, defineComponent, CSSProperties, ExtractPropTypes, computed } from 'vue'; import { defineComponent, CSSProperties, ExtractPropTypes, computed } from 'vue';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { rowContextState } from './Row';
import useConfigInject from '../_util/hooks/useConfigInject'; import useConfigInject from '../_util/hooks/useConfigInject';
import { useInjectRow } from './context'; import { useInjectRow } from './context';
@ -102,7 +101,7 @@ export default defineComponent({
const mergedStyle = computed(() => { const mergedStyle = computed(() => {
const { flex } = props; const { flex } = props;
const gutterVal = gutter.value; const gutterVal = gutter.value;
let style: CSSProperties = {}; const style: CSSProperties = {};
// Horizontal gutter use padding // Horizontal gutter use padding
if (gutterVal && gutterVal[0] > 0) { if (gutterVal && gutterVal[0] > 0) {
const horizontalGutter = `${gutterVal[0] / 2}px`; const horizontalGutter = `${gutterVal[0] / 2}px`;

View File

@ -211,7 +211,7 @@ export default defineComponent({
return ( return (
<aside {...attrs} class={siderCls} style={divStyle} ref={ref}> <aside {...attrs} class={siderCls} style={divStyle} ref={ref}>
<div class={`${pre}-children`}>{slots.default?.()}</div> <div class={`${pre}-children`}>{slots.default?.()}</div>
{collapsible || (below && zeroWidthTrigger) ? triggerDom : null} {collapsible || (below.value && zeroWidthTrigger) ? triggerDom : null}
</aside> </aside>
); );
}; };

View File

@ -3,8 +3,7 @@ import classNames from '../_util/classNames';
import useConfigInject from '../_util/hooks/useConfigInject'; import useConfigInject from '../_util/hooks/useConfigInject';
import { skeletonElementProps, SkeletonElementProps } from './Element'; import { skeletonElementProps, SkeletonElementProps } from './Element';
export interface SkeletonImageProps export type SkeletonImageProps = Omit<SkeletonElementProps, 'size' | 'shape' | 'active'>;
extends Omit<SkeletonElementProps, 'size' | 'shape' | 'active'> {}
const path = const path =
'M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z'; 'M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z';

View File

@ -12,8 +12,8 @@ export const skeletonParagraphProps = {
export type SkeletonParagraphProps = Partial<ExtractPropTypes<typeof skeletonParagraphProps>>; export type SkeletonParagraphProps = Partial<ExtractPropTypes<typeof skeletonParagraphProps>>;
const SkeletonParagraph = defineComponent({ const SkeletonParagraph = defineComponent({
props: skeletonParagraphProps,
name: 'SkeletonParagraph', name: 'SkeletonParagraph',
props: skeletonParagraphProps,
setup(props) { setup(props) {
const getWidth = (index: number) => { const getWidth = (index: number) => {
const { width, rows = 2 } = props; const { width, rows = 2 } = props;

View File

@ -10,7 +10,7 @@ import useConfigInject from '../_util/hooks/useConfigInject';
import Element from './Element'; import Element from './Element';
/* This only for skeleton internal. */ /* This only for skeleton internal. */
interface SkeletonAvatarProps extends Omit<AvatarProps, 'active'> {} type SkeletonAvatarProps = Omit<AvatarProps, 'active'>;
export const skeletonProps = { export const skeletonProps = {
active: PropTypes.looseBool, active: PropTypes.looseBool,

View File

@ -9,8 +9,8 @@ export const skeletonTitleProps = {
export type SkeletonTitleProps = Partial<ExtractPropTypes<typeof skeletonTitleProps>>; export type SkeletonTitleProps = Partial<ExtractPropTypes<typeof skeletonTitleProps>>;
const SkeletonTitle = defineComponent({ const SkeletonTitle = defineComponent({
props: skeletonTitleProps,
name: 'SkeletonTitle', name: 'SkeletonTitle',
props: skeletonTitleProps,
setup(props) { setup(props) {
return () => { return () => {
const { prefixCls, width } = props; const { prefixCls, width } = props;