feat: form support variant (#46573)

This commit is contained in:
MadCcc 2023-12-22 10:05:15 +08:00 committed by GitHub
parent bf7085882d
commit 924f46fd88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 3486 additions and 96 deletions

View File

@ -1,24 +0,0 @@
type DefaultVariant = 'outlined' | 'borderless';
/**
* Compatible for legacy `bordered` prop.
*/
const useVariant = <T extends string>(
variant: T | undefined,
legacyBordered: boolean | undefined,
variants: readonly (T | DefaultVariant)[],
): [T | DefaultVariant, boolean] => {
let mergedVariant: T | DefaultVariant;
if (typeof variant !== 'undefined') {
mergedVariant = variant;
} else if (legacyBordered === false) {
mergedVariant = 'borderless';
} else {
mergedVariant = 'outlined';
}
const enableVariantCls = variants.includes(mergedVariant);
return [mergedVariant, enableVariantCls];
};
export default useVariant;

View File

@ -5,8 +5,8 @@ import type {
DefaultOptionType,
FieldNames,
MultipleCascaderProps as RcMultipleCascaderProps,
SingleCascaderProps as RcSingleCascaderProps,
ShowSearchType,
SingleCascaderProps as RcSingleCascaderProps,
} from 'rc-cascader';
import RcCascader from 'rc-cascader';
import type { Placement } from 'rc-select/lib/BaseSelect';
@ -36,9 +36,8 @@ import useCheckable from './hooks/useCheckable';
import useColumnIcons from './hooks/useColumnIcons';
import CascaderPanel from './Panel';
import useStyle from './style';
import type { SelectVariant } from '../select';
import useVariant from '../_util/hooks/useVariants';
import { SelectVariants } from '../select';
import type { Variant } from '../form/hooks/useVariants';
import useVariant from '../form/hooks/useVariants';
// Align the design since we use `rc-select` in root. This help:
// - List search content will show all content
@ -146,7 +145,7 @@ export type CascaderProps<DataNodeType extends BaseOptionType = any> =
* @since 5.13.0
* @default "outlined"
*/
variant?: SelectVariant;
variant?: Variant;
};
export interface CascaderRef {
@ -231,7 +230,7 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction);
const [variant, enableVariantCls] = useVariant(customVariant, bordered, SelectVariants);
const [variant, enableVariantCls] = useVariant(customVariant, bordered);
// =================== No Found ====================
const mergedNotFoundContent = notFoundContent || renderEmpty?.('Cascader') || (

View File

@ -29,8 +29,7 @@ import Components from './Components';
import type { CommonPickerMethods, PickerComponentClass } from './interface';
import { useZIndex } from '../../_util/hooks/useZIndex';
import useCSSVarCls from '../../config-provider/hooks/useCSSVarCls';
import useVariant from '../../_util/hooks/useVariants';
import { InputVariants } from '../../input/Input';
import useVariant from '../../form/hooks/useVariants';
export default function generateRangePicker<DateType>(generateConfig: GenerateConfig<DateType>) {
type InternalRangePickerProps = RangePickerProps<DateType> & {};
@ -75,7 +74,7 @@ export default function generateRangePicker<DateType>(generateConfig: GenerateCo
const { format, showTime, picker } = props as any;
const rootPrefixCls = getPrefixCls();
const [variant, enableVariantCls] = useVariant(customVariant, bordered, InputVariants);
const [variant, enableVariantCls] = useVariant(customVariant, bordered);
const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);

View File

@ -30,8 +30,7 @@ import Components from './Components';
import type { CommonPickerMethods, DatePickRef, PickerComponentClass } from './interface';
import { useZIndex } from '../../_util/hooks/useZIndex';
import useCSSVarCls from '../../config-provider/hooks/useCSSVarCls';
import useVariant from '../../_util/hooks/useVariants';
import { InputVariants } from '../../input/Input';
import useVariant from '../../form/hooks/useVariants';
export default function generatePicker<DateType>(generateConfig: GenerateConfig<DateType>) {
type CustomPickerProps = {
@ -83,7 +82,7 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
const innerRef = React.useRef<RCPicker<DateType>>(null);
const { format, showTime } = props as any;
const [variant, enableVariantCls] = useVariant(customVariant, bordered, InputVariants);
const [variant, enableVariantCls] = useVariant(customVariant, bordered);
const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);

View File

@ -15,7 +15,7 @@ import type { SizeType } from '../../config-provider/SizeContext';
import type { TimePickerLocale } from '../../time-picker';
import generateRangePicker from './generateRangePicker';
import generateSinglePicker from './generateSinglePicker';
import type { InputVariant } from '../../input/Input';
import type { Variant } from '../../form/hooks/useVariants';
const DataPickerPlacements = ['bottomLeft', 'bottomRight', 'topLeft', 'topRight'] as const;
type DataPickerPlacement = (typeof DataPickerPlacements)[number];
@ -34,7 +34,7 @@ type InjectDefaultProps<Props> = Omit<
* @since 5.13.0
* @default "outlined"
*/
variant?: InputVariant;
variant?: Variant;
};
export type PickerLocale = {

View File

@ -12,7 +12,7 @@ import SizeContext from '../config-provider/SizeContext';
import useSize from '../config-provider/hooks/useSize';
import type { ColProps } from '../grid/col';
import type { FormContextProps } from './context';
import { FormContext, FormProvider } from './context';
import { FormContext, FormProvider, VariantContext } from './context';
import useForm, { type FormInstance } from './hooks/useForm';
import useFormWarning from './hooks/useFormWarning';
import type { FormLabelAlign } from './interface';
@ -20,6 +20,7 @@ import useStyle from './style';
import ValidateMessagesContext from './validateMessagesContext';
import type { FeedbackIcons } from './FormItem';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import type { Variant } from './hooks/useVariants';
export type RequiredMark =
| boolean
@ -45,6 +46,7 @@ export interface FormProps<Values = any> extends Omit<RcFormProps<Values>, 'form
/** @deprecated Will warning in future branch. Pls use `requiredMark` instead. */
hideRequiredMark?: boolean;
rootClassName?: string;
variant?: Variant;
}
const InternalForm: React.ForwardRefRenderFunction<FormInstance, FormProps> = (props, ref) => {
@ -71,6 +73,7 @@ const InternalForm: React.ForwardRefRenderFunction<FormInstance, FormProps> = (p
name,
style,
feedbackIcons,
variant,
...restFormProps
} = props;
@ -182,28 +185,30 @@ const InternalForm: React.ForwardRefRenderFunction<FormInstance, FormProps> = (p
};
return wrapCSSVar(
<DisabledContextProvider disabled={disabled}>
<SizeContext.Provider value={mergedSize}>
<FormProvider
{...{
// This is not list in API, we pass with spread
validateMessages: contextValidateMessages,
}}
>
<FormContext.Provider value={formContextValue}>
<FieldForm
id={name}
{...restFormProps}
name={name}
onFinishFailed={onInternalFinishFailed}
form={wrapForm}
style={{ ...contextForm?.style, ...style }}
className={formClassName}
/>
</FormContext.Provider>
</FormProvider>
</SizeContext.Provider>
</DisabledContextProvider>,
<VariantContext.Provider value={variant}>
<DisabledContextProvider disabled={disabled}>
<SizeContext.Provider value={mergedSize}>
<FormProvider
{...{
// This is not list in API, we pass with spread
validateMessages: contextValidateMessages,
}}
>
<FormContext.Provider value={formContextValue}>
<FieldForm
id={name}
{...restFormProps}
name={name}
onFinishFailed={onInternalFinishFailed}
form={wrapForm}
style={{ ...contextForm?.style, ...style }}
className={formClassName}
/>
</FormContext.Provider>
</FormProvider>
</SizeContext.Provider>
</DisabledContextProvider>
</VariantContext.Provider>,
);
};

View File

@ -11873,6 +11873,720 @@ exports[`renders components/form/demo/validate-trigger.tsx correctly 1`] = `
</form>
`;
exports[`renders components/form/demo/variant.tsx correctly 1`] = `
<form
class="ant-form ant-form-horizontal"
style="max-width:600px"
>
<div
class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-6"
>
<label
class="ant-form-item-required"
for="Input"
title="Input"
>
Input
</label>
</div>
<div
class="ant-col ant-form-item-control ant-col-xs-24 ant-col-sm-14"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<input
aria-required="true"
class="ant-input ant-input-filled"
id="Input"
type="text"
value=""
/>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-6"
>
<label
class="ant-form-item-required"
for="InputNumber"
title="InputNumber"
>
InputNumber
</label>
</div>
<div
class="ant-col ant-form-item-control ant-col-xs-24 ant-col-sm-14"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<div
class="ant-input-number ant-input-number-in-form-item ant-input-number-filled"
style="width:100%"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up"
role="button"
unselectable="on"
>
<span
aria-label="up"
class="anticon anticon-up ant-input-number-handler-up-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-input-number-handler-down-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
aria-required="true"
autocomplete="off"
class="ant-input-number-input"
id="InputNumber"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-6"
>
<label
class="ant-form-item-required"
for="TextArea"
title="TextArea"
>
TextArea
</label>
</div>
<div
class="ant-col ant-form-item-control ant-col-xs-24 ant-col-sm-14"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<textarea
aria-required="true"
class="ant-input ant-input-filled"
id="TextArea"
/>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-6"
>
<label
class="ant-form-item-required"
for="Mentions"
title="Mentions"
>
Mentions
</label>
</div>
<div
class="ant-col ant-form-item-control ant-col-xs-24 ant-col-sm-14"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<div
class="ant-mentions ant-mentions-filled"
>
<textarea
aria-required="true"
class="rc-textarea"
id="Mentions"
rows="1"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-6"
>
<label
class="ant-form-item-required"
for="Select"
title="Select"
>
Select
</label>
</div>
<div
class="ant-col ant-form-item-control ant-col-xs-24 ant-col-sm-14"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<div
aria-required="true"
class="ant-select ant-select-filled ant-select-in-form-item ant-select-single ant-select-show-arrow"
>
<div
class="ant-select-selector"
>
<span
class="ant-select-selection-search"
>
<input
aria-autocomplete="list"
aria-controls="Select_list"
aria-expanded="false"
aria-haspopup="listbox"
aria-owns="Select_list"
aria-required="true"
autocomplete="off"
class="ant-select-selection-search-input"
id="Select"
readonly=""
role="combobox"
style="opacity:0"
type="search"
unselectable="on"
value=""
/>
</span>
<span
class="ant-select-selection-placeholder"
/>
</div>
<span
aria-hidden="true"
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-select-suffix"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-6"
>
<label
class="ant-form-item-required"
for="Cascader"
title="Cascader"
>
Cascader
</label>
</div>
<div
class="ant-col ant-form-item-control ant-col-xs-24 ant-col-sm-14"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<div
aria-required="true"
class="ant-select ant-cascader ant-select-filled ant-select-in-form-item ant-select-single ant-select-allow-clear ant-select-show-arrow"
>
<div
class="ant-select-selector"
>
<span
class="ant-select-selection-search"
>
<input
aria-autocomplete="list"
aria-controls="Cascader_list"
aria-expanded="false"
aria-haspopup="listbox"
aria-owns="Cascader_list"
aria-required="true"
autocomplete="off"
class="ant-select-selection-search-input"
id="Cascader"
readonly=""
role="combobox"
style="opacity:0"
type="search"
unselectable="on"
value=""
/>
</span>
<span
class="ant-select-selection-placeholder"
/>
</div>
<span
aria-hidden="true"
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-select-suffix"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-6"
>
<label
class="ant-form-item-required"
for="TreeSelect"
title="TreeSelect"
>
TreeSelect
</label>
</div>
<div
class="ant-col ant-form-item-control ant-col-xs-24 ant-col-sm-14"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<div
aria-required="true"
class="ant-select ant-tree-select ant-select-filled ant-select-in-form-item ant-select-single ant-select-show-arrow"
>
<div
class="ant-select-selector"
>
<span
class="ant-select-selection-search"
>
<input
aria-autocomplete="list"
aria-controls="TreeSelect_list"
aria-expanded="false"
aria-haspopup="listbox"
aria-owns="TreeSelect_list"
aria-required="true"
autocomplete="off"
class="ant-select-selection-search-input"
id="TreeSelect"
readonly=""
role="combobox"
style="opacity:0"
type="search"
unselectable="on"
value=""
/>
</span>
<span
class="ant-select-selection-placeholder"
/>
</div>
<span
aria-hidden="true"
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-select-suffix"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-6"
>
<label
class="ant-form-item-required"
for="DatePicker"
title="DatePicker"
>
DatePicker
</label>
</div>
<div
class="ant-col ant-form-item-control ant-col-xs-24 ant-col-sm-14"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<div
class="ant-picker ant-picker-filled"
>
<div
class="ant-picker-input"
>
<input
aria-required="true"
autocomplete="off"
id="DatePicker"
placeholder="Select date"
readonly=""
size="12"
title=""
value=""
/>
<span
class="ant-picker-suffix"
>
<span
aria-label="calendar"
class="anticon anticon-calendar"
role="img"
>
<svg
aria-hidden="true"
data-icon="calendar"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-6"
>
<label
class="ant-form-item-required"
for="RangePicker"
title="RangePicker"
>
RangePicker
</label>
</div>
<div
class="ant-col ant-form-item-control ant-col-xs-24 ant-col-sm-14"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<div
aria-required="true"
class="ant-picker ant-picker-range ant-picker-filled"
>
<div
class="ant-picker-input ant-picker-input-active"
>
<input
autocomplete="off"
id="RangePicker"
placeholder="Start date"
readonly=""
size="12"
value=""
/>
</div>
<div
class="ant-picker-range-separator"
>
<span
aria-label="to"
class="ant-picker-separator"
>
<span
aria-label="swap-right"
class="anticon anticon-swap-right"
role="img"
>
<svg
aria-hidden="true"
data-icon="swap-right"
fill="currentColor"
focusable="false"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M873.1 596.2l-164-208A32 32 0 00684 376h-64.8c-6.7 0-10.4 7.7-6.3 13l144.3 183H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h695.9c26.8 0 41.7-30.8 25.2-51.8z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-picker-input"
>
<input
autocomplete="off"
placeholder="End date"
readonly=""
size="12"
value=""
/>
</div>
<div
class="ant-picker-active-bar"
style="left:0;width:0;position:absolute"
/>
<span
class="ant-picker-suffix"
>
<span
aria-label="calendar"
class="anticon anticon-calendar"
role="img"
>
<svg
aria-hidden="true"
data-icon="calendar"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-col-16 ant-col-offset-6 ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<button
class="ant-btn ant-btn-primary"
type="submit"
>
<span>
Submit
</span>
</button>
</div>
</div>
</div>
</div>
</div>
</form>
`;
exports[`renders components/form/demo/warning-only.tsx correctly 1`] = `
<form
autocomplete="off"

View File

@ -1,6 +1,6 @@
import type { PropsWithChildren, ReactNode } from 'react';
import * as React from 'react';
import { useContext, useMemo } from 'react';
import { createContext, useContext, useMemo } from 'react';
import { FormProvider as RcFormProvider } from 'rc-field-form';
import type { FormProviderProps as RcFormProviderProps } from 'rc-field-form/lib/FormContext';
import type { Meta } from 'rc-field-form/lib/interface';
@ -10,6 +10,7 @@ import type { ColProps } from '../grid/col';
import type { FormInstance, RequiredMark } from './Form';
import type { ValidateStatus, FeedbackIcons } from './FormItem';
import type { FormLabelAlign } from './interface';
import type { Variant } from './hooks/useVariants';
/** Form Context. Set top form style and pass to Form Item usage. */
export interface FormContextProps {
@ -98,3 +99,5 @@ export const NoFormStyle: React.FC<NoFormStyleProps> = ({ children, status, over
</FormItemInputContext.Provider>
);
};
export const VariantContext = createContext<Variant | undefined>(undefined);

View File

@ -0,0 +1,7 @@
## zh-CN
改变表单内所有组件的变体,可选 `outlined` `filled``borderless`
## en-US
Change the variant of all components in the form, optional `outlined` `filled` and `borderless`

View File

@ -0,0 +1,101 @@
import React from 'react';
import {
Button,
Cascader,
DatePicker,
Form,
Input,
InputNumber,
Mentions,
Select,
TreeSelect,
} from 'antd';
const { RangePicker } = DatePicker;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 14 },
},
};
const App: React.FC = () => (
<Form {...formItemLayout} variant="filled" style={{ maxWidth: 600 }}>
<Form.Item label="Input" name="Input" rules={[{ required: true, message: 'Please input!' }]}>
<Input />
</Form.Item>
<Form.Item
label="InputNumber"
name="InputNumber"
rules={[{ required: true, message: 'Please input!' }]}
>
<InputNumber style={{ width: '100%' }} />
</Form.Item>
<Form.Item
label="TextArea"
name="TextArea"
rules={[{ required: true, message: 'Please input!' }]}
>
<Input.TextArea />
</Form.Item>
<Form.Item
label="Mentions"
name="Mentions"
rules={[{ required: true, message: 'Please input!' }]}
>
<Mentions />
</Form.Item>
<Form.Item label="Select" name="Select" rules={[{ required: true, message: 'Please input!' }]}>
<Select />
</Form.Item>
<Form.Item
label="Cascader"
name="Cascader"
rules={[{ required: true, message: 'Please input!' }]}
>
<Cascader />
</Form.Item>
<Form.Item
label="TreeSelect"
name="TreeSelect"
rules={[{ required: true, message: 'Please input!' }]}
>
<TreeSelect />
</Form.Item>
<Form.Item
label="DatePicker"
name="DatePicker"
rules={[{ required: true, message: 'Please input!' }]}
>
<DatePicker />
</Form.Item>
<Form.Item
label="RangePicker"
name="RangePicker"
rules={[{ required: true, message: 'Please input!' }]}
>
<RangePicker />
</Form.Item>
<Form.Item wrapperCol={{ offset: 6, span: 16 }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
export default App;

View File

@ -0,0 +1,29 @@
import { useContext } from 'react';
import { VariantContext } from '../context';
export const Variants = ['outlined', 'borderless', 'filled'] as const;
export type Variant = (typeof Variants)[number];
/**
* Compatible for legacy `bordered` prop.
*/
const useVariant = (
variant: Variant | undefined,
legacyBordered: boolean | undefined = undefined,
): [Variant, boolean] => {
const ctxVariant = useContext(VariantContext);
let mergedVariant: Variant;
if (typeof variant !== 'undefined') {
mergedVariant = variant;
} else if (legacyBordered === false) {
mergedVariant = 'borderless';
} else {
mergedVariant = ctxVariant ?? 'outlined';
}
const enableVariantCls = Variants.includes(mergedVariant);
return [mergedVariant, enableVariantCls];
};
export default useVariant;

View File

@ -20,6 +20,7 @@ High performance Form component with data scope management. Including data colle
<code src="./demo/control-hooks.tsx">Form methods</code>
<code src="./demo/layout.tsx">Form Layout</code>
<code src="./demo/disabled.tsx">Form disabled</code>
<code src="./demo/variant.tsx" version="5.13.0">Form variants</code>
<code src="./demo/required-mark.tsx">Required style</code>
<code src="./demo/size.tsx">Form size</code>
<code src="./demo/layout-can-wrap.tsx">label can wrap</code>
@ -81,6 +82,7 @@ Common props ref[Common props](/docs/react/common-props)
| size | Set field component size (antd components only) | `small` \| `middle` \| `large` | - | |
| validateMessages | Validation prompt template, description [see below](#validatemessages) | [ValidateMessages](https://github.com/ant-design/ant-design/blob/6234509d18bac1ac60fbb3f92a5b2c6a6361295a/components/locale/en_US.ts#L88-L134) | - | |
| validateTrigger | Config field validate trigger | string \| string\[] | `onChange` | 4.3.0 |
| variant | Variant of components inside form | `outlined` \| `borderless` \| `filled` | `outlined` | 5.13.0 |
| wrapperCol | The layout for input controls, same as `labelCol` | [object](/components/grid/#col) | - | |
| onFieldsChange | Trigger when field updated | function(changedFields, allFields) | - | |
| onFinish | Trigger after submitting the form and verifying data successfully | function(values) | - | |

View File

@ -21,6 +21,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*ylFATY6w-ygAAA
<code src="./demo/control-hooks.tsx">表单方法调用</code>
<code src="./demo/layout.tsx">表单布局</code>
<code src="./demo/disabled.tsx">表单禁用</code>
<code src="./demo/variant.tsx" version="5.13.0">表单变体</code>
<code src="./demo/required-mark.tsx">必选样式</code>
<code src="./demo/size.tsx">表单尺寸</code>
<code src="./demo/layout-can-wrap.tsx">表单标签可换行</code>
@ -82,6 +83,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*ylFATY6w-ygAAA
| size | 设置字段组件的尺寸(仅限 antd 组件) | `small` \| `middle` \| `large` | - | |
| validateMessages | 验证提示模板,说明[见下](#validatemessages) | [ValidateMessages](https://github.com/ant-design/ant-design/blob/6234509d18bac1ac60fbb3f92a5b2c6a6361295a/components/locale/en_US.ts#L88-L134) | - | |
| validateTrigger | 统一设置字段触发验证的时机 | string \| string\[] | `onChange` | 4.3.0 |
| variant | 表单内控件变体 | `outlined` \| `borderless` \| `filled` | `outlined` | 5.13.0 |
| wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol | [object](/components/grid-cn#col) | - | |
| onFieldsChange | 字段更新时触发回调事件 | function(changedFields, allFields) | - | |
| onFinish | 提交表单且数据验证成功后回调事件 | function(values) | - | |

View File

@ -14,10 +14,9 @@ import { FormItemInputContext, NoFormStyle } from '../form/context';
import { NoCompactStyle, useCompactItemContext } from '../space/Compact';
import useStyle from './style';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import type { InputVariant } from '../input/Input';
import { InputVariants } from '../input/Input';
import { devUseWarning } from '../_util/warning';
import useVariant from '../_util/hooks/useVariants';
import type { Variant } from '../form/hooks/useVariants';
import useVariant from '../form/hooks/useVariants';
export interface InputNumberProps<T extends ValueType = ValueType>
extends Omit<RcInputNumberProps<T>, 'prefix' | 'size' | 'controls'> {
@ -36,7 +35,7 @@ export interface InputNumberProps<T extends ValueType = ValueType>
* @since 5.13.0
* @default "outlined"
*/
variant?: InputVariant;
variant?: Variant;
}
const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props, ref) => {
@ -108,7 +107,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
const disabled = React.useContext(DisabledContext);
const mergedDisabled = customDisabled ?? disabled;
const [variant, enableVariantCls] = useVariant(customVariant, bordered, InputVariants);
const [variant, enableVariantCls] = useVariant(customVariant, bordered);
// eslint-disable-next-line react/jsx-no-useless-fragment
const suffixNode = hasFeedback && <>{feedbackIcon}</>;

View File

@ -19,7 +19,8 @@ import useRemovePasswordTimeout from './hooks/useRemovePasswordTimeout';
import useStyle from './style';
import { hasPrefixSuffix } from './utils';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import useVariant from '../_util/hooks/useVariants';
import type { Variant } from '../form/hooks/useVariants';
import useVariant from '../form/hooks/useVariants';
export interface InputFocusOptions extends FocusOptions {
cursor?: 'start' | 'end' | 'all';
@ -56,9 +57,6 @@ export function triggerFocus(
}
}
export const InputVariants = ['outlined', 'borderless', 'filled'] as const;
export type InputVariant = (typeof InputVariants)[number];
export interface InputProps
extends Omit<
RcInputProps,
@ -74,7 +72,7 @@ export interface InputProps
* @since 5.13.0
* @default "outlined"
*/
variant?: InputVariant;
variant?: Variant;
[key: `data-${string}`]: string | undefined;
}
@ -183,7 +181,7 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
mergedAllowClear = { clearIcon: <CloseCircleFilled /> };
}
const [variant, enableVariantCls] = useVariant(customVariant, bordered, InputVariants);
const [variant, enableVariantCls] = useVariant(customVariant, bordered);
return wrapCSSVar(
<RcInput

View File

@ -14,11 +14,12 @@ import DisabledContext from '../config-provider/DisabledContext';
import useSize from '../config-provider/hooks/useSize';
import type { SizeType } from '../config-provider/SizeContext';
import { FormItemInputContext } from '../form/context';
import type { InputFocusOptions, InputVariant } from './Input';
import { InputVariants, triggerFocus } from './Input';
import type { InputFocusOptions } from './Input';
import { triggerFocus } from './Input';
import useStyle from './style';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import useVariant from '../_util/hooks/useVariants';
import type { Variant } from '../form/hooks/useVariants';
import useVariant from '../form/hooks/useVariants';
import { devUseWarning } from '../_util/warning';
export interface TextAreaProps extends Omit<RcTextAreaProps, 'suffix'> {
@ -31,7 +32,7 @@ export interface TextAreaProps extends Omit<RcTextAreaProps, 'suffix'> {
* @since 5.13.0
* @default "outlined"
*/
variant?: InputVariant;
variant?: Variant;
}
export interface TextAreaRef {
@ -102,7 +103,7 @@ const TextArea = forwardRef<TextAreaRef, TextAreaProps>((props, ref) => {
const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);
const [variant, enableVariantCls] = useVariant(customVariant, bordered, InputVariants);
const [variant, enableVariantCls] = useVariant(customVariant, bordered);
return wrapCSSVar(
<RcTextArea

View File

@ -19,9 +19,8 @@ import { FormItemInputContext } from '../form/context';
import Spin from '../spin';
import useStyle from './style';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import type { InputVariant } from '../input/Input';
import { InputVariants } from '../input/Input';
import useVariant from '../_util/hooks/useVariants';
import type { Variant } from '../form/hooks/useVariants';
import useVariant from '../form/hooks/useVariants';
export const { Option } = RcMentions;
@ -49,7 +48,7 @@ export interface MentionProps extends Omit<RcMentionsProps, 'suffix'> {
* @since 5.13.0
* @default "outlined"
*/
variant?: InputVariant;
variant?: Variant;
}
export interface MentionsRef extends RcMentionsRef {}
@ -167,7 +166,7 @@ const InternalMentions: React.ForwardRefRenderFunction<MentionsRef, MentionProps
const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);
const [variant, enableVariantCls] = useVariant(customVariant, undefined, InputVariants);
const [variant, enableVariantCls] = useVariant(customVariant);
// eslint-disable-next-line react/jsx-no-useless-fragment
const suffixNode = hasFeedback && <>{feedbackIcon}</>;

View File

@ -134,7 +134,7 @@ const genMentionsStyle: GenerateStyle<MentionsToken> = (token) => {
border: 'none',
outline: 'none',
resize: 'none',
backgroundColor: 'inherit',
backgroundColor: 'transparent',
...genPlaceholderStyle(token.colorTextPlaceholder),
},

View File

@ -26,7 +26,8 @@ import useBuiltinPlacements from './useBuiltinPlacements';
import useIcons from './useIcons';
import useShowArrow from './useShowArrow';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import useVariants from '../_util/hooks/useVariants';
import type { Variant } from '../form/hooks/useVariants';
import useVariants from '../form/hooks/useVariants';
type RawValue = string | number;
@ -40,9 +41,6 @@ export interface LabeledValue {
export type SelectValue = RawValue | RawValue[] | LabeledValue | LabeledValue[] | undefined;
export const SelectVariants = ['outlined', 'borderless', 'filled'] as const;
export type SelectVariant = (typeof SelectVariants)[number];
export interface InternalSelectProps<
ValueType = any,
OptionType extends BaseOptionType | DefaultOptionType = DefaultOptionType,
@ -63,7 +61,7 @@ export interface InternalSelectProps<
* @since 5.13.0
* @default "outlined"
*/
variant?: SelectVariant;
variant?: Variant;
}
export interface SelectProps<
@ -95,7 +93,7 @@ const InternalSelect = <
) => {
const {
prefixCls: customizePrefixCls,
bordered = true,
bordered,
className,
rootClassName,
getPopupContainer,
@ -135,7 +133,7 @@ const InternalSelect = <
const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction);
const [variant, enableVariantCls] = useVariants(customizeVariant, bordered, SelectVariants);
const [variant, enableVariantCls] = useVariants(customizeVariant, bordered);
const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);

View File

@ -30,9 +30,8 @@ import SwitcherIconCom from '../tree/utils/iconUtil';
import useStyle from './style';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import { useZIndex } from '../_util/hooks/useZIndex';
import type { SelectVariant } from '../select';
import { SelectVariants } from '../select';
import useVariant from '../_util/hooks/useVariants';
import type { Variant } from '../form/hooks/useVariants';
import useVariant from '../form/hooks/useVariants';
type RawValue = string | number;
@ -83,7 +82,7 @@ export interface TreeSelectProps<
* @since 5.13.0
* @default "outlined"
*/
variant?: SelectVariant;
variant?: Variant;
}
const InternalTreeSelect = <
@ -169,7 +168,7 @@ const InternalTreeSelect = <
const [wrapCSSVar, hashId, cssVarCls] = useSelectStyle(prefixCls, rootCls);
const [treeSelectWrapCSSVar] = useStyle(treeSelectPrefixCls, treePrefixCls, treeSelectRootCls);
const [variant, enableVariantCls] = useVariant(customVariant, bordered, SelectVariants);
const [variant, enableVariantCls] = useVariant(customVariant, bordered);
const mergedDropdownClassName = classNames(
popupClassName || dropdownClassName,