feat: ConfigProvider support variant (#49535)

* feat: ConfigProvider support variant

* docs: add docs for variant

* feat: support config all components variant

* chore: fix lint

* chore: add test

* test: add test
This commit is contained in:
MadCcc 2024-06-21 17:47:09 +08:00 committed by GitHub
parent ff12f47d18
commit 010f82bd1a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 187 additions and 39 deletions

View File

@ -25,7 +25,7 @@ import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import useSize from '../config-provider/hooks/useSize'; import useSize from '../config-provider/hooks/useSize';
import type { SizeType } from '../config-provider/SizeContext'; import type { SizeType } from '../config-provider/SizeContext';
import { FormItemInputContext } from '../form/context'; import { FormItemInputContext } from '../form/context';
import type { Variant } from '../form/hooks/useVariants'; import type { Variant } from '../config-provider';
import useVariant from '../form/hooks/useVariants'; import useVariant from '../form/hooks/useVariants';
import mergedBuiltinPlacements from '../select/mergedBuiltinPlacements'; import mergedBuiltinPlacements from '../select/mergedBuiltinPlacements';
import useSelectStyle from '../select/style'; import useSelectStyle from '../select/style';
@ -224,7 +224,7 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction); const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction);
const [variant, enableVariantCls] = useVariant(customVariant, bordered); const [variant, enableVariantCls] = useVariant('cascader', customVariant, bordered);
// =================== No Found ==================== // =================== No Found ====================
const mergedNotFoundContent = notFoundContent || renderEmpty?.('Cascader') || ( const mergedNotFoundContent = notFoundContent || renderEmpty?.('Cascader') || (

View File

@ -10,6 +10,7 @@ import Button from '../../button';
import Input from '../../input'; import Input from '../../input';
import Select from '../../select'; import Select from '../../select';
import Table from '../../table'; import Table from '../../table';
import Form from '../../form';
describe('ConfigProvider', () => { describe('ConfigProvider', () => {
mountTest(() => ( mountTest(() => (
@ -148,4 +149,40 @@ describe('ConfigProvider', () => {
errSpy.mockRestore(); errSpy.mockRestore();
warnSpy.mockRestore(); warnSpy.mockRestore();
}); });
it('should support variant', () => {
const { container } = render(
<>
<ConfigProvider variant="filled">
<Input id="variant-input-1" />
</ConfigProvider>
<ConfigProvider variant="filled">
<Input id="variant-input-2" variant="outlined" />
</ConfigProvider>
<ConfigProvider variant="filled">
<Form variant="borderless">
<Input id="variant-input-3" />
</Form>
</ConfigProvider>
<ConfigProvider input={{ variant: 'filled' }}>
<Input id="variant-input-4" />
</ConfigProvider>
<ConfigProvider variant="borderless" input={{ variant: 'filled' }}>
<Input id="variant-input-5" />
</ConfigProvider>
<ConfigProvider variant="borderless" input={{ variant: 'filled' }}>
<Form variant="outlined">
<Input id="variant-input-6" />
</Form>
</ConfigProvider>
</>,
);
expect(container.querySelector('#variant-input-1')).toHaveClass('ant-input-filled');
expect(container.querySelector('#variant-input-2')).toHaveClass('ant-input-outlined');
expect(container.querySelector('#variant-input-3')).toHaveClass('ant-input-borderless');
expect(container.querySelector('#variant-input-4')).toHaveClass('ant-input-filled');
expect(container.querySelector('#variant-input-5')).toHaveClass('ant-input-filled');
expect(container.querySelector('#variant-input-6')).toHaveClass('ant-input-outlined');
});
}); });

View File

@ -57,6 +57,8 @@ import Transfer from '../../transfer';
import Tree from '../../tree'; import Tree from '../../tree';
import Typography from '../../typography'; import Typography from '../../typography';
import Upload from '../../upload'; import Upload from '../../upload';
import InputNumber from '../../input-number';
import TreeSelect from '../../tree-select';
describe('ConfigProvider support style and className props', () => { describe('ConfigProvider support style and className props', () => {
it('Should Space classNames works', () => { it('Should Space classNames works', () => {
@ -1575,4 +1577,43 @@ describe('ConfigProvider support style and className props', () => {
const element = container.querySelector<HTMLSpanElement>('.test-cp-icon'); const element = container.querySelector<HTMLSpanElement>('.test-cp-icon');
expect(element).toBeTruthy(); expect(element).toBeTruthy();
}); });
it('should variant config work', () => {
const { container } = render(
<ConfigProvider
input={{ variant: 'filled' }}
inputNumber={{ variant: 'filled' }}
textArea={{ variant: 'filled' }}
mentions={{ variant: 'borderless' }}
select={{ variant: 'filled' }}
cascader={{ variant: 'outlined' }}
treeSelect={{ variant: 'borderless' }}
datePicker={{ variant: 'filled' }}
rangePicker={{ variant: 'filled' }}
timePicker={{ variant: 'borderless' }}
>
<Input className="input-variant" />
<InputNumber className="input-number-variant" />
<Input.TextArea className="textarea-variant" />
<Mentions className="mentions-variant" />
<Select className="select-variant" />
<Cascader className="cascader-variant" />
<TreeSelect className="tree-select-variant" />
<DatePicker className="date-picker-variant" />
<DatePicker.RangePicker className="range-picker-variant" />
<TimePicker className="time-picker-variant" />
</ConfigProvider>,
);
expect(container.querySelector('.input-variant')).toHaveClass('ant-input-filled');
expect(container.querySelector('.input-number-variant')).toHaveClass('ant-input-number-filled');
expect(container.querySelector('.textarea-variant')).toHaveClass('ant-input-filled');
expect(container.querySelector('.mentions-variant')).toHaveClass('ant-mentions-borderless');
expect(container.querySelector('.select-variant')).toHaveClass('ant-select-filled');
expect(container.querySelector('.cascader-variant')).toHaveClass('ant-select-outlined');
expect(container.querySelector('.tree-select-variant')).toHaveClass('ant-select-borderless');
expect(container.querySelector('.date-picker-variant')).toHaveClass('ant-picker-filled');
expect(container.querySelector('.range-picker-variant')).toHaveClass('ant-picker-filled');
expect(container.querySelector('.time-picker-variant')).toHaveClass('ant-picker-borderless');
});
}); });

View File

@ -26,6 +26,12 @@ import type { TagProps } from '../tag';
import type { AliasToken, MappingAlgorithm, OverrideToken } from '../theme/interface'; import type { AliasToken, MappingAlgorithm, OverrideToken } from '../theme/interface';
import type { TourProps } from '../tour/interface'; import type { TourProps } from '../tour/interface';
import type { TransferProps } from '../transfer'; import type { TransferProps } from '../transfer';
import type { InputNumberProps } from '../input-number';
import type { TreeSelectProps } from '../tree-select';
import type { DatePickerProps, RangePickerProps } from '../date-picker';
import type { TimePickerProps } from '../time-picker';
import type { CascaderProps } from '../cascader';
import type { MentionsProps } from '../mentions';
import type { RenderEmptyHandler } from './defaultRenderEmpty'; import type { RenderEmptyHandler } from './defaultRenderEmpty';
export const defaultIconPrefixCls = 'anticon'; export const defaultIconPrefixCls = 'anticon';
@ -136,10 +142,10 @@ export type AlertConfig = ComponentStyleConfig & Pick<AlertProps, 'closable' | '
export type BadgeConfig = ComponentStyleConfig & Pick<BadgeProps, 'classNames' | 'styles'>; export type BadgeConfig = ComponentStyleConfig & Pick<BadgeProps, 'classNames' | 'styles'>;
export type InputConfig = ComponentStyleConfig & export type InputConfig = ComponentStyleConfig &
Pick<InputProps, 'autoComplete' | 'classNames' | 'styles' | 'allowClear'>; Pick<InputProps, 'autoComplete' | 'classNames' | 'styles' | 'allowClear' | 'variant'>;
export type TextAreaConfig = ComponentStyleConfig & export type TextAreaConfig = ComponentStyleConfig &
Pick<TextAreaProps, 'autoComplete' | 'classNames' | 'styles' | 'allowClear'>; Pick<TextAreaProps, 'autoComplete' | 'classNames' | 'styles' | 'allowClear' | 'variant'>;
export type ButtonConfig = ComponentStyleConfig & export type ButtonConfig = ComponentStyleConfig &
Pick<ButtonProps, 'classNames' | 'styles' | 'autoInsertSpace'>; Pick<ButtonProps, 'classNames' | 'styles' | 'autoInsertSpace'>;
@ -158,22 +164,39 @@ export type FlexConfig = ComponentStyleConfig & Pick<FlexProps, 'vertical'>;
export type TransferConfig = ComponentStyleConfig & Pick<TransferProps, 'selectionsIcon'>; export type TransferConfig = ComponentStyleConfig & Pick<TransferProps, 'selectionsIcon'>;
export type FormConfig = ComponentStyleConfig & export type FormConfig = ComponentStyleConfig &
Pick<FormProps, 'requiredMark' | 'colon' | 'scrollToFirstError' | 'validateMessages'>; Pick<FormProps, 'requiredMark' | 'colon' | 'scrollToFirstError' | 'validateMessages' | 'variant'>;
export type FloatButtonGroupConfig = Pick<FloatButtonGroupProps, 'closeIcon'>; export type FloatButtonGroupConfig = Pick<FloatButtonGroupProps, 'closeIcon'>;
export type PaginationConfig = ComponentStyleConfig & Pick<PaginationProps, 'showSizeChanger'>; export type PaginationConfig = ComponentStyleConfig & Pick<PaginationProps, 'showSizeChanger'>;
export type SelectConfig = ComponentStyleConfig & Pick<SelectProps, 'showSearch'>; export type SelectConfig = ComponentStyleConfig & Pick<SelectProps, 'showSearch' | 'variant'>;
export type SpaceConfig = ComponentStyleConfig & Pick<SpaceProps, 'size' | 'classNames' | 'styles'>; export type SpaceConfig = ComponentStyleConfig & Pick<SpaceProps, 'size' | 'classNames' | 'styles'>;
export type InputNumberConfig = ComponentStyleConfig & Pick<InputNumberProps, 'variant'>;
export type CascaderConfig = ComponentStyleConfig & Pick<CascaderProps, 'variant'>;
export type TreeSelectConfig = ComponentStyleConfig & Pick<TreeSelectProps, 'variant'>;
export type DatePickerConfig = ComponentStyleConfig & Pick<DatePickerProps, 'variant'>;
export type RangePickerConfig = ComponentStyleConfig & Pick<RangePickerProps, 'variant'>;
export type TimePickerConfig = ComponentStyleConfig & Pick<TimePickerProps, 'variant'>;
export type MentionsConfig = ComponentStyleConfig & Pick<MentionsProps, 'variant'>;
export type PopupOverflow = 'viewport' | 'scroll'; export type PopupOverflow = 'viewport' | 'scroll';
export interface ListConfig extends ComponentStyleConfig { export interface ListConfig extends ComponentStyleConfig {
item?: Pick<ListItemProps, 'classNames' | 'styles'>; item?: Pick<ListItemProps, 'classNames' | 'styles'>;
} }
export const Variants = ['outlined', 'borderless', 'filled'] as const;
export type Variant = (typeof Variants)[number];
export interface WaveConfig { export interface WaveConfig {
/** /**
* @descCN `false` * @descCN `false`
@ -202,8 +225,10 @@ export interface ConfigConsumerProps {
csp?: CSPConfig; csp?: CSPConfig;
/** @deprecated Please use `{ button: { autoInsertSpace: boolean }}` instead */ /** @deprecated Please use `{ button: { autoInsertSpace: boolean }}` instead */
autoInsertSpaceInButton?: boolean; autoInsertSpaceInButton?: boolean;
variant?: Variant;
input?: InputConfig; input?: InputConfig;
textArea?: TextAreaConfig; textArea?: TextAreaConfig;
inputNumber?: InputNumberConfig;
pagination?: PaginationConfig; pagination?: PaginationConfig;
locale?: Locale; locale?: Locale;
direction?: DirectionType; direction?: DirectionType;
@ -221,7 +246,8 @@ export interface ConfigConsumerProps {
drawer?: DrawerConfig; drawer?: DrawerConfig;
calendar?: ComponentStyleConfig; calendar?: ComponentStyleConfig;
carousel?: ComponentStyleConfig; carousel?: ComponentStyleConfig;
cascader?: ComponentStyleConfig; cascader?: CascaderConfig;
treeSelect?: TreeSelectConfig;
collapse?: CollapseConfig; collapse?: CollapseConfig;
floatButtonGroup?: FloatButtonGroupConfig; floatButtonGroup?: FloatButtonGroupConfig;
typography?: ComponentStyleConfig; typography?: ComponentStyleConfig;
@ -233,7 +259,7 @@ export interface ConfigConsumerProps {
image?: ImageConfig; image?: ImageConfig;
layout?: ComponentStyleConfig; layout?: ComponentStyleConfig;
list?: ListConfig; list?: ListConfig;
mentions?: ComponentStyleConfig; mentions?: MentionsConfig;
modal?: ModalConfig; modal?: ModalConfig;
progress?: ComponentStyleConfig; progress?: ComponentStyleConfig;
result?: ComponentStyleConfig; result?: ComponentStyleConfig;
@ -255,14 +281,14 @@ export interface ConfigConsumerProps {
card?: CardConfig; card?: CardConfig;
tabs?: TabsConfig; tabs?: TabsConfig;
timeline?: ComponentStyleConfig; timeline?: ComponentStyleConfig;
timePicker?: ComponentStyleConfig; timePicker?: TimePickerConfig;
tour?: TourConfig; tour?: TourConfig;
upload?: ComponentStyleConfig; upload?: ComponentStyleConfig;
notification?: NotificationConfig; notification?: NotificationConfig;
tree?: ComponentStyleConfig; tree?: ComponentStyleConfig;
colorPicker?: ComponentStyleConfig; colorPicker?: ComponentStyleConfig;
datePicker?: ComponentStyleConfig; datePicker?: DatePickerConfig;
rangePicker?: ComponentStyleConfig; rangePicker?: RangePickerConfig;
dropdown?: ComponentStyleConfig; dropdown?: ComponentStyleConfig;
flex?: FlexConfig; flex?: FlexConfig;
wave?: WaveConfig; wave?: WaveConfig;

View File

@ -65,6 +65,7 @@ Some components use dynamic style to support wave effect. You can config `csp` p
| prefixCls | Set prefix className | string | `ant` | | | prefixCls | Set prefix className | string | `ant` | |
| renderEmpty | Set empty content of components. Ref [Empty](/components/empty/) | function(componentName: string): ReactNode | - | | | renderEmpty | Set empty content of components. Ref [Empty](/components/empty/) | function(componentName: string): ReactNode | - | |
| theme | Set theme, ref [Customize Theme](/docs/react/customize-theme) | [Theme](/docs/react/customize-theme#theme) | - | 5.0.0 | | theme | Set theme, ref [Customize Theme](/docs/react/customize-theme) | [Theme](/docs/react/customize-theme#theme) | - | 5.0.0 |
| variant | Set variant of data entry components | `outlined` \| `filled` \| `borderless` | - | 5.19.0 |
| virtual | Disable virtual scroll when set to `false` | boolean | - | 4.3.0 | | virtual | Disable virtual scroll when set to `false` | boolean | - | 4.3.0 |
| warning | Config warning level, when `strict` is `false`, it will aggregate deprecated information into a single message | { strict: boolean } | - | 5.10.0 | | warning | Config warning level, when `strict` is `false`, it will aggregate deprecated information into a single message | { strict: boolean } | - | 5.10.0 |

View File

@ -47,8 +47,16 @@ import type {
TourConfig, TourConfig,
TransferConfig, TransferConfig,
WaveConfig, WaveConfig,
Variant,
InputNumberConfig,
RangePickerConfig,
DatePickerConfig,
TimePickerConfig,
CascaderConfig,
TreeSelectConfig,
MentionsConfig,
} from './context'; } from './context';
import { ConfigConsumer, ConfigContext, defaultIconPrefixCls } from './context'; import { ConfigConsumer, ConfigContext, defaultIconPrefixCls, Variants } from './context';
import { registerTheme } from './cssVariables'; import { registerTheme } from './cssVariables';
import type { RenderEmptyHandler } from './defaultRenderEmpty'; import type { RenderEmptyHandler } from './defaultRenderEmpty';
import { DisabledContextProvider } from './DisabledContext'; import { DisabledContextProvider } from './DisabledContext';
@ -60,6 +68,10 @@ import type { SizeType } from './SizeContext';
import SizeContext, { SizeContextProvider } from './SizeContext'; import SizeContext, { SizeContextProvider } from './SizeContext';
import useStyle from './style'; import useStyle from './style';
export type { Variant };
export { Variants };
/** /**
* Since too many feedback using static method like `Modal.confirm` not getting theme, we record the * Since too many feedback using static method like `Modal.confirm` not getting theme, we record the
* theme register info here to help developer get warning info. * theme register info here to help developer get warning info.
@ -125,8 +137,10 @@ export interface ConfigProviderProps {
csp?: CSPConfig; csp?: CSPConfig;
/** @deprecated Please use `{ button: { autoInsertSpace: boolean }}` instead */ /** @deprecated Please use `{ button: { autoInsertSpace: boolean }}` instead */
autoInsertSpaceInButton?: boolean; autoInsertSpaceInButton?: boolean;
variant?: Variant;
form?: FormConfig; form?: FormConfig;
input?: InputConfig; input?: InputConfig;
inputNumber?: InputNumberConfig;
textArea?: TextAreaConfig; textArea?: TextAreaConfig;
select?: SelectConfig; select?: SelectConfig;
pagination?: PaginationConfig; pagination?: PaginationConfig;
@ -161,7 +175,8 @@ export interface ConfigProviderProps {
button?: ButtonConfig; button?: ButtonConfig;
calendar?: ComponentStyleConfig; calendar?: ComponentStyleConfig;
carousel?: ComponentStyleConfig; carousel?: ComponentStyleConfig;
cascader?: ComponentStyleConfig; cascader?: CascaderConfig;
treeSelect?: TreeSelectConfig;
collapse?: CollapseConfig; collapse?: CollapseConfig;
divider?: ComponentStyleConfig; divider?: ComponentStyleConfig;
drawer?: DrawerConfig; drawer?: DrawerConfig;
@ -174,7 +189,7 @@ export interface ConfigProviderProps {
image?: ImageConfig; image?: ImageConfig;
layout?: ComponentStyleConfig; layout?: ComponentStyleConfig;
list?: ListConfig; list?: ListConfig;
mentions?: ComponentStyleConfig; mentions?: MentionsConfig;
modal?: ModalConfig; modal?: ModalConfig;
progress?: ComponentStyleConfig; progress?: ComponentStyleConfig;
result?: ComponentStyleConfig; result?: ComponentStyleConfig;
@ -197,13 +212,13 @@ export interface ConfigProviderProps {
card?: CardConfig; card?: CardConfig;
tabs?: TabsConfig; tabs?: TabsConfig;
timeline?: ComponentStyleConfig; timeline?: ComponentStyleConfig;
timePicker?: ComponentStyleConfig; timePicker?: TimePickerConfig;
upload?: ComponentStyleConfig; upload?: ComponentStyleConfig;
notification?: NotificationConfig; notification?: NotificationConfig;
tree?: ComponentStyleConfig; tree?: ComponentStyleConfig;
colorPicker?: ComponentStyleConfig; colorPicker?: ComponentStyleConfig;
datePicker?: ComponentStyleConfig; datePicker?: DatePickerConfig;
rangePicker?: ComponentStyleConfig; rangePicker?: RangePickerConfig;
dropdown?: ComponentStyleConfig; dropdown?: ComponentStyleConfig;
flex?: FlexConfig; flex?: FlexConfig;
/** /**
@ -367,6 +382,9 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
warning: warningConfig, warning: warningConfig,
tour, tour,
floatButtonGroup, floatButtonGroup,
variant,
inputNumber,
treeSelect,
} = props; } = props;
// =================================== Context =================================== // =================================== Context ===================================
@ -463,6 +481,9 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
warning: warningConfig, warning: warningConfig,
tour, tour,
floatButtonGroup, floatButtonGroup,
variant,
inputNumber,
treeSelect,
}; };
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {

View File

@ -66,6 +66,7 @@ export default Demo;
| prefixCls | 设置统一样式前缀 | string | `ant` | | | prefixCls | 设置统一样式前缀 | string | `ant` | |
| renderEmpty | 自定义组件空状态。参考 [空状态](/components/empty-cn) | function(componentName: string): ReactNode | - | | | renderEmpty | 自定义组件空状态。参考 [空状态](/components/empty-cn) | function(componentName: string): ReactNode | - | |
| theme | 设置主题,参考 [定制主题](/docs/react/customize-theme-cn) | [Theme](/docs/react/customize-theme-cn#theme) | - | 5.0.0 | | theme | 设置主题,参考 [定制主题](/docs/react/customize-theme-cn) | [Theme](/docs/react/customize-theme-cn#theme) | - | 5.0.0 |
| variant | 设置全局输入组件形态变体 | `outlined` \| `filled` \| `borderless` | - | 5.19.0 |
| virtual | 设置 `false` 时关闭虚拟滚动 | boolean | - | 4.3.0 | | virtual | 设置 `false` 时关闭虚拟滚动 | boolean | - | 4.3.0 |
| warning | 设置警告等级,`strict` 为 `false` 时会将废弃相关信息聚合为单条信息 | { strict: boolean } | - | 5.10.0 | | warning | 设置警告等级,`strict` 为 `false` 时会将废弃相关信息聚合为单条信息 | { strict: boolean } | - | 5.10.0 |

View File

@ -58,7 +58,7 @@ export default function generateRangePicker<DateType extends AnyObject>(
const { picker } = props; const { picker } = props;
const rootPrefixCls = getPrefixCls(); const rootPrefixCls = getPrefixCls();
const [variant, enableVariantCls] = useVariant(customVariant, bordered); const [variant, enableVariantCls] = useVariant('rangePicker', customVariant, bordered);
const rootCls = useCSSVarCls(prefixCls); const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls); const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);

View File

@ -70,7 +70,7 @@ export default function generatePicker<DateType extends AnyObject>(
const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction); const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction);
const innerRef = React.useRef<PickerRef>(null); const innerRef = React.useRef<PickerRef>(null);
const [variant, enableVariantCls] = useVariant(customVariant, bordered); const [variant, enableVariantCls] = useVariant('datePicker', customVariant, bordered);
const rootCls = useCSSVarCls(prefixCls); const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls); const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);

View File

@ -8,7 +8,7 @@ import type { Locale as RcPickerLocale } from 'rc-picker/lib/interface';
import type { InputStatus } from '../../_util/statusUtils'; import type { InputStatus } from '../../_util/statusUtils';
import type { AnyObject } from '../../_util/type'; import type { AnyObject } from '../../_util/type';
import type { SizeType } from '../../config-provider/SizeContext'; import type { SizeType } from '../../config-provider/SizeContext';
import type { Variant } from '../../form/hooks/useVariants'; import type { Variant } from '../../config-provider';
import type { TimePickerLocale } from '../../time-picker'; import type { TimePickerLocale } from '../../time-picker';
const DataPickerPlacements = ['bottomLeft', 'bottomRight', 'topLeft', 'topRight'] as const; const DataPickerPlacements = ['bottomLeft', 'bottomRight', 'topLeft', 'topRight'] as const;

View File

@ -19,7 +19,7 @@ import type { FeedbackIcons } from './FormItem';
import useForm from './hooks/useForm'; import useForm from './hooks/useForm';
import type { FormInstance } from './hooks/useForm'; import type { FormInstance } from './hooks/useForm';
import useFormWarning from './hooks/useFormWarning'; import useFormWarning from './hooks/useFormWarning';
import type { Variant } from './hooks/useVariants'; import type { Variant } from '../config-provider';
import type { FormLabelAlign } from './interface'; import type { FormLabelAlign } from './interface';
import useStyle from './style'; import useStyle from './style';
import ValidateMessagesContext from './validateMessagesContext'; import ValidateMessagesContext from './validateMessagesContext';

View File

@ -9,7 +9,7 @@ import omit from 'rc-util/lib/omit';
import type { ColProps } from '../grid/col'; import type { ColProps } from '../grid/col';
import type { FormInstance, RequiredMark } from './Form'; import type { FormInstance, RequiredMark } from './Form';
import type { FeedbackIcons, ValidateStatus } from './FormItem'; import type { FeedbackIcons, ValidateStatus } from './FormItem';
import type { Variant } from './hooks/useVariants'; import type { Variant } from '../config-provider';
import type { FormLabelAlign } from './interface'; import type { FormLabelAlign } from './interface';
/** Form Context. Set top form style and pass to Form Item usage. */ /** Form Context. Set top form style and pass to Form Item usage. */

View File

@ -1,18 +1,34 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { VariantContext } from '../context'; import { VariantContext } from '../context';
import type { Variant, ConfigProviderProps } from '../../config-provider';
import { ConfigContext, Variants } from '../../config-provider';
export const Variants = ['outlined', 'borderless', 'filled'] as const; type VariantComponents = keyof Pick<
export type Variant = (typeof Variants)[number]; ConfigProviderProps,
| 'input'
| 'inputNumber'
| 'textArea'
| 'mentions'
| 'select'
| 'cascader'
| 'treeSelect'
| 'datePicker'
| 'timePicker'
| 'rangePicker'
>;
/** /**
* Compatible for legacy `bordered` prop. * Compatible for legacy `bordered` prop.
*/ */
const useVariant = ( const useVariant = (
component: VariantComponents,
variant: Variant | undefined, variant: Variant | undefined,
legacyBordered: boolean | undefined = undefined, legacyBordered: boolean | undefined = undefined,
): [Variant, boolean] => { ): [Variant, boolean] => {
const { variant: configVariant, [component]: componentConfig } = useContext(ConfigContext);
const ctxVariant = useContext(VariantContext); const ctxVariant = useContext(VariantContext);
const configComponentVariant = componentConfig?.variant;
let mergedVariant: Variant; let mergedVariant: Variant;
if (typeof variant !== 'undefined') { if (typeof variant !== 'undefined') {
@ -20,7 +36,8 @@ const useVariant = (
} else if (legacyBordered === false) { } else if (legacyBordered === false) {
mergedVariant = 'borderless'; mergedVariant = 'borderless';
} else { } else {
mergedVariant = ctxVariant ?? 'outlined'; // form variant > component global variant > global variant
mergedVariant = ctxVariant ?? configComponentVariant ?? configVariant ?? 'outlined';
} }
const enableVariantCls = Variants.includes(mergedVariant); const enableVariantCls = Variants.includes(mergedVariant);

View File

@ -15,7 +15,7 @@ import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import useSize from '../config-provider/hooks/useSize'; import useSize from '../config-provider/hooks/useSize';
import type { SizeType } from '../config-provider/SizeContext'; import type { SizeType } from '../config-provider/SizeContext';
import { FormItemInputContext } from '../form/context'; import { FormItemInputContext } from '../form/context';
import type { Variant } from '../form/hooks/useVariants'; import type { Variant } from '../config-provider';
import useVariant from '../form/hooks/useVariants'; import useVariant from '../form/hooks/useVariants';
import { useCompactItemContext } from '../space/Compact'; import { useCompactItemContext } from '../space/Compact';
import useStyle from './style'; import useStyle from './style';
@ -109,7 +109,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
const disabled = React.useContext(DisabledContext); const disabled = React.useContext(DisabledContext);
const mergedDisabled = customDisabled ?? disabled; const mergedDisabled = customDisabled ?? disabled;
const [variant, enableVariantCls] = useVariant(customVariant, bordered); const [variant, enableVariantCls] = useVariant('inputNumber', customVariant, bordered);
// eslint-disable-next-line react/jsx-no-useless-fragment // eslint-disable-next-line react/jsx-no-useless-fragment
const suffixNode = hasFeedback && <>{feedbackIcon}</>; const suffixNode = hasFeedback && <>{feedbackIcon}</>;

View File

@ -15,7 +15,7 @@ import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import useSize from '../config-provider/hooks/useSize'; import useSize from '../config-provider/hooks/useSize';
import type { SizeType } from '../config-provider/SizeContext'; import type { SizeType } from '../config-provider/SizeContext';
import { FormItemInputContext } from '../form/context'; import { FormItemInputContext } from '../form/context';
import type { Variant } from '../form/hooks/useVariants'; import type { Variant } from '../config-provider';
import useVariant from '../form/hooks/useVariants'; import useVariant from '../form/hooks/useVariants';
import { useCompactItemContext } from '../space/Compact'; import { useCompactItemContext } from '../space/Compact';
import useRemovePasswordTimeout from './hooks/useRemovePasswordTimeout'; import useRemovePasswordTimeout from './hooks/useRemovePasswordTimeout';
@ -174,7 +174,7 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
const mergedAllowClear = getAllowClear(allowClear ?? input?.allowClear); const mergedAllowClear = getAllowClear(allowClear ?? input?.allowClear);
const [variant, enableVariantCls] = useVariant(customVariant, bordered); const [variant, enableVariantCls] = useVariant('input', customVariant, bordered);
return wrapCSSVar( return wrapCSSVar(
<RcInput <RcInput

View File

@ -12,7 +12,7 @@ import useSize from '../../config-provider/hooks/useSize';
import type { SizeType } from '../../config-provider/SizeContext'; import type { SizeType } from '../../config-provider/SizeContext';
import { FormItemInputContext } from '../../form/context'; import { FormItemInputContext } from '../../form/context';
import type { FormItemStatusContextProps } from '../../form/context'; import type { FormItemStatusContextProps } from '../../form/context';
import type { Variant } from '../../form/hooks/useVariants'; import type { Variant } from '../../config-provider';
import type { InputRef } from '../Input'; import type { InputRef } from '../Input';
import useStyle from '../style/otp'; import useStyle from '../style/otp';
import OTPInput from './OTPInput'; import OTPInput from './OTPInput';

View File

@ -15,7 +15,7 @@ import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import useSize from '../config-provider/hooks/useSize'; import useSize from '../config-provider/hooks/useSize';
import type { SizeType } from '../config-provider/SizeContext'; import type { SizeType } from '../config-provider/SizeContext';
import { FormItemInputContext } from '../form/context'; import { FormItemInputContext } from '../form/context';
import type { Variant } from '../form/hooks/useVariants'; import type { Variant } from '../config-provider';
import useVariant from '../form/hooks/useVariants'; import useVariant from '../form/hooks/useVariants';
import type { InputFocusOptions } from './Input'; import type { InputFocusOptions } from './Input';
import { triggerFocus } from './Input'; import { triggerFocus } from './Input';
@ -96,7 +96,7 @@ const TextArea = forwardRef<TextAreaRef, TextAreaProps>((props, ref) => {
const rootCls = useCSSVarCls(prefixCls); const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls); const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);
const [variant, enableVariantCls] = useVariant(customVariant, bordered); const [variant, enableVariantCls] = useVariant('textArea', customVariant, bordered);
const mergedAllowClear = getAllowClear(allowClear ?? textArea?.allowClear); const mergedAllowClear = getAllowClear(allowClear ?? textArea?.allowClear);

View File

@ -18,7 +18,7 @@ import { ConfigContext } from '../config-provider';
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty'; import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls'; import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import { FormItemInputContext } from '../form/context'; import { FormItemInputContext } from '../form/context';
import type { Variant } from '../form/hooks/useVariants'; import type { Variant } from '../config-provider';
import useVariant from '../form/hooks/useVariants'; import useVariant from '../form/hooks/useVariants';
import Spin from '../spin'; import Spin from '../spin';
import useStyle from './style'; import useStyle from './style';
@ -161,7 +161,7 @@ const InternalMentions = React.forwardRef<MentionsRef, MentionProps>((props, ref
const rootCls = useCSSVarCls(prefixCls); const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls); const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);
const [variant, enableVariantCls] = useVariant(customVariant); const [variant, enableVariantCls] = useVariant('mentions', customVariant);
// eslint-disable-next-line react/jsx-no-useless-fragment // eslint-disable-next-line react/jsx-no-useless-fragment
const suffixNode = hasFeedback && <>{feedbackIcon}</>; const suffixNode = hasFeedback && <>{feedbackIcon}</>;

View File

@ -21,7 +21,7 @@ import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import useSize from '../config-provider/hooks/useSize'; import useSize from '../config-provider/hooks/useSize';
import type { SizeType } from '../config-provider/SizeContext'; import type { SizeType } from '../config-provider/SizeContext';
import { FormItemInputContext } from '../form/context'; import { FormItemInputContext } from '../form/context';
import type { Variant } from '../form/hooks/useVariants'; import type { Variant } from '../config-provider';
import useVariants from '../form/hooks/useVariants'; import useVariants from '../form/hooks/useVariants';
import { useCompactItemContext } from '../space/Compact'; import { useCompactItemContext } from '../space/Compact';
import { useToken } from '../theme/internal'; import { useToken } from '../theme/internal';
@ -142,7 +142,7 @@ const InternalSelect = <
const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction); const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction);
const [variant, enableVariantCls] = useVariants(customizeVariant, bordered); const [variant, enableVariantCls] = useVariants('select', customizeVariant, bordered);
const rootCls = useCSSVarCls(prefixCls); const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls); const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);

View File

@ -11,6 +11,7 @@ import type {
PickerPropsWithMultiple, PickerPropsWithMultiple,
RangePickerProps, RangePickerProps,
} from '../date-picker/generatePicker/interface'; } from '../date-picker/generatePicker/interface';
import useVariant from '../form/hooks/useVariants';
export type PickerTimeProps<DateType extends AnyObject> = PickerPropsWithMultiple< export type PickerTimeProps<DateType extends AnyObject> = PickerPropsWithMultiple<
DateType, DateType,
@ -45,13 +46,15 @@ export interface TimePickerProps extends Omit<PickerTimeProps<Dayjs>, 'picker'>
} }
const TimePicker = React.forwardRef<any, TimePickerProps>( const TimePicker = React.forwardRef<any, TimePickerProps>(
({ addon, renderExtraFooter, ...restProps }, ref) => { ({ addon, renderExtraFooter, variant, bordered, ...restProps }, ref) => {
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning('TimePicker'); const warning = devUseWarning('TimePicker');
warning.deprecated(!addon, 'addon', 'renderExtraFooter'); warning.deprecated(!addon, 'addon', 'renderExtraFooter');
} }
const [mergedVariant] = useVariant('timePicker', variant, bordered);
const internalRenderExtraFooter = React.useMemo(() => { const internalRenderExtraFooter = React.useMemo(() => {
if (renderExtraFooter) { if (renderExtraFooter) {
return renderExtraFooter; return renderExtraFooter;
@ -69,6 +72,7 @@ const TimePicker = React.forwardRef<any, TimePickerProps>(
mode={undefined} mode={undefined}
ref={ref} ref={ref}
renderExtraFooter={internalRenderExtraFooter} renderExtraFooter={internalRenderExtraFooter}
variant={mergedVariant}
/> />
); );
}, },

View File

@ -21,7 +21,7 @@ import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import useSize from '../config-provider/hooks/useSize'; import useSize from '../config-provider/hooks/useSize';
import type { SizeType } from '../config-provider/SizeContext'; import type { SizeType } from '../config-provider/SizeContext';
import { FormItemInputContext } from '../form/context'; import { FormItemInputContext } from '../form/context';
import type { Variant } from '../form/hooks/useVariants'; import type { Variant } from '../config-provider';
import useVariant from '../form/hooks/useVariants'; import useVariant from '../form/hooks/useVariants';
import mergedBuiltinPlacements from '../select/mergedBuiltinPlacements'; import mergedBuiltinPlacements from '../select/mergedBuiltinPlacements';
import useSelectStyle from '../select/style'; import useSelectStyle from '../select/style';
@ -170,7 +170,7 @@ const InternalTreeSelect = <
const [wrapCSSVar, hashId, cssVarCls] = useSelectStyle(prefixCls, rootCls); const [wrapCSSVar, hashId, cssVarCls] = useSelectStyle(prefixCls, rootCls);
const [treeSelectWrapCSSVar] = useStyle(treeSelectPrefixCls, treePrefixCls, treeSelectRootCls); const [treeSelectWrapCSSVar] = useStyle(treeSelectPrefixCls, treePrefixCls, treeSelectRootCls);
const [variant, enableVariantCls] = useVariant(customVariant, bordered); const [variant, enableVariantCls] = useVariant('treeSelect', customVariant, bordered);
const mergedDropdownClassName = classNames( const mergedDropdownClassName = classNames(
popupClassName || dropdownClassName, popupClassName || dropdownClassName,