Skip to content

Commit f39d489

Browse files
committed
refactor: form
1 parent 975d70e commit f39d489

23 files changed

+762
-1001
lines changed

components/_util/transition.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ export const getTransitionProps = (transitionName: string, opt: TransitionProps
2727
// appearFromClass: `${transitionName}-appear ${transitionName}-appear-prepare`,
2828
// appearActiveClass: `antdv-base-transtion`,
2929
// appearToClass: `${transitionName}-appear ${transitionName}-appear-active`,
30-
enterFromClass: `${transitionName}-enter ${transitionName}-enter-prepare ${transitionName}-enter-start`,
31-
enterActiveClass: `${transitionName}-enter ${transitionName}-enter-prepare`,
32-
enterToClass: `${transitionName}-enter ${transitionName}-enter-active`,
30+
enterFromClass: `${transitionName} ${transitionName}-enter ${transitionName}-enter-prepare ${transitionName}-enter-start`,
31+
enterActiveClass: `${transitionName} ${transitionName}-enter ${transitionName}-enter-prepare`,
32+
enterToClass: `${transitionName} ${transitionName}-enter ${transitionName}-enter-active`,
3333
leaveFromClass: ` ${transitionName}-leave`,
3434
leaveActiveClass: `${transitionName}-leave ${transitionName}-leave-active`,
3535
leaveToClass: `${transitionName}-leave ${transitionName}-leave-active`,

components/config-provider/SizeContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export type SizeType = 'small' | 'middle' | 'large' | undefined;
44
const SizeContextKey: InjectionKey<Ref<SizeType>> = Symbol('SizeContextKey');
55

66
export const useInjectSize = () => {
7-
return inject(SizeContextKey, ref(undefined));
7+
return inject(SizeContextKey, ref(undefined as SizeType));
88
};
99
export const useProviderSize = (size: Ref<SizeType>) => {
1010
const parentSize = useInjectSize();

components/config-provider/hooks/useConfigInject.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { computed, h, inject } from 'vue';
2+
import type { SizeType } from '../context';
23
import { defaultConfigProvider, configProviderKey } from '../context';
4+
import { useInjectDisabled } from '../DisabledContext';
35
import { DefaultRenderEmpty } from '../renderEmpty';
6+
import { useInjectSize } from '../SizeContext';
47
export default (name: string, props: Record<any, any>) => {
8+
const sizeContext = useInjectSize();
9+
const disabledContext = useInjectDisabled();
510
const configProvider = inject(configProviderKey, {
611
...defaultConfigProvider,
712
renderEmpty: (name?: string) => h(DefaultRenderEmpty, { componentName: name }),
@@ -27,11 +32,11 @@ export default (name: string, props: Record<any, any>) => {
2732
? configProvider.virtual?.value !== false
2833
: props.virtual !== false) && dropdownMatchSelectWidth.value !== false,
2934
);
30-
const size = computed(() => props.size || configProvider.componentSize?.value);
35+
const size = computed(() => (props.size as SizeType) || sizeContext.value);
3136
const autocomplete = computed(
3237
() => props.autocomplete ?? configProvider.input?.value?.autocomplete,
3338
);
34-
const disabled = computed(() => props.disabled || configProvider.componentDisabled?.value);
39+
const disabled = computed<boolean>(() => props.disabled ?? disabledContext.value);
3540
const csp = computed(() => props.csp ?? configProvider.csp);
3641
return {
3742
configProvider,

components/form/ErrorList.tsx

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
import { useInjectFormItemPrefix } from './context';
22
import type { VueNode } from '../_util/type';
3-
import { computed, defineComponent, ref, watch } from 'vue';
4-
import { getTransitionGroupProps, TransitionGroup } from '../_util/transition';
5-
import useConfigInject from '../config-provider/hooks/useConfigInject';
3+
import { computed, defineComponent, ref, Transition, watch } from 'vue';
4+
import { getTransitionGroupProps, getTransitionProps, TransitionGroup } from '../_util/transition';
5+
66
import collapseMotion from '../_util/collapseMotion';
7+
import useStyle from './style';
78

89
export interface ErrorListProps {
910
errors?: VueNode[];
1011
/** @private Internal Usage. Do not use in your production */
1112
help?: VueNode;
12-
/** @private Internal Usage. Do not use in your production */
13-
onDomErrorVisibleChange?: (visible: boolean) => void;
13+
onErrorVisibleChanged?: (visible: boolean) => void;
1414
}
1515

1616
export default defineComponent({
1717
compatConfig: { MODE: 3 },
1818
name: 'ErrorList',
19-
props: ['errors', 'help', 'onDomErrorVisibleChange', 'helpStatus', 'warnings'],
20-
setup(props) {
21-
const { prefixCls: rootPrefixCls } = useConfigInject('', props);
19+
inheritAttrs: false,
20+
props: ['errors', 'help', 'onErrorVisibleChanged', 'helpStatus', 'warnings'],
21+
setup(props, { attrs }) {
2222
const { prefixCls, status } = useInjectFormItemPrefix();
2323
const baseClassName = computed(() => `${prefixCls.value}-item-explain`);
2424
const visible = computed(() => !!(props.errors && props.errors.length));
2525
const innerStatus = ref(status.value);
26-
26+
const [, hashId] = useStyle(prefixCls);
2727
// Memo status in same visible
2828
watch([visible, status], () => {
2929
if (visible.value) {
@@ -32,25 +32,35 @@ export default defineComponent({
3232
});
3333

3434
return () => {
35-
const colMItem = collapseMotion(`${rootPrefixCls.value}-show-help-item`);
35+
const colMItem = collapseMotion(`${prefixCls.value}-show-help-item`);
3636
const transitionGroupProps = getTransitionGroupProps(
37-
`${rootPrefixCls.value}-show-help-item`,
37+
`${prefixCls.value}-show-help-item`,
3838
colMItem,
3939
);
40-
(transitionGroupProps as any).class = baseClassName.value;
41-
return props.errors?.length ? (
42-
<TransitionGroup {...transitionGroupProps} tag="div">
43-
{props.errors?.map((error: any, index: number) => (
44-
<div
45-
key={index}
46-
role="alert"
47-
class={innerStatus.value ? `${baseClassName.value}-${innerStatus.value}` : ''}
48-
>
49-
{error}
50-
</div>
51-
))}
52-
</TransitionGroup>
53-
) : null;
40+
return (
41+
<Transition
42+
{...getTransitionProps(`${prefixCls.value}-show-help`)}
43+
onAfterEnter={() => props.onErrorVisibleChanged(true)}
44+
onAfterLeave={() => props.onErrorVisibleChanged(false)}
45+
>
46+
<TransitionGroup
47+
{...transitionGroupProps}
48+
tag="div"
49+
role="alert"
50+
v-show={!!props.errors?.length}
51+
class={[hashId.value, baseClassName.value, attrs.class]}
52+
>
53+
{props.errors?.map((error: any, index: number) => (
54+
<div
55+
key={index}
56+
class={innerStatus.value ? `${baseClassName.value}-${innerStatus.value}` : ''}
57+
>
58+
{error}
59+
</div>
60+
))}
61+
</TransitionGroup>
62+
</Transition>
63+
);
5464
};
5565
},
5666
});

components/form/Form.tsx

Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { PropType, ExtractPropTypes, HTMLAttributes, ComponentPublicInstance } from 'vue';
1+
import type { ExtractPropTypes, HTMLAttributes, ComponentPublicInstance } from 'vue';
22
import { defineComponent, computed, watch, ref } from 'vue';
33
import PropTypes from '../_util/vue-types';
44
import classNames from '../_util/classNames';
@@ -13,7 +13,15 @@ import isEqual from 'lodash-es/isEqual';
1313
import type { Options } from 'scroll-into-view-if-needed';
1414
import scrollIntoView from 'scroll-into-view-if-needed';
1515
import initDefaultProps from '../_util/props-util/initDefaultProps';
16-
import { tuple } from '../_util/type';
16+
import {
17+
anyType,
18+
booleanType,
19+
functionType,
20+
objectType,
21+
someType,
22+
stringType,
23+
tuple,
24+
} from '../_util/type';
1725
import type { ColProps } from '../grid/Col';
1826
import type {
1927
InternalNamePath,
@@ -31,7 +39,9 @@ import { useProvideForm } from './context';
3139
import type { SizeType } from '../config-provider';
3240
import useForm from './useForm';
3341
import { useInjectGlobalForm } from '../config-provider/context';
34-
42+
import useStyle from './style';
43+
import { useProviderSize } from '../config-provider/SizeContext';
44+
import { useProviderDisabled } from '../config-provider/DisabledContext';
3545
export type RequiredMark = boolean | 'optional';
3646
export type FormLayout = 'horizontal' | 'inline' | 'vertical';
3747

@@ -40,36 +50,31 @@ export type ValidationRule = Rule;
4050

4151
export const formProps = () => ({
4252
layout: PropTypes.oneOf(tuple('horizontal', 'inline', 'vertical')),
43-
labelCol: { type: Object as PropType<ColProps & HTMLAttributes> },
44-
wrapperCol: { type: Object as PropType<ColProps & HTMLAttributes> },
45-
colon: { type: Boolean, default: undefined },
46-
labelAlign: {
47-
...PropTypes.oneOf(tuple('left', 'right')),
48-
type: String as PropType<FormLabelAlign>,
49-
},
50-
labelWrap: { type: Boolean, default: undefined },
53+
labelCol: objectType<ColProps & HTMLAttributes>(),
54+
wrapperCol: objectType<ColProps & HTMLAttributes>(),
55+
colon: booleanType(),
56+
labelAlign: stringType<FormLabelAlign>(),
57+
labelWrap: booleanType(),
5158
prefixCls: String,
52-
requiredMark: { type: [String, Boolean] as PropType<RequiredMark | ''>, default: undefined },
59+
requiredMark: someType<RequiredMark | ''>([String, Boolean]),
5360
/** @deprecated Will warning in future branch. Pls use `requiredMark` instead. */
54-
hideRequiredMark: { type: Boolean, default: undefined },
61+
hideRequiredMark: booleanType(),
5562
model: PropTypes.object,
56-
rules: { type: Object as PropType<{ [k: string]: Rule[] | Rule }> },
57-
validateMessages: {
58-
type: Object as PropType<ValidateMessages>,
59-
default: undefined as ValidateMessages,
60-
},
61-
validateOnRuleChange: { type: Boolean, default: undefined },
63+
rules: objectType<{ [k: string]: Rule[] | Rule }>(),
64+
validateMessages: objectType<ValidateMessages>(),
65+
validateOnRuleChange: booleanType(),
6266
// 提交失败自动滚动到第一个错误字段
63-
scrollToFirstError: { type: [Boolean, Object] as PropType<boolean | Options> },
64-
onSubmit: Function as PropType<(e: Event) => void>,
67+
scrollToFirstError: anyType<boolean | Options>(),
68+
onSubmit: functionType<(e: Event) => void>(),
6569
name: String,
66-
validateTrigger: { type: [String, Array] as PropType<string | string[]> },
67-
size: { type: String as PropType<SizeType> },
68-
onValuesChange: { type: Function as PropType<Callbacks['onValuesChange']> },
69-
onFieldsChange: { type: Function as PropType<Callbacks['onFieldsChange']> },
70-
onFinish: { type: Function as PropType<Callbacks['onFinish']> },
71-
onFinishFailed: { type: Function as PropType<Callbacks['onFinishFailed']> },
72-
onValidate: { type: Function as PropType<Callbacks['onValidate']> },
70+
validateTrigger: someType<string | string[]>([String, Array]),
71+
size: stringType<SizeType>(),
72+
disabled: booleanType(),
73+
onValuesChange: functionType<Callbacks['onValuesChange']>(),
74+
onFieldsChange: functionType<Callbacks['onFieldsChange']>(),
75+
onFinish: functionType<Callbacks['onFinish']>(),
76+
onFinishFailed: functionType<Callbacks['onFinishFailed']>(),
77+
onValidate: functionType<Callbacks['onValidate']>(),
7378
});
7479

7580
export type FormProps = Partial<ExtractPropTypes<ReturnType<typeof formProps>>>;
@@ -114,7 +119,13 @@ const Form = defineComponent({
114119
useForm,
115120
// emits: ['finishFailed', 'submit', 'finish', 'validate'],
116121
setup(props, { emit, slots, expose, attrs }) {
117-
const { prefixCls, direction, form: contextForm, size } = useConfigInject('form', props);
122+
const {
123+
prefixCls,
124+
direction,
125+
form: contextForm,
126+
size,
127+
disabled,
128+
} = useConfigInject('form', props);
118129
const requiredMark = computed(() => props.requiredMark === '' || props.requiredMark);
119130
const mergedRequiredMark = computed(() => {
120131
if (requiredMark.value !== undefined) {
@@ -130,6 +141,8 @@ const Form = defineComponent({
130141
}
131142
return true;
132143
});
144+
useProviderSize(size);
145+
useProviderDisabled(disabled);
133146
const mergedColon = computed(() => props.colon ?? contextForm.value?.colon);
134147
const { validateMessages: globalValidateMessages } = useInjectGlobalForm();
135148
const validateMessages = computed(() => {
@@ -139,13 +152,21 @@ const Form = defineComponent({
139152
...props.validateMessages,
140153
};
141154
});
155+
156+
// Style
157+
const [wrapSSR, hashId] = useStyle(prefixCls);
158+
142159
const formClassName = computed(() =>
143-
classNames(prefixCls.value, {
144-
[`${prefixCls.value}-${props.layout}`]: true,
145-
[`${prefixCls.value}-hide-required-mark`]: mergedRequiredMark.value === false,
146-
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
147-
[`${prefixCls.value}-${size.value}`]: size.value,
148-
}),
160+
classNames(
161+
prefixCls.value,
162+
{
163+
[`${prefixCls.value}-${props.layout}`]: true,
164+
[`${prefixCls.value}-hide-required-mark`]: mergedRequiredMark.value === false,
165+
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
166+
[`${prefixCls.value}-${size.value}`]: size.value,
167+
},
168+
hashId.value,
169+
),
149170
);
150171
const lastValidatePromise = ref();
151172
const fields: Record<string, FieldExpose> = {};
@@ -380,10 +401,10 @@ const Form = defineComponent({
380401
);
381402

382403
return () => {
383-
return (
404+
return wrapSSR(
384405
<form {...attrs} onSubmit={handleSubmit} class={[formClassName.value, attrs.class]}>
385406
{slots.default?.()}
386-
</form>
407+
</form>,
387408
);
388409
};
389410
},

0 commit comments

Comments
 (0)