refactor: form

pull/6315/head
tangjinzhou 2023-02-21 23:11:05 +08:00
parent 975d70e7ed
commit f39d4894e4
23 changed files with 762 additions and 1001 deletions

View File

@ -27,9 +27,9 @@ export const getTransitionProps = (transitionName: string, opt: TransitionProps
// appearFromClass: `${transitionName}-appear ${transitionName}-appear-prepare`,
// appearActiveClass: `antdv-base-transtion`,
// appearToClass: `${transitionName}-appear ${transitionName}-appear-active`,
enterFromClass: `${transitionName}-enter ${transitionName}-enter-prepare ${transitionName}-enter-start`,
enterActiveClass: `${transitionName}-enter ${transitionName}-enter-prepare`,
enterToClass: `${transitionName}-enter ${transitionName}-enter-active`,
enterFromClass: `${transitionName} ${transitionName}-enter ${transitionName}-enter-prepare ${transitionName}-enter-start`,
enterActiveClass: `${transitionName} ${transitionName}-enter ${transitionName}-enter-prepare`,
enterToClass: `${transitionName} ${transitionName}-enter ${transitionName}-enter-active`,
leaveFromClass: ` ${transitionName}-leave`,
leaveActiveClass: `${transitionName}-leave ${transitionName}-leave-active`,
leaveToClass: `${transitionName}-leave ${transitionName}-leave-active`,

View File

@ -4,7 +4,7 @@ export type SizeType = 'small' | 'middle' | 'large' | undefined;
const SizeContextKey: InjectionKey<Ref<SizeType>> = Symbol('SizeContextKey');
export const useInjectSize = () => {
return inject(SizeContextKey, ref(undefined));
return inject(SizeContextKey, ref(undefined as SizeType));
};
export const useProviderSize = (size: Ref<SizeType>) => {
const parentSize = useInjectSize();

View File

@ -1,7 +1,12 @@
import { computed, h, inject } from 'vue';
import type { SizeType } from '../context';
import { defaultConfigProvider, configProviderKey } from '../context';
import { useInjectDisabled } from '../DisabledContext';
import { DefaultRenderEmpty } from '../renderEmpty';
import { useInjectSize } from '../SizeContext';
export default (name: string, props: Record<any, any>) => {
const sizeContext = useInjectSize();
const disabledContext = useInjectDisabled();
const configProvider = inject(configProviderKey, {
...defaultConfigProvider,
renderEmpty: (name?: string) => h(DefaultRenderEmpty, { componentName: name }),
@ -27,11 +32,11 @@ export default (name: string, props: Record<any, any>) => {
? configProvider.virtual?.value !== false
: props.virtual !== false) && dropdownMatchSelectWidth.value !== false,
);
const size = computed(() => props.size || configProvider.componentSize?.value);
const size = computed(() => (props.size as SizeType) || sizeContext.value);
const autocomplete = computed(
() => props.autocomplete ?? configProvider.input?.value?.autocomplete,
);
const disabled = computed(() => props.disabled || configProvider.componentDisabled?.value);
const disabled = computed<boolean>(() => props.disabled ?? disabledContext.value);
const csp = computed(() => props.csp ?? configProvider.csp);
return {
configProvider,

View File

@ -1,29 +1,29 @@
import { useInjectFormItemPrefix } from './context';
import type { VueNode } from '../_util/type';
import { computed, defineComponent, ref, watch } from 'vue';
import { getTransitionGroupProps, TransitionGroup } from '../_util/transition';
import useConfigInject from '../config-provider/hooks/useConfigInject';
import { computed, defineComponent, ref, Transition, watch } from 'vue';
import { getTransitionGroupProps, getTransitionProps, TransitionGroup } from '../_util/transition';
import collapseMotion from '../_util/collapseMotion';
import useStyle from './style';
export interface ErrorListProps {
errors?: VueNode[];
/** @private Internal Usage. Do not use in your production */
help?: VueNode;
/** @private Internal Usage. Do not use in your production */
onDomErrorVisibleChange?: (visible: boolean) => void;
onErrorVisibleChanged?: (visible: boolean) => void;
}
export default defineComponent({
compatConfig: { MODE: 3 },
name: 'ErrorList',
props: ['errors', 'help', 'onDomErrorVisibleChange', 'helpStatus', 'warnings'],
setup(props) {
const { prefixCls: rootPrefixCls } = useConfigInject('', props);
inheritAttrs: false,
props: ['errors', 'help', 'onErrorVisibleChanged', 'helpStatus', 'warnings'],
setup(props, { attrs }) {
const { prefixCls, status } = useInjectFormItemPrefix();
const baseClassName = computed(() => `${prefixCls.value}-item-explain`);
const visible = computed(() => !!(props.errors && props.errors.length));
const innerStatus = ref(status.value);
const [, hashId] = useStyle(prefixCls);
// Memo status in same visible
watch([visible, status], () => {
if (visible.value) {
@ -32,25 +32,35 @@ export default defineComponent({
});
return () => {
const colMItem = collapseMotion(`${rootPrefixCls.value}-show-help-item`);
const colMItem = collapseMotion(`${prefixCls.value}-show-help-item`);
const transitionGroupProps = getTransitionGroupProps(
`${rootPrefixCls.value}-show-help-item`,
`${prefixCls.value}-show-help-item`,
colMItem,
);
(transitionGroupProps as any).class = baseClassName.value;
return props.errors?.length ? (
<TransitionGroup {...transitionGroupProps} tag="div">
{props.errors?.map((error: any, index: number) => (
<div
key={index}
role="alert"
class={innerStatus.value ? `${baseClassName.value}-${innerStatus.value}` : ''}
>
{error}
</div>
))}
</TransitionGroup>
) : null;
return (
<Transition
{...getTransitionProps(`${prefixCls.value}-show-help`)}
onAfterEnter={() => props.onErrorVisibleChanged(true)}
onAfterLeave={() => props.onErrorVisibleChanged(false)}
>
<TransitionGroup
{...transitionGroupProps}
tag="div"
role="alert"
v-show={!!props.errors?.length}
class={[hashId.value, baseClassName.value, attrs.class]}
>
{props.errors?.map((error: any, index: number) => (
<div
key={index}
class={innerStatus.value ? `${baseClassName.value}-${innerStatus.value}` : ''}
>
{error}
</div>
))}
</TransitionGroup>
</Transition>
);
};
},
});

View File

@ -1,4 +1,4 @@
import type { PropType, ExtractPropTypes, HTMLAttributes, ComponentPublicInstance } from 'vue';
import type { ExtractPropTypes, HTMLAttributes, ComponentPublicInstance } from 'vue';
import { defineComponent, computed, watch, ref } from 'vue';
import PropTypes from '../_util/vue-types';
import classNames from '../_util/classNames';
@ -13,7 +13,15 @@ import isEqual from 'lodash-es/isEqual';
import type { Options } from 'scroll-into-view-if-needed';
import scrollIntoView from 'scroll-into-view-if-needed';
import initDefaultProps from '../_util/props-util/initDefaultProps';
import { tuple } from '../_util/type';
import {
anyType,
booleanType,
functionType,
objectType,
someType,
stringType,
tuple,
} from '../_util/type';
import type { ColProps } from '../grid/Col';
import type {
InternalNamePath,
@ -31,7 +39,9 @@ import { useProvideForm } from './context';
import type { SizeType } from '../config-provider';
import useForm from './useForm';
import { useInjectGlobalForm } from '../config-provider/context';
import useStyle from './style';
import { useProviderSize } from '../config-provider/SizeContext';
import { useProviderDisabled } from '../config-provider/DisabledContext';
export type RequiredMark = boolean | 'optional';
export type FormLayout = 'horizontal' | 'inline' | 'vertical';
@ -40,36 +50,31 @@ export type ValidationRule = Rule;
export const formProps = () => ({
layout: PropTypes.oneOf(tuple('horizontal', 'inline', 'vertical')),
labelCol: { type: Object as PropType<ColProps & HTMLAttributes> },
wrapperCol: { type: Object as PropType<ColProps & HTMLAttributes> },
colon: { type: Boolean, default: undefined },
labelAlign: {
...PropTypes.oneOf(tuple('left', 'right')),
type: String as PropType<FormLabelAlign>,
},
labelWrap: { type: Boolean, default: undefined },
labelCol: objectType<ColProps & HTMLAttributes>(),
wrapperCol: objectType<ColProps & HTMLAttributes>(),
colon: booleanType(),
labelAlign: stringType<FormLabelAlign>(),
labelWrap: booleanType(),
prefixCls: String,
requiredMark: { type: [String, Boolean] as PropType<RequiredMark | ''>, default: undefined },
requiredMark: someType<RequiredMark | ''>([String, Boolean]),
/** @deprecated Will warning in future branch. Pls use `requiredMark` instead. */
hideRequiredMark: { type: Boolean, default: undefined },
hideRequiredMark: booleanType(),
model: PropTypes.object,
rules: { type: Object as PropType<{ [k: string]: Rule[] | Rule }> },
validateMessages: {
type: Object as PropType<ValidateMessages>,
default: undefined as ValidateMessages,
},
validateOnRuleChange: { type: Boolean, default: undefined },
rules: objectType<{ [k: string]: Rule[] | Rule }>(),
validateMessages: objectType<ValidateMessages>(),
validateOnRuleChange: booleanType(),
//
scrollToFirstError: { type: [Boolean, Object] as PropType<boolean | Options> },
onSubmit: Function as PropType<(e: Event) => void>,
scrollToFirstError: anyType<boolean | Options>(),
onSubmit: functionType<(e: Event) => void>(),
name: String,
validateTrigger: { type: [String, Array] as PropType<string | string[]> },
size: { type: String as PropType<SizeType> },
onValuesChange: { type: Function as PropType<Callbacks['onValuesChange']> },
onFieldsChange: { type: Function as PropType<Callbacks['onFieldsChange']> },
onFinish: { type: Function as PropType<Callbacks['onFinish']> },
onFinishFailed: { type: Function as PropType<Callbacks['onFinishFailed']> },
onValidate: { type: Function as PropType<Callbacks['onValidate']> },
validateTrigger: someType<string | string[]>([String, Array]),
size: stringType<SizeType>(),
disabled: booleanType(),
onValuesChange: functionType<Callbacks['onValuesChange']>(),
onFieldsChange: functionType<Callbacks['onFieldsChange']>(),
onFinish: functionType<Callbacks['onFinish']>(),
onFinishFailed: functionType<Callbacks['onFinishFailed']>(),
onValidate: functionType<Callbacks['onValidate']>(),
});
export type FormProps = Partial<ExtractPropTypes<ReturnType<typeof formProps>>>;
@ -114,7 +119,13 @@ const Form = defineComponent({
useForm,
// emits: ['finishFailed', 'submit', 'finish', 'validate'],
setup(props, { emit, slots, expose, attrs }) {
const { prefixCls, direction, form: contextForm, size } = useConfigInject('form', props);
const {
prefixCls,
direction,
form: contextForm,
size,
disabled,
} = useConfigInject('form', props);
const requiredMark = computed(() => props.requiredMark === '' || props.requiredMark);
const mergedRequiredMark = computed(() => {
if (requiredMark.value !== undefined) {
@ -130,6 +141,8 @@ const Form = defineComponent({
}
return true;
});
useProviderSize(size);
useProviderDisabled(disabled);
const mergedColon = computed(() => props.colon ?? contextForm.value?.colon);
const { validateMessages: globalValidateMessages } = useInjectGlobalForm();
const validateMessages = computed(() => {
@ -139,13 +152,21 @@ const Form = defineComponent({
...props.validateMessages,
};
});
// Style
const [wrapSSR, hashId] = useStyle(prefixCls);
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,
}),
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,
},
hashId.value,
),
);
const lastValidatePromise = ref();
const fields: Record<string, FieldExpose> = {};
@ -380,10 +401,10 @@ const Form = defineComponent({
);
return () => {
return (
return wrapSSR(
<form {...attrs} onSubmit={handleSubmit} class={[formClassName.value, attrs.class]}>
{slots.default?.()}
</form>
</form>,
);
};
},

View File

@ -7,6 +7,7 @@ import type {
HTMLAttributes,
} from 'vue';
import {
onMounted,
reactive,
watch,
defineComponent,
@ -49,6 +50,7 @@ import type { FormItemStatusContextProps } from './FormItemContext';
import { FormItemInputContext, useProvideFormItemContext } from './FormItemContext';
import useDebounce from './utils/useDebounce';
import classNames from '../_util/classNames';
import useStyle from './style';
const ValidateStatuses = tuple('success', 'warning', 'error', 'validating', '');
export type ValidateStatus = (typeof ValidateStatuses)[number];
@ -156,6 +158,8 @@ export default defineComponent({
warning(props.prop === undefined, `\`prop\` is deprecated. Please use \`name\` instead.`);
const eventKey = `form-item-${++indexGuid}`;
const { prefixCls } = useConfigInject('form', props);
const [wrapSSR, hashId] = useStyle(prefixCls);
const itemRef = ref<HTMLDivElement>();
const formContext = useInjectForm();
const fieldName = computed(() => props.name || props.prop);
const errors = ref([]);
@ -165,6 +169,7 @@ export default defineComponent({
const val = fieldName.value;
return getNamePath(val);
});
const fieldId = computed(() => {
if (!namePath.value.length) {
return undefined;
@ -407,7 +412,7 @@ export default defineComponent({
});
const itemClassName = computed(() => ({
[`${prefixCls.value}-item`]: true,
[hashId.value]: true,
// Status
[`${prefixCls.value}-item-has-feedback`]: mergedValidateStatus.value && props.hasFeedback,
[`${prefixCls.value}-item-has-success`]: mergedValidateStatus.value === 'success',
@ -440,51 +445,94 @@ export default defineComponent({
isFormItemInput: true,
});
});
const marginBottom = ref<number>(null);
const showMarginOffset = ref(false);
const updateMarginBottom = () => {
if (itemRef.value) {
const itemStyle = getComputedStyle(itemRef.value);
marginBottom.value = parseInt(itemStyle.marginBottom, 10);
}
};
onMounted(() => {
watch(
showMarginOffset,
() => {
if (showMarginOffset.value) {
updateMarginBottom();
}
},
{ flush: 'post', immediate: true },
);
});
const onErrorVisibleChanged = (nextVisible: boolean) => {
if (!nextVisible) {
marginBottom.value = null;
}
};
return () => {
if (props.noStyle) return slots.default?.();
const help = props.help ?? (slots.help ? filterEmpty(slots.help()) : null);
return (
<Row
{...attrs}
const withHelp = !!(
(help !== undefined && help !== null && Array.isArray(help) && help.length) ||
debounceErrors.value.length
);
showMarginOffset.value = withHelp;
return wrapSSR(
<div
class={[
itemClassName.value,
(help !== undefined && help !== null) || debounceErrors.value.length
? `${prefixCls.value}-item-with-help`
: '',
withHelp ? `${prefixCls.value}-item-with-help` : '',
attrs.class,
]}
key="row"
v-slots={{
default: () => (
<>
{/* Label */}
<FormItemLabel
{...props}
htmlFor={htmlFor.value}
required={isRequired.value}
requiredMark={formContext.requiredMark.value}
prefixCls={prefixCls.value}
onClick={onLabelClick}
label={props.label ?? slots.label?.()}
/>
{/* Input Group */}
<FormItemInput
{...props}
errors={
help !== undefined && help !== null ? toArray(help) : debounceErrors.value
}
prefixCls={prefixCls.value}
status={mergedValidateStatus.value}
ref={inputRef}
help={help}
extra={props.extra ?? slots.extra?.()}
v-slots={{ default: slots.default }}
// v-slots={{ default: () => [firstChildren, children.slice(1)] }}
></FormItemInput>
</>
),
}}
></Row>
ref={itemRef}
>
<Row
{...attrs}
class={`${prefixCls.value}-row`}
key="row"
v-slots={{
default: () => (
<>
{/* Label */}
<FormItemLabel
{...props}
htmlFor={htmlFor.value}
required={isRequired.value}
requiredMark={formContext.requiredMark.value}
prefixCls={prefixCls.value}
onClick={onLabelClick}
label={props.label ?? slots.label?.()}
/>
{/* Input Group */}
<FormItemInput
{...props}
errors={
help !== undefined && help !== null ? toArray(help) : debounceErrors.value
}
marginBottom={marginBottom.value}
prefixCls={prefixCls.value}
status={mergedValidateStatus.value}
ref={inputRef}
help={help}
extra={props.extra ?? slots.extra?.()}
v-slots={{ default: slots.default }}
onErrorVisibleChanged={onErrorVisibleChanged}
></FormItemInput>
</>
),
}}
></Row>
{!!marginBottom.value && (
<div
class={`${prefixCls.value}-margin-offset`}
style={{
marginBottom: `-${marginBottom.value}px`,
}}
/>
)}
</div>,
);
};
},

View File

@ -7,6 +7,7 @@ import type { ValidateStatus } from './FormItem';
import type { VueNode } from '../_util/type';
import type { HTMLAttributes } from 'vue';
import { computed, defineComponent } from 'vue';
import { filterEmpty } from '../_util/props-util';
export interface FormItemInputMiscProps {
prefixCls: string;
@ -35,6 +36,8 @@ const FormItemInput = defineComponent({
'help',
'extra',
'status',
'marginBottom',
'onErrorVisibleChanged',
],
setup(props, { slots }) {
const formContext = useInjectForm();
@ -54,8 +57,10 @@ const FormItemInput = defineComponent({
const {
prefixCls,
wrapperCol,
marginBottom,
onErrorVisibleChanged,
help = slots.help?.(),
errors = slots.errors?.(),
errors = filterEmpty(slots.errors?.()),
// hasFeedback,
// status,
extra = slots.extra?.(),
@ -69,7 +74,6 @@ const FormItemInput = defineComponent({
// Should provides additional icon if `hasFeedback`
// const IconNode = status && iconMap[status];
return (
<Col
{...mergedWrapperCol}
@ -79,17 +83,18 @@ const FormItemInput = defineComponent({
<>
<div class={`${baseClassName}-control-input`}>
<div class={`${baseClassName}-control-input-content`}>{slots.default?.()}</div>
{/* {hasFeedback && IconNode ? (
<span class={`${baseClassName}-children-icon`}>
<IconNode />
</span>
) : null} */}
</div>
<ErrorList
errors={errors}
help={help}
class={`${baseClassName}-explain-connected`}
/>
{marginBottom !== null || errors.length ? (
<div style={{ display: 'flex', flexWrap: 'nowrap' }}>
<ErrorList
errors={errors}
help={help}
class={`${baseClassName}-explain-connected`}
onErrorVisibleChanged={onErrorVisibleChanged}
/>
{!!marginBottom && <div style={{ width: 0, height: `${marginBottom}px` }} />}
</div>
) : null}
{extra ? <div class={`${baseClassName}-extra`}>{extra}</div> : null}
</>
),

View File

@ -3,7 +3,7 @@ category: Components
type: Data Entry
cols: 1
title: Form
cover: https://gw.alipayobjects.com/zos/alicdn/ORmcdeaoO/Form.svg
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*-lcdS5Qm1bsAAAAAAAAAAAAADrJ8AQ/original
---
High performance Form component with data scope management. Including data collection, verification, and styles.
@ -32,6 +32,7 @@ A form consists of one or more form fields whose type includes input, textarea,
| Property | Description | Type | Default Value | Version |
| --- | --- | --- | --- | --- |
| colon | change default props colon value of Form.Item (only effective when prop layout is horizontal) | boolean | true | |
| disabled | Set form component disable, only available for antdv components | boolean | false | 4.0 |
| hideRequiredMark | Hide required mark of all form items | Boolean | false | |
| labelAlign | text align of label of all items | 'left' \| 'right' | 'right' | |
| labelCol | The layout of label. You can set `span` `offset` to something like `{span: 3, offset: 12}` or `sm: {span: 3, offset: 12}` same as with `<Col>` | [object](/components/grid/#Col) | | |

View File

@ -4,7 +4,7 @@ subtitle: 表单
type: 数据录入
cols: 1
title: Form
cover: https://gw.alipayobjects.com/zos/alicdn/ORmcdeaoO/Form.svg
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*-lcdS5Qm1bsAAAAAAAAAAAAADrJ8AQ/original
---
高性能表单控件,自带数据域管理。包含数据录入、校验以及对应样式。
@ -33,6 +33,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/ORmcdeaoO/Form.svg
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| colon | 配置 Form.Item 的 colon 的默认值 (只有在属性 layout 为 horizontal 时有效) | boolean | true | |
| disabled | 设置表单组件禁用,仅对 antdv 组件有效 | boolean | false | 4.0 |
| hideRequiredMark | 隐藏所有表单项的必选标记 | Boolean | false | |
| labelAlign | label 标签的文本对齐方式 | 'left' \| 'right' | 'right' | |
| labelCol | label 标签布局,同 `<Col>` 组件,设置 `span` `offset` 值,如 `{span: 3, offset: 12}``sm: {span: 3, offset: 12}` | [object](/components/grid-cn/#Col) | | |

View File

@ -1,16 +0,0 @@
@import (reference) '../../style/themes/index';
@form-prefix-cls: ~'@{ant-prefix}-form';
@form-item-prefix-cls: ~'@{form-prefix-cls}-item';
// ================================================================
// = Children Component =
// ================================================================
// FIXME: useless, remove in v5
.@{form-item-prefix-cls} {
.@{ant-prefix}-input-number {
+ .@{form-prefix-cls}-text {
margin-left: 8px;
}
}
}

View File

@ -0,0 +1,56 @@
import type { FormToken } from '.';
import type { GenerateStyle } from '../../theme/internal';
const genFormValidateMotionStyle: GenerateStyle<FormToken> = token => {
const { componentCls } = token;
const helpCls = `${componentCls}-show-help`;
const helpItemCls = `${componentCls}-show-help-item`;
return {
[helpCls]: {
// Explain holder
transition: `opacity ${token.motionDurationSlow} ${token.motionEaseInOut}`,
'&-appear, &-enter': {
opacity: 0,
'&-active': {
opacity: 1,
},
},
'&-leave': {
opacity: 1,
'&-active': {
opacity: 0,
},
},
// Explain
[helpItemCls]: {
overflow: 'hidden',
transition: `height ${token.motionDurationSlow} ${token.motionEaseInOut},
opacity ${token.motionDurationSlow} ${token.motionEaseInOut},
transform ${token.motionDurationSlow} ${token.motionEaseInOut} !important`,
[`&${helpItemCls}-appear, &${helpItemCls}-enter`]: {
transform: `translateY(-5px)`,
opacity: 0,
[`&-active`]: {
transform: 'translateY(0)',
opacity: 1,
},
},
[`&${helpItemCls}-leave-active`]: {
transform: `translateY(-5px)`,
},
},
},
};
};
export default genFormValidateMotionStyle;

View File

@ -1,22 +0,0 @@
@import (reference) '../../style/themes/index';
@form-prefix-cls: ~'@{ant-prefix}-form';
@form-item-prefix-cls: ~'@{form-prefix-cls}-item';
.@{form-prefix-cls}-horizontal {
.@{form-item-prefix-cls}-label {
flex-grow: 0;
}
.@{form-item-prefix-cls}-control {
flex: 1 1 0;
// https://github.com/ant-design/ant-design/issues/32777
// https://github.com/ant-design/ant-design/issues/33773
min-width: 0;
}
// https://github.com/ant-design/ant-design/issues/32980
// https://github.com/ant-design/ant-design/issues/34903
.@{form-item-prefix-cls}-label[class$='-24'] + .@{form-item-prefix-cls}-control,
.@{form-item-prefix-cls}-label[class*='-24 '] + .@{form-item-prefix-cls}-control {
min-width: unset;
}
}

View File

@ -1,323 +0,0 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@import '../../input/style/mixin';
@import './components';
@import './inline';
@import './horizontal';
@import './vertical';
@import './status';
@import './mixin';
@form-prefix-cls: ~'@{ant-prefix}-form';
@form-item-prefix-cls: ~'@{form-prefix-cls}-item';
@form-font-height: ceil(@font-size-base * @line-height-base);
.@{form-prefix-cls} {
.reset-component();
.reset-form();
.@{form-prefix-cls}-text {
display: inline-block;
padding-right: 8px;
}
// ================================================================
// = Size =
// ================================================================
.formSize(@input-height) {
.@{form-item-prefix-cls}-label > label {
height: @input-height;
}
.@{form-item-prefix-cls}-control-input {
min-height: @input-height;
}
}
&-small {
.formSize(@input-height-sm);
}
&-large {
.formSize(@input-height-lg);
}
}
.explainAndExtraDistance(@num) when (@num >= 0) {
padding-top: floor(@num);
}
.explainAndExtraDistance(@num) when (@num < 0) {
margin-top: ceil(@num);
margin-bottom: ceil(@num);
}
// ================================================================
// = Item =
// ================================================================
.@{form-item-prefix-cls} {
.reset-component();
margin-bottom: @form-item-margin-bottom;
vertical-align: top;
&-with-help {
margin-bottom: 0;
transition: none;
}
&-hidden,
&-hidden.@{ant-prefix}-row {
// https://github.com/ant-design/ant-design/issues/26141
display: none;
}
// ==============================================================
// = Label =
// ==============================================================
&-label {
display: inline-block;
flex-grow: 0;
overflow: hidden;
white-space: nowrap;
text-align: right;
vertical-align: middle;
&-left {
text-align: left;
}
&-wrap {
overflow: unset;
line-height: (@line-height-base - 0.25em);
white-space: unset;
}
> label {
position: relative;
display: inline-flex;
align-items: center;
max-width: 100%;
height: @form-item-label-height;
color: @label-color;
font-size: @form-item-label-font-size;
> .@{iconfont-css-prefix} {
font-size: @form-item-label-font-size;
vertical-align: top;
}
// Required mark
&.@{form-item-prefix-cls}-required:not(
.@{form-item-prefix-cls}-required-mark-optional
)::before {
display: inline-block;
margin-right: 4px;
color: @label-required-color;
font-size: @form-item-label-font-size;
font-family: SimSun, sans-serif;
line-height: 1;
content: '*';
.@{form-prefix-cls}-hide-required-mark & {
display: none;
}
}
// Optional mark
.@{form-item-prefix-cls}-optional {
display: inline-block;
margin-left: @margin-xss;
color: @text-color-secondary;
.@{form-prefix-cls}-hide-required-mark & {
display: none;
}
}
// Optional mark
.@{form-item-prefix-cls}-tooltip {
color: @text-color-secondary;
cursor: help;
writing-mode: horizontal-tb;
margin-inline-start: @margin-xss;
}
&::after {
& when (@form-item-trailing-colon=true) {
content: ':';
}
& when not (@form-item-trailing-colon=true) {
content: ' ';
}
position: relative;
top: -0.5px;
margin: 0 @form-item-label-colon-margin-right 0 @form-item-label-colon-margin-left;
}
&.@{form-item-prefix-cls}-no-colon::after {
content: ' ';
}
}
}
// ==============================================================
// = Input =
// ==============================================================
&-control {
display: flex;
flex-direction: column;
flex-grow: 1;
&:first-child:not([class^=~"'@{ant-prefix}-col-'"]):not([class*=~"' @{ant-prefix}-col-'"]) {
width: 100%;
}
}
&-control-input {
position: relative;
display: flex;
align-items: center;
min-height: @input-height-base;
&-content {
flex: auto;
max-width: 100%;
}
}
// ==============================================================
// = Explain =
// ==============================================================
&-explain,
&-extra {
clear: both;
color: @text-color-secondary;
font-size: @font-size-base;
line-height: @line-height-base;
transition: color 0.3s @ease-out; // sync input color transition
.explainAndExtraDistance((@form-item-margin-bottom - @form-font-height) / 2);
}
&-explain-connected {
height: 0;
min-height: 0;
opacity: 0;
}
&-extra {
min-height: @form-item-margin-bottom;
}
&-with-help &-explain {
height: auto;
min-height: @form-item-margin-bottom;
opacity: 1;
}
// ==============================================================
// = Feedback Icon =
// ==============================================================
&-feedback-icon {
font-size: @font-size-base;
text-align: center;
visibility: visible;
animation: zoomIn 0.3s @ease-out-back;
pointer-events: none;
&-success {
color: @success-color;
}
&-error {
color: @error-color;
}
&-warning {
color: @warning-color;
}
&-validating {
color: @primary-color;
}
}
}
// >>>>>>>>>> Motion <<<<<<<<<<
// Explain holder
.@{ant-prefix}-show-help {
transition: height @animation-duration-slow linear, min-height @animation-duration-slow linear,
margin-bottom @animation-duration-slow @ease-in-out,
opacity @animation-duration-slow @ease-in-out;
&-leave {
min-height: @form-item-margin-bottom;
&-active {
min-height: 0;
}
}
}
// Explain
.@{ant-prefix}-show-help-item {
overflow: hidden;
transition: height @animation-duration-slow @ease-in-out,
opacity @animation-duration-slow @ease-in-out, transform @animation-duration-slow @ease-in-out !important;
&-appear,
&-enter {
transform: translateY(-5px);
opacity: 0;
&-active {
transform: translateY(0);
opacity: 1;
}
}
&-leave-active {
transform: translateY(-5px);
}
}
// need there different zoom animation
// otherwise won't trigger anim
@keyframes diffZoomIn1 {
0% {
transform: scale(0);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
@keyframes diffZoomIn2 {
0% {
transform: scale(0);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
@keyframes diffZoomIn3 {
0% {
transform: scale(0);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
@import './rtl';

View File

@ -0,0 +1,492 @@
import type { CSSObject } from '../../_util/cssinjs';
import { genCollapseMotion, zoomIn } from '../../_style/motion';
import type { AliasToken, FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { resetComponent } from '../../_style';
import genFormValidateMotionStyle from './explain';
export interface FormToken extends FullToken<'Form'> {
formItemCls: string;
rootPrefixCls: string;
}
const resetForm = (token: AliasToken): CSSObject => ({
legend: {
display: 'block',
width: '100%',
marginBottom: token.marginLG,
padding: 0,
color: token.colorTextDescription,
fontSize: token.fontSizeLG,
lineHeight: 'inherit',
border: 0,
borderBottom: `${token.lineWidth}px ${token.lineType} ${token.colorBorder}`,
},
label: {
fontSize: token.fontSize,
},
'input[type="search"]': {
boxSizing: 'border-box',
},
// Position radios and checkboxes better
'input[type="radio"], input[type="checkbox"]': {
lineHeight: 'normal',
},
'input[type="file"]': {
display: 'block',
},
// Make range inputs behave like textual form controls
'input[type="range"]': {
display: 'block',
width: '100%',
},
// Make multiple select elements height not fixed
'select[multiple], select[size]': {
height: 'auto',
},
// Focus for file, radio, and checkbox
[`input[type='file']:focus,
input[type='radio']:focus,
input[type='checkbox']:focus`]: {
outline: 0,
boxShadow: `0 0 0 ${token.controlOutlineWidth}px ${token.controlOutline}`,
},
// Adjust output element
output: {
display: 'block',
paddingTop: 15,
color: token.colorText,
fontSize: token.fontSize,
lineHeight: token.lineHeight,
},
});
const genFormSize = (token: FormToken, height: number): CSSObject => {
const { formItemCls } = token;
return {
[formItemCls]: {
[`${formItemCls}-label > label`]: {
height,
},
[`${formItemCls}-control-input`]: {
minHeight: height,
},
},
};
};
const genFormStyle: GenerateStyle<FormToken> = token => {
const { componentCls } = token;
return {
[token.componentCls]: {
...resetComponent(token),
...resetForm(token),
[`${componentCls}-text`]: {
display: 'inline-block',
paddingInlineEnd: token.paddingSM,
},
// ================================================================
// = Size =
// ================================================================
'&-small': {
...genFormSize(token, token.controlHeightSM),
},
'&-large': {
...genFormSize(token, token.controlHeightLG),
},
},
};
};
const genFormItemStyle: GenerateStyle<FormToken> = token => {
const { formItemCls, iconCls, componentCls, rootPrefixCls } = token;
return {
[formItemCls]: {
...resetComponent(token),
marginBottom: token.marginLG,
verticalAlign: 'top',
'&-with-help': {
transition: 'none',
},
[`&-hidden,
&-hidden.${rootPrefixCls}-row`]: {
// https://github.com/ant-design/ant-design/issues/26141
display: 'none',
},
'&-has-warning': {
[`${formItemCls}-split`]: {
color: token.colorError,
},
},
'&-has-error': {
[`${formItemCls}-split`]: {
color: token.colorWarning,
},
},
// ==============================================================
// = Label =
// ==============================================================
[`${formItemCls}-label`]: {
display: 'inline-block',
flexGrow: 0,
overflow: 'hidden',
whiteSpace: 'nowrap',
textAlign: 'end',
verticalAlign: 'middle',
'&-left': {
textAlign: 'start',
},
'&-wrap': {
overflow: 'unset',
lineHeight: `${token.lineHeight} - 0.25em`,
whiteSpace: 'unset',
},
'> label': {
position: 'relative',
display: 'inline-flex',
alignItems: 'center',
maxWidth: '100%',
height: token.controlHeight,
color: token.colorTextHeading,
fontSize: token.fontSize,
[`> ${iconCls}`]: {
fontSize: token.fontSize,
verticalAlign: 'top',
},
// Required mark
[`&${formItemCls}-required:not(${formItemCls}-required-mark-optional)::before`]: {
display: 'inline-block',
marginInlineEnd: token.marginXXS,
color: token.colorError,
fontSize: token.fontSize,
fontFamily: 'SimSun, sans-serif',
lineHeight: 1,
content: '"*"',
[`${componentCls}-hide-required-mark &`]: {
display: 'none',
},
},
// Optional mark
[`${formItemCls}-optional`]: {
display: 'inline-block',
marginInlineStart: token.marginXXS,
color: token.colorTextDescription,
[`${componentCls}-hide-required-mark &`]: {
display: 'none',
},
},
// Optional mark
[`${formItemCls}-tooltip`]: {
color: token.colorTextDescription,
cursor: 'help',
writingMode: 'horizontal-tb',
marginInlineStart: token.marginXXS,
},
'&::after': {
content: '":"',
position: 'relative',
marginBlock: 0,
marginInlineStart: token.marginXXS / 2,
marginInlineEnd: token.marginXS,
},
[`&${formItemCls}-no-colon::after`]: {
content: '" "',
},
},
},
// ==============================================================
// = Input =
// ==============================================================
[`${formItemCls}-control`]: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
[`&:first-child:not([class^="'${rootPrefixCls}-col-'"]):not([class*="' ${rootPrefixCls}-col-'"])`]:
{
width: '100%',
},
'&-input': {
position: 'relative',
display: 'flex',
alignItems: 'center',
minHeight: token.controlHeight,
'&-content': {
flex: 'auto',
maxWidth: '100%',
},
},
},
// ==============================================================
// = Explain =
// ==============================================================
[formItemCls]: {
'&-explain, &-extra': {
clear: 'both',
color: token.colorTextDescription,
fontSize: token.fontSize,
lineHeight: token.lineHeight,
},
'&-explain-connected': {
width: '100%',
},
'&-extra': {
minHeight: token.controlHeightSM,
transition: `color ${token.motionDurationMid} ${token.motionEaseOut}`, // sync input color transition
},
'&-explain': {
'&-error': {
color: token.colorError,
},
'&-warning': {
color: token.colorWarning,
},
},
},
[`&-with-help ${formItemCls}-explain`]: {
height: 'auto',
opacity: 1,
},
// ==============================================================
// = Feedback Icon =
// ==============================================================
[`${formItemCls}-feedback-icon`]: {
fontSize: token.fontSize,
textAlign: 'center',
visibility: 'visible',
animationName: zoomIn,
animationDuration: token.motionDurationMid,
animationTimingFunction: token.motionEaseOutBack,
pointerEvents: 'none',
'&-success': {
color: token.colorSuccess,
},
'&-error': {
color: token.colorError,
},
'&-warning': {
color: token.colorWarning,
},
'&-validating': {
color: token.colorPrimary,
},
},
},
};
};
const genHorizontalStyle: GenerateStyle<FormToken> = token => {
const { componentCls, formItemCls, rootPrefixCls } = token;
return {
[`${componentCls}-horizontal`]: {
[`${formItemCls}-label`]: {
flexGrow: 0,
},
[`${formItemCls}-control`]: {
flex: '1 1 0',
// https://github.com/ant-design/ant-design/issues/32777
// https://github.com/ant-design/ant-design/issues/33773
minWidth: 0,
},
// https://github.com/ant-design/ant-design/issues/32980
[`${formItemCls}-label.${rootPrefixCls}-col-24 + ${formItemCls}-control`]: {
minWidth: 'unset',
},
},
};
};
const genInlineStyle: GenerateStyle<FormToken> = token => {
const { componentCls, formItemCls } = token;
return {
[`${componentCls}-inline`]: {
display: 'flex',
flexWrap: 'wrap',
[formItemCls]: {
flex: 'none',
flexWrap: 'nowrap',
marginInlineEnd: token.margin,
marginBottom: 0,
'&-with-help': {
marginBottom: token.marginLG,
},
[`> ${formItemCls}-label,
> ${formItemCls}-control`]: {
display: 'inline-block',
verticalAlign: 'top',
},
[`> ${formItemCls}-label`]: {
flex: 'none',
},
[`${componentCls}-text`]: {
display: 'inline-block',
},
[`${formItemCls}-has-feedback`]: {
display: 'inline-block',
},
},
},
};
};
const makeVerticalLayoutLabel = (token: FormToken): CSSObject => ({
margin: 0,
padding: `0 0 ${token.paddingXS}px`,
whiteSpace: 'initial',
textAlign: 'start',
'> label': {
margin: 0,
'&::after': {
display: 'none',
},
},
});
const makeVerticalLayout = (token: FormToken): CSSObject => {
const { componentCls, formItemCls } = token;
return {
[`${formItemCls} ${formItemCls}-label`]: makeVerticalLayoutLabel(token),
[componentCls]: {
[formItemCls]: {
flexWrap: 'wrap',
[`${formItemCls}-label,
${formItemCls}-control`]: {
flex: '0 0 100%',
maxWidth: '100%',
},
},
},
};
};
const genVerticalStyle: GenerateStyle<FormToken> = token => {
const { componentCls, formItemCls, rootPrefixCls } = token;
return {
[`${componentCls}-vertical`]: {
[formItemCls]: {
'&-row': {
flexDirection: 'column',
},
'&-label > label': {
height: 'auto',
},
[`${componentCls}-item-control`]: {
width: '100%',
},
},
},
[`${componentCls}-vertical ${formItemCls}-label,
.${rootPrefixCls}-col-24${formItemCls}-label,
.${rootPrefixCls}-col-xl-24${formItemCls}-label`]: makeVerticalLayoutLabel(token),
[`@media (max-width: ${token.screenXSMax}px)`]: [
makeVerticalLayout(token),
{
[componentCls]: {
[`.${rootPrefixCls}-col-xs-24${formItemCls}-label`]: makeVerticalLayoutLabel(token),
},
},
],
[`@media (max-width: ${token.screenSMMax}px)`]: {
[componentCls]: {
[`.${rootPrefixCls}-col-sm-24${formItemCls}-label`]: makeVerticalLayoutLabel(token),
},
},
[`@media (max-width: ${token.screenMDMax}px)`]: {
[componentCls]: {
[`.${rootPrefixCls}-col-md-24${formItemCls}-label`]: makeVerticalLayoutLabel(token),
},
},
[`@media (max-width: ${token.screenLGMax}px)`]: {
[componentCls]: {
[`.${rootPrefixCls}-col-lg-24${formItemCls}-label`]: makeVerticalLayoutLabel(token),
},
},
};
};
// ============================== Export ==============================
export default genComponentStyleHook('Form', (token, { rootPrefixCls }) => {
const formToken = mergeToken<FormToken>(token, {
formItemCls: `${token.componentCls}-item`,
rootPrefixCls,
});
return [
genFormStyle(formToken),
genFormItemStyle(formToken),
genFormValidateMotionStyle(formToken),
genHorizontalStyle(formToken),
genInlineStyle(formToken),
genVerticalStyle(formToken),
genCollapseMotion(formToken),
zoomIn,
];
});

View File

@ -1,5 +0,0 @@
import '../../style/index.less';
import './index.less';
// style dependencies
import '../../tooltip/style';

View File

@ -1,38 +0,0 @@
@import (reference) '../../style/themes/index';
@form-prefix-cls: ~'@{ant-prefix}-form';
@form-item-prefix-cls: ~'@{form-prefix-cls}-item';
.@{form-prefix-cls}-inline {
display: flex;
flex-wrap: wrap;
.@{form-prefix-cls}-item {
flex: none;
flex-wrap: nowrap;
margin-right: 16px;
margin-bottom: 0;
&-with-help {
margin-bottom: @form-item-margin-bottom;
}
> .@{form-item-prefix-cls}-label,
> .@{form-item-prefix-cls}-control {
display: inline-block;
vertical-align: top;
}
> .@{form-item-prefix-cls}-label {
flex: none;
}
.@{form-prefix-cls}-text {
display: inline-block;
}
.@{form-item-prefix-cls}-has-feedback {
display: inline-block;
}
}
}

View File

@ -1,78 +0,0 @@
@import '../../input/style/mixin';
.form-control-validation(
@text-color: @input-color;
@border-color: @input-border-color;
@background-color: @input-bg;
@hoverBorderColor: @primary-color-hover;
@outlineColor: @primary-color-outline;
) {
.@{ant-prefix}-form-item-split {
color: @text-color;
}
}
// Reset form styles
// -----------------------------
// Based on Bootstrap framework
.reset-form() {
legend {
display: block;
width: 100%;
margin-bottom: 20px;
padding: 0;
color: @text-color-secondary;
font-size: @font-size-lg;
line-height: inherit;
border: 0;
border-bottom: @border-width-base @border-style-base @border-color-base;
}
label {
font-size: @font-size-base;
}
input[type='search'] {
box-sizing: border-box;
}
// Position radios and checkboxes better
input[type='radio'],
input[type='checkbox'] {
line-height: normal;
}
input[type='file'] {
display: block;
}
// Make range inputs behave like textual form controls
input[type='range'] {
display: block;
width: 100%;
}
// Make multiple select elements height not fixed
select[multiple],
select[size] {
height: auto;
}
// Focus for file, radio, and checkbox
input[type='file']:focus,
input[type='radio']:focus,
input[type='checkbox']:focus {
outline: thin dotted;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
// Adjust output element
output {
display: block;
padding-top: 15px;
color: @input-color;
font-size: @font-size-base;
line-height: @line-height-base;
}
}

View File

@ -1,202 +0,0 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@import '../../input/style/mixin';
@form-prefix-cls: ~'@{ant-prefix}-form';
@form-item-prefix-cls: ~'@{form-prefix-cls}-item';
.@{form-prefix-cls} {
&-rtl {
direction: rtl;
}
}
// ================================================================
// = Item =
// ================================================================
.@{form-item-prefix-cls} {
// ==============================================================
// = Label =
// ==============================================================
&-label {
.@{form-prefix-cls}-rtl & {
text-align: left;
}
> label {
&.@{form-item-prefix-cls}-required::before {
.@{form-prefix-cls}-rtl & {
margin-right: 0;
margin-left: 4px;
}
}
&::after {
.@{form-prefix-cls}-rtl & {
margin: 0 @form-item-label-colon-margin-left 0 @form-item-label-colon-margin-right;
}
}
.@{form-item-prefix-cls}-optional {
.@{form-prefix-cls}-rtl & {
margin-right: @margin-xss;
margin-left: 0;
}
}
}
}
// ==============================================================
// = Input =
// ==============================================================
&-control {
.@{ant-prefix}-col-rtl &:first-child {
width: 100%;
}
}
// status
&-has-feedback {
.@{ant-prefix}-input {
.@{form-prefix-cls}-rtl & {
padding-right: @input-padding-horizontal-base;
padding-left: 24px;
}
}
.@{ant-prefix}-input-affix-wrapper {
.@{ant-prefix}-input-suffix {
.@{form-prefix-cls}-rtl & {
padding-right: @input-padding-horizontal-base;
padding-left: 18px;
}
}
.@{ant-prefix}-input {
.@{form-prefix-cls}-rtl & {
padding: 0;
}
}
}
.@{ant-prefix}-input-number-affix-wrapper {
.@{ant-prefix}-input-number {
.@{form-prefix-cls}-rtl & {
padding: 0;
}
}
}
.@{ant-prefix}-input-search:not(.@{ant-prefix}-input-search-enter-button) {
.@{ant-prefix}-input-suffix {
.@{form-prefix-cls}-rtl & {
right: auto;
left: 28px;
}
}
}
.@{ant-prefix}-input-number {
.@{form-prefix-cls}-rtl & {
padding-left: 18px;
}
}
> .@{ant-prefix}-select .@{ant-prefix}-select-arrow,
> .@{ant-prefix}-select .@{ant-prefix}-select-clear,
:not(.@{ant-prefix}-input-group-addon) > .@{ant-prefix}-select .@{ant-prefix}-select-arrow,
:not(.@{ant-prefix}-input-group-addon) > .@{ant-prefix}-select .@{ant-prefix}-select-clear,
:not(.@{ant-prefix}-input-number-group-addon)
> .@{ant-prefix}-select
.@{ant-prefix}-select-arrow,
:not(.@{ant-prefix}-input-number-group-addon)
> .@{ant-prefix}-select
.@{ant-prefix}-select-clear {
.@{form-prefix-cls}-rtl & {
right: auto;
left: 32px;
}
}
> .@{ant-prefix}-select .@{ant-prefix}-select-selection-selected-value,
:not(.@{ant-prefix}-input-group-addon)
> .@{ant-prefix}-select
.@{ant-prefix}-select-selection-selected-value,
:not(.@{ant-prefix}-input-number-group-addon)
> .@{ant-prefix}-select
.@{ant-prefix}-select-selection-selected-value {
.@{form-prefix-cls}-rtl & {
padding-right: 0;
padding-left: 42px;
}
}
.@{ant-prefix}-cascader-picker {
&-arrow {
.@{form-prefix-cls}-rtl & {
margin-right: 0;
margin-left: 19px;
}
}
&-clear {
.@{form-prefix-cls}-rtl & {
right: auto;
left: 32px;
}
}
}
.@{ant-prefix}-picker {
.@{form-prefix-cls}-rtl & {
padding-right: @input-padding-horizontal-base;
padding-left: @input-padding-horizontal-base + @font-size-base * 1.3;
}
&-large {
.@{form-prefix-cls}-rtl & {
padding-right: @input-padding-horizontal-lg;
padding-left: @input-padding-horizontal-lg + @font-size-base * 1.3;
}
}
&-small {
.@{form-prefix-cls}-rtl & {
padding-right: @input-padding-horizontal-sm;
padding-left: @input-padding-horizontal-sm + @font-size-base * 1.3;
}
}
}
&.@{form-item-prefix-cls} {
&-has-success,
&-has-warning,
&-has-error,
&-is-validating {
// ====================== Icon ======================
.@{form-item-prefix-cls}-children-icon {
.@{form-prefix-cls}-rtl & {
right: auto;
left: 0;
}
}
}
}
}
}
// inline
.@{form-prefix-cls}-inline {
.@{form-prefix-cls}-item {
.@{form-prefix-cls}-rtl& {
margin-right: 0;
margin-left: 16px;
}
}
}
// vertical
.make-vertical-layout-label() {
.@{form-prefix-cls}-rtl& {
text-align: right;
}
}

View File

@ -1,42 +0,0 @@
@import (reference) '../../style/themes/index';
@form-prefix-cls: ~'@{ant-prefix}-form';
@form-item-prefix-cls: ~'@{form-prefix-cls}-item';
.@{form-item-prefix-cls} {
// ================================================================
// = Status =
// ================================================================
/* Some non-status related component style is in `components.less` */
// ========================= Explain =========================
/* To support leave along ErrorList. We add additional className to handle explain style */
&-explain {
&-error {
color: @error-color;
}
&-warning {
color: @warning-color;
}
}
&-has-feedback {
// ======================== Switch =========================
.@{ant-prefix}-switch {
margin: 2px 0 4px;
}
}
// ======================== Warning ========================
&-has-warning {
.form-control-validation(@warning-color; @warning-color; @form-warning-input-bg; @warning-color-hover; @warning-color-outline);
}
// ========================= Error =========================
&-has-error {
.form-control-validation(@error-color; @error-color; @form-error-input-bg; @error-color-hover; @error-color-outline);
}
}

View File

@ -1,87 +0,0 @@
@import (reference) '../../style/themes/index';
@form-prefix-cls: ~'@{ant-prefix}-form';
@form-item-prefix-cls: ~'@{form-prefix-cls}-item';
// ================== Label ==================
.make-vertical-layout-label() {
& when (@form-vertical-label-margin > 0) {
margin: @form-vertical-label-margin;
}
padding: @form-vertical-label-padding;
line-height: @line-height-base;
white-space: initial;
text-align: left;
> label {
margin: 0;
&::after {
display: none;
}
}
}
.make-vertical-layout() {
.@{form-prefix-cls}-item .@{form-prefix-cls}-item-label {
.make-vertical-layout-label();
}
.@{form-prefix-cls} {
.@{form-prefix-cls}-item {
flex-wrap: wrap;
.@{form-prefix-cls}-item-label,
.@{form-prefix-cls}-item-control {
flex: 0 0 100%;
max-width: 100%;
}
}
}
}
.@{form-prefix-cls}-vertical {
.@{form-item-prefix-cls} {
flex-direction: column;
&-label > label {
height: auto;
}
}
}
.@{form-prefix-cls}-vertical .@{form-item-prefix-cls}-label,
/* when labelCol is 24, it is a vertical form */
.@{ant-prefix}-col-24.@{form-item-prefix-cls}-label,
.@{ant-prefix}-col-xl-24.@{form-item-prefix-cls}-label {
.make-vertical-layout-label();
}
@media (max-width: @screen-xs-max) {
.make-vertical-layout();
.@{ant-prefix}-col-xs-24.@{form-item-prefix-cls}-label {
.make-vertical-layout-label();
}
}
@media (max-width: @screen-sm-max) {
.@{ant-prefix}-col-sm-24.@{form-item-prefix-cls}-label {
.make-vertical-layout-label();
}
}
@media (max-width: @screen-md-max) {
.@{ant-prefix}-col-md-24.@{form-item-prefix-cls}-label {
.make-vertical-layout-label();
}
}
@media (max-width: @screen-lg-max) {
.@{ant-prefix}-col-lg-24.@{form-item-prefix-cls}-label {
.make-vertical-layout-label();
}
}
@media (max-width: @screen-xl-max) {
.@{ant-prefix}-col-xl-24.@{form-item-prefix-cls}-label {
.make-vertical-layout-label();
}
}

View File

@ -154,7 +154,7 @@ export default defineComponent({
<div
{...attrs}
class={classes.value}
style={{ ...mergedStyle.value, ...(attrs.style as CSSProperties) }}
style={[mergedStyle.value, attrs.style as CSSProperties]}
>
{slots.default?.()}
</div>,

View File

@ -1,64 +0,0 @@
// import './button/style';
// import './icon/style';
// import './radio/style';
// import './checkbox/style';
// import './grid/style';
// import './tag/style';
// import './rate/style';
// import './pagination/style';
// import './avatar/style';
// import './badge/style';
//import './tabs/style';
// import './input/style';
// import './tooltip/style';
// import './popover/style';
// import './popconfirm/style';
// import './menu/style';
// import './mentions/style';
// import './dropdown/style';
// import './divider/style';
// import './card/style';
// import './collapse/style';
// import './carousel/style';
// import './notification/style';
// import './message/style';
// import './spin/style';
// import './select/style';
// import './switch/style';
// import './auto-complete/style';
// import './affix/style';
// import './cascader/style';
// import './back-top/style';
// import './modal/style';
// import './alert/style';
// import './time-picker/style';
// import './steps/style';
// import './breadcrumb/style';
// import './calendar/style';
// import './date-picker/style';
// import './slider/style';
// import './table/style';
// import './progress/style';
// import './timeline/style';
// import './input-number/style';
// import './transfer/style';
// import './tree/style';
// import './upload/style';
// import './layout/style';
// import './anchor/style';
// import './list/style';
// import './tree-select/style';
// import './drawer/style';
// import './skeleton/style';
// import './comment/style';
// import './config-provider/style';
// import './empty/style';
// import './statistic/style';
// import './result/style';
// import './descriptions/style';
// import './page-header/style';
import './form/style';
// import './space/style';
// import './image/style';
// import './typography/style';
// import './color-picker/style';

View File

@ -30,7 +30,7 @@ import type { ComponentToken as ProgressComponentToken } from '../../progress/st
import type { ComponentToken as RadioComponentToken } from '../../radio/style';
import type { ComponentToken as RateComponentToken } from '../../rate/style';
import type { ComponentToken as ResultComponentToken } from '../../result/style';
// import type { ComponentToken as SegmentedComponentToken } from '../../segmented/style';
import type { ComponentToken as SegmentedComponentToken } from '../../segmented/style';
import type { ComponentToken as SelectComponentToken } from '../../select/style';
import type { ComponentToken as SkeletonComponentToken } from '../../skeleton/style';
import type { ComponentToken as SliderComponentToken } from '../../slider/style';
@ -49,7 +49,6 @@ import type { ComponentToken as UploadComponentToken } from '../../upload/style'
// import type { ComponentToken as QRCodeComponentToken } from '../../qrcode/style';
// import type { ComponentToken as AppComponentToken } from '../../app/style';
// import type { ComponentToken as WaveToken } from '../../_util/wave/style';
import type { ComponentToken as SegmentedComponentToken } from '../../segmented/style';
export interface ComponentTokenMap {
Affix?: {};