1
This commit is contained in:
+7
@@ -0,0 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import type { RadioGroupContextProps } from './interface';
|
||||
declare const RadioGroupContext: React.Context<RadioGroupContextProps | undefined>;
|
||||
export declare const RadioGroupContextProvider: React.Provider<RadioGroupContextProps | undefined>;
|
||||
export default RadioGroupContext;
|
||||
export declare const RadioOptionTypeContext: React.Context<import("./interface").RadioGroupOptionType | undefined>;
|
||||
export declare const RadioOptionTypeContextProvider: React.Provider<import("./interface").RadioGroupOptionType | undefined>;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import * as React from 'react';
|
||||
const RadioGroupContext = /*#__PURE__*/React.createContext(undefined);
|
||||
export const RadioGroupContextProvider = RadioGroupContext.Provider;
|
||||
export default RadioGroupContext;
|
||||
export const RadioOptionTypeContext = /*#__PURE__*/React.createContext(undefined);
|
||||
export const RadioOptionTypeContextProvider = RadioOptionTypeContext.Provider;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import type { RadioGroupProps } from './interface';
|
||||
declare const _default: React.NamedExoticComponent<RadioGroupProps & React.RefAttributes<HTMLDivElement>>;
|
||||
export default _default;
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
"use client";
|
||||
|
||||
import * as React from 'react';
|
||||
import { useControlledState } from '@rc-component/util';
|
||||
import useId from "@rc-component/util/es/hooks/useId";
|
||||
import pickAttrs from "@rc-component/util/es/pickAttrs";
|
||||
import { clsx } from 'clsx';
|
||||
import { useOrientation } from '../_util/hooks';
|
||||
import { isNumber } from '../_util/is';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
|
||||
import useSize from '../config-provider/hooks/useSize';
|
||||
import { FormItemInputContext } from '../form/context';
|
||||
import { toNamePathStr } from '../form/hooks/useForm';
|
||||
import { RadioGroupContextProvider } from './context';
|
||||
import Radio from './radio';
|
||||
import useStyle from './style';
|
||||
const RadioGroup = /*#__PURE__*/React.forwardRef((props, ref) => {
|
||||
const {
|
||||
getPrefixCls,
|
||||
direction
|
||||
} = React.useContext(ConfigContext);
|
||||
const {
|
||||
name: formItemName
|
||||
} = React.useContext(FormItemInputContext);
|
||||
const defaultName = useId(toNamePathStr(formItemName));
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className,
|
||||
rootClassName,
|
||||
options,
|
||||
buttonStyle = 'outline',
|
||||
disabled,
|
||||
children,
|
||||
size: customizeSize,
|
||||
style,
|
||||
id,
|
||||
optionType,
|
||||
name = defaultName,
|
||||
defaultValue,
|
||||
value: customizedValue,
|
||||
block = false,
|
||||
onChange,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
onFocus,
|
||||
onBlur,
|
||||
orientation,
|
||||
vertical,
|
||||
role = 'radiogroup'
|
||||
} = props;
|
||||
const [value, setValue] = useControlledState(defaultValue, customizedValue);
|
||||
const onRadioChange = React.useCallback(event => {
|
||||
const lastValue = value;
|
||||
const val = event.target.value;
|
||||
if (!('value' in props)) {
|
||||
setValue(val);
|
||||
}
|
||||
if (val !== lastValue) {
|
||||
onChange?.(event);
|
||||
}
|
||||
}, [value, setValue, onChange]);
|
||||
const prefixCls = getPrefixCls('radio', customizePrefixCls);
|
||||
const groupPrefixCls = `${prefixCls}-group`;
|
||||
// Style
|
||||
const rootCls = useCSSVarCls(prefixCls);
|
||||
const [hashId, cssVarCls] = useStyle(prefixCls, rootCls);
|
||||
let childrenToRender = children;
|
||||
// 如果存在 options, 优先使用
|
||||
if (options && options.length > 0) {
|
||||
childrenToRender = options.map(option => {
|
||||
if (typeof option === 'string' || isNumber(option)) {
|
||||
// 此处类型自动推导为 string
|
||||
return /*#__PURE__*/React.createElement(Radio, {
|
||||
key: option.toString(),
|
||||
prefixCls: prefixCls,
|
||||
disabled: disabled,
|
||||
value: option,
|
||||
checked: value === option
|
||||
}, option);
|
||||
}
|
||||
// 此处类型自动推导为 { label: string value: string }
|
||||
return /*#__PURE__*/React.createElement(Radio, {
|
||||
key: `radio-group-value-options-${option.value}`,
|
||||
prefixCls: prefixCls,
|
||||
disabled: option.disabled || disabled,
|
||||
value: option.value,
|
||||
checked: value === option.value,
|
||||
title: option.title,
|
||||
style: option.style,
|
||||
className: option.className,
|
||||
id: option.id,
|
||||
required: option.required
|
||||
}, option.label);
|
||||
});
|
||||
}
|
||||
const mergedSize = useSize(customizeSize);
|
||||
const [, mergedVertical] = useOrientation(orientation, vertical);
|
||||
const classString = clsx(groupPrefixCls, `${groupPrefixCls}-${buttonStyle}`, {
|
||||
[`${groupPrefixCls}-large`]: mergedSize === 'large',
|
||||
[`${groupPrefixCls}-small`]: mergedSize === 'small',
|
||||
[`${groupPrefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${groupPrefixCls}-block`]: block
|
||||
}, className, rootClassName, hashId, cssVarCls, rootCls);
|
||||
const memoizedValue = React.useMemo(() => ({
|
||||
onChange: onRadioChange,
|
||||
value,
|
||||
disabled,
|
||||
name,
|
||||
optionType,
|
||||
block
|
||||
}), [onRadioChange, value, disabled, name, optionType, block]);
|
||||
return /*#__PURE__*/React.createElement("div", {
|
||||
...pickAttrs(props, {
|
||||
aria: true,
|
||||
data: true
|
||||
}),
|
||||
role: role,
|
||||
className: clsx(classString, {
|
||||
[`${prefixCls}-group-vertical`]: mergedVertical
|
||||
}),
|
||||
style: style,
|
||||
onMouseEnter: onMouseEnter,
|
||||
onMouseLeave: onMouseLeave,
|
||||
onFocus: onFocus,
|
||||
onBlur: onBlur,
|
||||
id: id,
|
||||
ref: ref
|
||||
}, /*#__PURE__*/React.createElement(RadioGroupContextProvider, {
|
||||
value: memoizedValue
|
||||
}, childrenToRender));
|
||||
});
|
||||
export default /*#__PURE__*/React.memo(RadioGroup);
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import Group from './group';
|
||||
import InternalRadio from './radio';
|
||||
import Button from './radioButton';
|
||||
export type { RadioChangeEvent, RadioChangeEventTarget, RadioGroupButtonStyle, RadioGroupContextProps, RadioGroupOptionType, RadioGroupProps, RadioProps, RadioRef, RadioSemanticClassNames, RadioSemanticName, RadioSemanticStyles, } from './interface';
|
||||
export { Button, Group };
|
||||
type CompoundedComponent = typeof InternalRadio & {
|
||||
Group: typeof Group;
|
||||
Button: typeof Button;
|
||||
};
|
||||
declare const Radio: CompoundedComponent;
|
||||
export default Radio;
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import Group from './group';
|
||||
import InternalRadio from './radio';
|
||||
import Button from './radioButton';
|
||||
export { Button, Group };
|
||||
const Radio = InternalRadio;
|
||||
Radio.Button = Button;
|
||||
Radio.Group = Group;
|
||||
Radio.__ANT_RADIO = true;
|
||||
export default Radio;
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
import type * as React from 'react';
|
||||
import type { Orientation, SemanticClassNamesType, SemanticStylesType } from '../_util/hooks';
|
||||
import type { AbstractCheckboxProps } from '../checkbox/Checkbox';
|
||||
import type { AbstractCheckboxGroupProps } from '../checkbox/Group';
|
||||
import type { SizeType } from '../config-provider/SizeContext';
|
||||
export type { CheckboxRef as RadioRef } from '@rc-component/checkbox';
|
||||
export type RadioGroupButtonStyle = 'outline' | 'solid';
|
||||
export type RadioGroupOptionType = 'default' | 'button';
|
||||
export interface RadioGroupProps extends AbstractCheckboxGroupProps {
|
||||
defaultValue?: any;
|
||||
value?: any;
|
||||
onChange?: (e: RadioChangeEvent) => void;
|
||||
size?: SizeType;
|
||||
disabled?: boolean;
|
||||
onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;
|
||||
name?: string;
|
||||
children?: React.ReactNode;
|
||||
id?: string;
|
||||
optionType?: RadioGroupOptionType;
|
||||
orientation?: Orientation;
|
||||
buttonStyle?: RadioGroupButtonStyle;
|
||||
onFocus?: React.FocusEventHandler<HTMLDivElement>;
|
||||
onBlur?: React.FocusEventHandler<HTMLDivElement>;
|
||||
block?: boolean;
|
||||
vertical?: boolean;
|
||||
}
|
||||
export interface RadioGroupContextProps {
|
||||
onChange: (e: RadioChangeEvent) => void;
|
||||
value: any;
|
||||
disabled?: boolean;
|
||||
name?: string;
|
||||
block?: boolean;
|
||||
}
|
||||
export type RadioSemanticName = keyof RadioSemanticClassNames & keyof RadioSemanticStyles;
|
||||
export type RadioSemanticClassNames = {
|
||||
root?: string;
|
||||
icon?: string;
|
||||
label?: string;
|
||||
};
|
||||
export type RadioSemanticStyles = {
|
||||
root?: React.CSSProperties;
|
||||
icon?: React.CSSProperties;
|
||||
label?: React.CSSProperties;
|
||||
};
|
||||
export type RadioClassNamesType = SemanticClassNamesType<RadioProps, RadioSemanticClassNames>;
|
||||
export type RadioStylesType = SemanticStylesType<RadioProps, RadioSemanticStyles>;
|
||||
export interface RadioProps extends AbstractCheckboxProps<RadioChangeEvent> {
|
||||
classNames?: RadioClassNamesType;
|
||||
styles?: RadioStylesType;
|
||||
}
|
||||
export interface RadioChangeEventTarget extends RadioProps {
|
||||
checked: boolean;
|
||||
}
|
||||
export interface RadioChangeEvent {
|
||||
target: RadioChangeEventTarget;
|
||||
stopPropagation: () => void;
|
||||
preventDefault: () => void;
|
||||
nativeEvent: MouseEvent;
|
||||
}
|
||||
export type RadioOptionTypeContextProps = RadioGroupOptionType;
|
||||
+1
@@ -0,0 +1 @@
|
||||
export {};
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import type { RadioProps, RadioRef } from './interface';
|
||||
declare const Radio: React.ForwardRefExoticComponent<RadioProps & React.RefAttributes<RadioRef>>;
|
||||
export default Radio;
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
"use client";
|
||||
|
||||
import * as React from 'react';
|
||||
import RcCheckbox from '@rc-component/checkbox';
|
||||
import { composeRef } from "@rc-component/util/es/ref";
|
||||
import { clsx } from 'clsx';
|
||||
import { useMergeSemantic } from '../_util/hooks';
|
||||
import { devUseWarning } from '../_util/warning';
|
||||
import Wave from '../_util/wave';
|
||||
import { TARGET_CLS } from '../_util/wave/interface';
|
||||
import useBubbleLock from '../checkbox/useBubbleLock';
|
||||
import { useComponentConfig } from '../config-provider/context';
|
||||
import DisabledContext from '../config-provider/DisabledContext';
|
||||
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
|
||||
import { FormItemInputContext } from '../form/context';
|
||||
import RadioGroupContext, { RadioOptionTypeContext } from './context';
|
||||
import useStyle from './style';
|
||||
const InternalRadio = (props, ref) => {
|
||||
const groupContext = React.useContext(RadioGroupContext);
|
||||
const radioOptionTypeContext = React.useContext(RadioOptionTypeContext);
|
||||
const {
|
||||
getPrefixCls,
|
||||
direction,
|
||||
className: contextClassName,
|
||||
style: contextStyle,
|
||||
classNames: contextClassNames,
|
||||
styles: contextStyles
|
||||
} = useComponentConfig('radio');
|
||||
const innerRef = React.useRef(null);
|
||||
const mergedRef = composeRef(ref, innerRef);
|
||||
const {
|
||||
isFormItemInput
|
||||
} = React.useContext(FormItemInputContext);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const warning = devUseWarning('Radio');
|
||||
process.env.NODE_ENV !== "production" ? warning(!('optionType' in props), 'usage', '`optionType` is only support in Radio.Group.') : void 0;
|
||||
}
|
||||
const onChange = e => {
|
||||
props.onChange?.(e);
|
||||
groupContext?.onChange?.(e);
|
||||
};
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className,
|
||||
rootClassName,
|
||||
children,
|
||||
style,
|
||||
title,
|
||||
classNames,
|
||||
styles,
|
||||
...restProps
|
||||
} = props;
|
||||
const radioPrefixCls = getPrefixCls('radio', customizePrefixCls);
|
||||
const isButtonType = (groupContext?.optionType || radioOptionTypeContext) === 'button';
|
||||
const prefixCls = isButtonType ? `${radioPrefixCls}-button` : radioPrefixCls;
|
||||
// Style
|
||||
const rootCls = useCSSVarCls(radioPrefixCls);
|
||||
const [hashId, cssVarCls] = useStyle(radioPrefixCls, rootCls);
|
||||
const radioProps = {
|
||||
...restProps
|
||||
};
|
||||
// ===================== Disabled =====================
|
||||
const disabled = React.useContext(DisabledContext);
|
||||
// ====================== Checked ======================
|
||||
let mergedChecked = radioProps.checked;
|
||||
if (groupContext) {
|
||||
radioProps.name = groupContext.name;
|
||||
radioProps.onChange = onChange;
|
||||
mergedChecked = props.value === groupContext.value;
|
||||
radioProps.disabled = radioProps.disabled ?? groupContext.disabled;
|
||||
}
|
||||
radioProps.disabled = radioProps.disabled ?? disabled;
|
||||
// =========== Merged Props for Semantic ===========
|
||||
const mergedProps = {
|
||||
...props,
|
||||
...radioProps,
|
||||
checked: mergedChecked
|
||||
};
|
||||
const [mergedClassNames, mergedStyles] = useMergeSemantic([contextClassNames, classNames], [contextStyles, styles], {
|
||||
props: mergedProps
|
||||
});
|
||||
const wrapperClassString = clsx(`${prefixCls}-wrapper`, {
|
||||
[`${prefixCls}-wrapper-checked`]: mergedChecked,
|
||||
[`${prefixCls}-wrapper-disabled`]: radioProps.disabled,
|
||||
[`${prefixCls}-wrapper-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-wrapper-in-form-item`]: isFormItemInput,
|
||||
[`${prefixCls}-wrapper-block`]: !!groupContext?.block
|
||||
}, contextClassName, className, rootClassName, mergedClassNames.root, hashId, cssVarCls, rootCls);
|
||||
// ============================ Event Lock ============================
|
||||
const [onLabelClick, onInputClick] = useBubbleLock(radioProps.onClick);
|
||||
// ============================== Render ==============================
|
||||
return /*#__PURE__*/React.createElement(Wave, {
|
||||
component: "Radio",
|
||||
disabled: radioProps.disabled
|
||||
}, /*#__PURE__*/React.createElement("label", {
|
||||
className: wrapperClassString,
|
||||
style: {
|
||||
...mergedStyles.root,
|
||||
...contextStyle,
|
||||
...style
|
||||
},
|
||||
onMouseEnter: props.onMouseEnter,
|
||||
onMouseLeave: props.onMouseLeave,
|
||||
title: title,
|
||||
onClick: onLabelClick
|
||||
}, /*#__PURE__*/React.createElement(RcCheckbox, {
|
||||
...radioProps,
|
||||
checked: mergedChecked,
|
||||
className: clsx(mergedClassNames.icon, {
|
||||
[TARGET_CLS]: !isButtonType
|
||||
}),
|
||||
style: mergedStyles.icon,
|
||||
type: "radio",
|
||||
prefixCls: prefixCls,
|
||||
ref: mergedRef,
|
||||
onClick: onInputClick
|
||||
}), children !== undefined ? (/*#__PURE__*/React.createElement("span", {
|
||||
className: clsx(`${prefixCls}-label`, mergedClassNames.label),
|
||||
style: mergedStyles.label
|
||||
}, children)) : null));
|
||||
};
|
||||
const Radio = /*#__PURE__*/React.forwardRef(InternalRadio);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Radio.displayName = 'Radio';
|
||||
}
|
||||
export default Radio;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import type { AbstractCheckboxProps } from '../checkbox/Checkbox';
|
||||
import type { RadioChangeEvent, RadioRef } from './interface';
|
||||
export type RadioButtonProps = AbstractCheckboxProps<RadioChangeEvent>;
|
||||
declare const _default: React.ForwardRefExoticComponent<RadioButtonProps & React.RefAttributes<RadioRef>>;
|
||||
export default _default;
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
"use client";
|
||||
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { RadioOptionTypeContextProvider } from './context';
|
||||
import Radio from './radio';
|
||||
const RadioButton = (props, ref) => {
|
||||
const {
|
||||
getPrefixCls
|
||||
} = React.useContext(ConfigContext);
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
...radioProps
|
||||
} = props;
|
||||
const prefixCls = getPrefixCls('radio', customizePrefixCls);
|
||||
return /*#__PURE__*/React.createElement(RadioOptionTypeContextProvider, {
|
||||
value: "button"
|
||||
}, /*#__PURE__*/React.createElement(Radio, {
|
||||
prefixCls: prefixCls,
|
||||
...radioProps,
|
||||
type: "radio",
|
||||
ref: ref
|
||||
}));
|
||||
};
|
||||
export default /*#__PURE__*/React.forwardRef(RadioButton);
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
import type { GetDefaultToken } from '../../theme/internal';
|
||||
export interface ComponentToken {
|
||||
/**
|
||||
* @desc 单选框大小
|
||||
* @descEN Radio size
|
||||
*/
|
||||
radioSize: number;
|
||||
/**
|
||||
* @desc 单选框圆点大小
|
||||
* @descEN Size of Radio dot
|
||||
*/
|
||||
dotSize: number;
|
||||
/**
|
||||
* @desc 单选框圆点禁用颜色
|
||||
* @descEN Color of disabled Radio dot
|
||||
*/
|
||||
dotColorDisabled: string;
|
||||
/**
|
||||
* @desc 单选框按钮背景色
|
||||
* @descEN Background color of Radio button
|
||||
*/
|
||||
buttonBg: string;
|
||||
/**
|
||||
* @desc 单选框按钮选中背景色
|
||||
* @descEN Background color of checked Radio button
|
||||
*/
|
||||
buttonCheckedBg: string;
|
||||
/**
|
||||
* @desc 单选框按钮文本颜色
|
||||
* @descEN Color of Radio button text
|
||||
*/
|
||||
buttonColor: string;
|
||||
/**
|
||||
* @desc 单选框按钮横向内间距
|
||||
* @descEN Horizontal padding of Radio button
|
||||
*/
|
||||
buttonPaddingInline: number;
|
||||
/**
|
||||
* @desc 单选框按钮选中并禁用时的背景色
|
||||
* @descEN Background color of checked and disabled Radio button
|
||||
*/
|
||||
buttonCheckedBgDisabled: string;
|
||||
/**
|
||||
* @desc 单选框按钮选中并禁用时的文本颜色
|
||||
* @descEN Color of checked and disabled Radio button text
|
||||
*/
|
||||
buttonCheckedColorDisabled: string;
|
||||
/**
|
||||
* @desc 单选框实色按钮选中时的文本颜色
|
||||
* @descEN Color of checked solid Radio button text
|
||||
*/
|
||||
buttonSolidCheckedColor: string;
|
||||
/**
|
||||
* @desc 单选框实色按钮选中时的背景色
|
||||
* @descEN Background color of checked solid Radio button text
|
||||
*/
|
||||
buttonSolidCheckedBg: string;
|
||||
/**
|
||||
* @desc 单选框实色按钮选中时的悬浮态背景色
|
||||
* @descEN Background color of checked solid Radio button text when hover
|
||||
*/
|
||||
buttonSolidCheckedHoverBg: string;
|
||||
/**
|
||||
* @desc 单选框实色按钮选中时的激活态背景色
|
||||
* @descEN Background color of checked solid Radio button text when active
|
||||
*/
|
||||
buttonSolidCheckedActiveBg: string;
|
||||
/**
|
||||
* @desc 单选框右间距
|
||||
* @descEN Margin right of Radio button
|
||||
*/
|
||||
wrapperMarginInlineEnd: number;
|
||||
}
|
||||
export declare const prepareComponentToken: GetDefaultToken<'Radio'>;
|
||||
declare const _default: (prefixCls: string, rootCls?: string) => readonly [string, string];
|
||||
export default _default;
|
||||
+423
@@ -0,0 +1,423 @@
|
||||
import { unit } from '@ant-design/cssinjs';
|
||||
import { genFocusOutline, resetComponent } from '../../style';
|
||||
import { genStyleHooks, mergeToken } from '../../theme/internal';
|
||||
// ============================== Styles ==============================
|
||||
// styles from RadioGroup only
|
||||
const getGroupRadioStyle = token => {
|
||||
const {
|
||||
componentCls,
|
||||
antCls
|
||||
} = token;
|
||||
const groupPrefixCls = `${componentCls}-group`;
|
||||
return {
|
||||
[groupPrefixCls]: {
|
||||
...resetComponent(token),
|
||||
display: 'inline-block',
|
||||
fontSize: 0,
|
||||
// RTL
|
||||
[`&${groupPrefixCls}-rtl`]: {
|
||||
direction: 'rtl'
|
||||
},
|
||||
[`&${groupPrefixCls}-block`]: {
|
||||
display: 'flex'
|
||||
},
|
||||
[`${antCls}-badge ${antCls}-badge-count`]: {
|
||||
zIndex: 1
|
||||
},
|
||||
[`> ${antCls}-badge:not(:first-child) > ${antCls}-button-wrapper`]: {
|
||||
borderInlineStart: 'none'
|
||||
},
|
||||
'&-vertical': {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
rowGap: token.marginXS,
|
||||
[`${componentCls}-wrapper`]: {
|
||||
marginInlineEnd: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
// Styles from radio-wrapper
|
||||
const getRadioBasicStyle = token => {
|
||||
const {
|
||||
componentCls,
|
||||
wrapperMarginInlineEnd,
|
||||
colorPrimary,
|
||||
colorPrimaryHover,
|
||||
radioSize,
|
||||
motionDurationSlow,
|
||||
motionDurationMid,
|
||||
motionEaseInOutCirc,
|
||||
colorBgContainer,
|
||||
colorBorder,
|
||||
lineWidth,
|
||||
colorBgContainerDisabled,
|
||||
colorTextDisabled,
|
||||
paddingXS,
|
||||
dotColorDisabled,
|
||||
dotSize,
|
||||
lineType,
|
||||
radioColor,
|
||||
radioBgColor
|
||||
} = token;
|
||||
return {
|
||||
[`${componentCls}-wrapper`]: {
|
||||
...resetComponent(token),
|
||||
display: 'inline-flex',
|
||||
alignItems: 'baseline',
|
||||
marginInlineStart: 0,
|
||||
marginInlineEnd: wrapperMarginInlineEnd,
|
||||
cursor: 'pointer',
|
||||
'&:last-child': {
|
||||
marginInlineEnd: 0
|
||||
},
|
||||
// RTL
|
||||
[`&${componentCls}-wrapper-rtl`]: {
|
||||
direction: 'rtl'
|
||||
},
|
||||
'&-disabled': {
|
||||
cursor: 'not-allowed',
|
||||
color: token.colorTextDisabled
|
||||
},
|
||||
'&::after': {
|
||||
display: 'inline-block',
|
||||
width: 0,
|
||||
overflow: 'hidden',
|
||||
content: '"\\a0"'
|
||||
},
|
||||
'&-block': {
|
||||
flex: 1,
|
||||
justifyContent: 'center'
|
||||
},
|
||||
// ===================== Radio =====================
|
||||
[componentCls]: {
|
||||
...resetComponent(token),
|
||||
position: 'relative',
|
||||
whiteSpace: 'nowrap',
|
||||
lineHeight: 1,
|
||||
cursor: 'pointer',
|
||||
alignSelf: 'center',
|
||||
// Styles moved from inner
|
||||
boxSizing: 'border-box',
|
||||
display: 'block',
|
||||
width: `calc(${radioSize} * 1px)`,
|
||||
height: `calc(${radioSize} * 1px)`,
|
||||
backgroundColor: colorBgContainer,
|
||||
border: `${unit(lineWidth)} ${lineType} ${colorBorder}`,
|
||||
borderRadius: '50%',
|
||||
transition: `all ${motionDurationMid}`,
|
||||
flex: 'none',
|
||||
// Dot
|
||||
'&:after': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%) scale(0)',
|
||||
width: `calc(${dotSize} * 1px)`,
|
||||
height: `calc(${dotSize} * 1px)`,
|
||||
backgroundColor: radioColor,
|
||||
borderRadius: '50%',
|
||||
transformOrigin: '50% 50%',
|
||||
opacity: 0,
|
||||
transition: `all ${motionDurationSlow} ${motionEaseInOutCirc}`
|
||||
},
|
||||
// Wrapper > Radio > input
|
||||
[`${componentCls}-input`]: {
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
zIndex: 1,
|
||||
cursor: 'pointer',
|
||||
opacity: 0,
|
||||
margin: 0
|
||||
},
|
||||
// Focus outline on radio when input is focus-visible
|
||||
[`&:has(${componentCls}-input:focus-visible)`]: genFocusOutline(token)
|
||||
},
|
||||
// ===================== Hover =====================
|
||||
[`&:hover:not(${componentCls}-wrapper-disabled) ${componentCls}`]: {
|
||||
borderColor: colorPrimary
|
||||
},
|
||||
[`&:hover ${componentCls}-checked:not(${componentCls}-disabled)`]: {
|
||||
backgroundColor: colorPrimaryHover,
|
||||
borderColor: 'transparent'
|
||||
},
|
||||
// ==================== Checked ====================
|
||||
[`${componentCls}-checked`]: {
|
||||
backgroundColor: radioBgColor,
|
||||
borderColor: colorPrimary,
|
||||
'&::after': {
|
||||
transform: `translate(-50%, -50%)`,
|
||||
opacity: 1
|
||||
}
|
||||
},
|
||||
// ==================== Disable ====================
|
||||
[`${componentCls}-disabled`]: {
|
||||
// Wrapper > Radio > input
|
||||
[`&, ${componentCls}-input`]: {
|
||||
cursor: 'not-allowed',
|
||||
// Disabled for native input to enable Tooltip event handler
|
||||
pointerEvents: 'none'
|
||||
},
|
||||
// Disabled radio styles
|
||||
background: colorBgContainerDisabled,
|
||||
borderColor: colorBorder,
|
||||
'&::after': {
|
||||
backgroundColor: dotColorDisabled
|
||||
}
|
||||
},
|
||||
[`${componentCls}-disabled + span`]: {
|
||||
color: colorTextDisabled,
|
||||
cursor: 'not-allowed'
|
||||
},
|
||||
[`span${componentCls} + *`]: {
|
||||
paddingInlineStart: paddingXS,
|
||||
paddingInlineEnd: paddingXS
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
// Styles from radio-button
|
||||
const getRadioButtonStyle = token => {
|
||||
const {
|
||||
buttonColor,
|
||||
controlHeight,
|
||||
componentCls,
|
||||
lineWidth,
|
||||
lineType,
|
||||
colorBorder,
|
||||
motionDurationMid,
|
||||
buttonPaddingInline,
|
||||
fontSize,
|
||||
buttonBg,
|
||||
fontSizeLG,
|
||||
controlHeightLG,
|
||||
controlHeightSM,
|
||||
paddingXS,
|
||||
borderRadius,
|
||||
borderRadiusSM,
|
||||
borderRadiusLG,
|
||||
buttonCheckedBg,
|
||||
buttonSolidCheckedColor,
|
||||
colorTextDisabled,
|
||||
colorBgContainerDisabled,
|
||||
buttonCheckedBgDisabled,
|
||||
buttonCheckedColorDisabled,
|
||||
colorPrimary,
|
||||
colorPrimaryHover,
|
||||
colorPrimaryActive,
|
||||
buttonSolidCheckedBg,
|
||||
buttonSolidCheckedHoverBg,
|
||||
buttonSolidCheckedActiveBg,
|
||||
calc
|
||||
} = token;
|
||||
return {
|
||||
[`${componentCls}-button-wrapper`]: {
|
||||
position: 'relative',
|
||||
display: 'inline-block',
|
||||
height: controlHeight,
|
||||
margin: 0,
|
||||
paddingInline: buttonPaddingInline,
|
||||
paddingBlock: 0,
|
||||
color: buttonColor,
|
||||
fontSize,
|
||||
lineHeight: unit(calc(controlHeight).sub(calc(lineWidth).mul(2)).equal()),
|
||||
background: buttonBg,
|
||||
border: `${unit(lineWidth)} ${lineType} ${colorBorder}`,
|
||||
// strange align fix for chrome but works
|
||||
// https://gw.alipayobjects.com/zos/rmsportal/VFTfKXJuogBAXcvfAUWJ.gif
|
||||
borderBlockStartWidth: calc(lineWidth).add(0.02).equal(),
|
||||
borderInlineEndWidth: lineWidth,
|
||||
cursor: 'pointer',
|
||||
transition: [`color`, `background-color`, `box-shadow`].map(prop => `${prop} ${motionDurationMid}`).join(','),
|
||||
a: {
|
||||
color: buttonColor
|
||||
},
|
||||
[`> ${componentCls}-button`]: {
|
||||
position: 'absolute',
|
||||
insetBlockStart: 0,
|
||||
insetInlineStart: 0,
|
||||
zIndex: -1,
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
},
|
||||
'&:not(:last-child)': {
|
||||
marginInlineEnd: calc(lineWidth).mul(-1).equal()
|
||||
},
|
||||
'&:first-child': {
|
||||
borderInlineStart: `${unit(lineWidth)} ${lineType} ${colorBorder}`,
|
||||
borderStartStartRadius: borderRadius,
|
||||
borderEndStartRadius: borderRadius
|
||||
},
|
||||
'&:last-child': {
|
||||
borderStartEndRadius: borderRadius,
|
||||
borderEndEndRadius: borderRadius
|
||||
},
|
||||
'&:first-child:last-child': {
|
||||
borderRadius
|
||||
},
|
||||
[`${componentCls}-group-large &`]: {
|
||||
height: controlHeightLG,
|
||||
fontSize: fontSizeLG,
|
||||
lineHeight: unit(calc(controlHeightLG).sub(calc(lineWidth).mul(2)).equal()),
|
||||
'&:first-child': {
|
||||
borderStartStartRadius: borderRadiusLG,
|
||||
borderEndStartRadius: borderRadiusLG
|
||||
},
|
||||
'&:last-child': {
|
||||
borderStartEndRadius: borderRadiusLG,
|
||||
borderEndEndRadius: borderRadiusLG
|
||||
}
|
||||
},
|
||||
[`${componentCls}-group-small &`]: {
|
||||
height: controlHeightSM,
|
||||
paddingInline: calc(paddingXS).sub(lineWidth).equal(),
|
||||
paddingBlock: 0,
|
||||
lineHeight: unit(calc(controlHeightSM).sub(calc(lineWidth).mul(2)).equal()),
|
||||
'&:first-child': {
|
||||
borderStartStartRadius: borderRadiusSM,
|
||||
borderEndStartRadius: borderRadiusSM
|
||||
},
|
||||
'&:last-child': {
|
||||
borderStartEndRadius: borderRadiusSM,
|
||||
borderEndEndRadius: borderRadiusSM
|
||||
}
|
||||
},
|
||||
'&:hover': {
|
||||
position: 'relative',
|
||||
color: colorPrimary
|
||||
},
|
||||
'&:has(:focus-visible)': genFocusOutline(token),
|
||||
[`${componentCls}, input[type='checkbox'], input[type='radio']`]: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
opacity: 0,
|
||||
pointerEvents: 'none'
|
||||
},
|
||||
[`&-checked:not(${componentCls}-button-wrapper-disabled)`]: {
|
||||
zIndex: 1,
|
||||
color: colorPrimary,
|
||||
background: buttonCheckedBg,
|
||||
borderColor: colorPrimary,
|
||||
'&::before': {
|
||||
backgroundColor: colorPrimary
|
||||
},
|
||||
'&:first-child': {
|
||||
borderColor: colorPrimary
|
||||
},
|
||||
'&:hover': {
|
||||
color: colorPrimaryHover,
|
||||
borderColor: colorPrimaryHover,
|
||||
'&::before': {
|
||||
backgroundColor: colorPrimaryHover
|
||||
}
|
||||
},
|
||||
'&:active': {
|
||||
color: colorPrimaryActive,
|
||||
borderColor: colorPrimaryActive,
|
||||
'&::before': {
|
||||
backgroundColor: colorPrimaryActive
|
||||
}
|
||||
}
|
||||
},
|
||||
[`${componentCls}-group-solid &-checked:not(${componentCls}-button-wrapper-disabled)`]: {
|
||||
color: buttonSolidCheckedColor,
|
||||
background: buttonSolidCheckedBg,
|
||||
borderColor: buttonSolidCheckedBg,
|
||||
'&:hover': {
|
||||
color: buttonSolidCheckedColor,
|
||||
background: buttonSolidCheckedHoverBg,
|
||||
borderColor: buttonSolidCheckedHoverBg
|
||||
},
|
||||
'&:active': {
|
||||
color: buttonSolidCheckedColor,
|
||||
background: buttonSolidCheckedActiveBg,
|
||||
borderColor: buttonSolidCheckedActiveBg
|
||||
}
|
||||
},
|
||||
'&-disabled': {
|
||||
color: colorTextDisabled,
|
||||
backgroundColor: colorBgContainerDisabled,
|
||||
borderColor: colorBorder,
|
||||
cursor: 'not-allowed',
|
||||
'&:first-child, &:hover': {
|
||||
color: colorTextDisabled,
|
||||
backgroundColor: colorBgContainerDisabled,
|
||||
borderColor: colorBorder
|
||||
}
|
||||
},
|
||||
[`&-disabled${componentCls}-button-wrapper-checked`]: {
|
||||
color: buttonCheckedColorDisabled,
|
||||
backgroundColor: buttonCheckedBgDisabled,
|
||||
borderColor: colorBorder,
|
||||
boxShadow: 'none'
|
||||
},
|
||||
'&-block': {
|
||||
flex: 1,
|
||||
textAlign: 'center'
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
// ============================== Export ==============================
|
||||
export const prepareComponentToken = token => {
|
||||
const {
|
||||
wireframe,
|
||||
padding,
|
||||
marginXS,
|
||||
lineWidth,
|
||||
fontSizeLG,
|
||||
colorText,
|
||||
colorBgContainer,
|
||||
colorTextDisabled,
|
||||
controlItemBgActiveDisabled,
|
||||
colorTextLightSolid,
|
||||
colorPrimary,
|
||||
colorPrimaryHover,
|
||||
colorPrimaryActive,
|
||||
colorWhite
|
||||
} = token;
|
||||
const dotPadding = 4; // Fixed value
|
||||
const radioSize = fontSizeLG;
|
||||
const radioDotSize = wireframe ? radioSize - dotPadding * 2 : radioSize - (dotPadding + lineWidth) * 2;
|
||||
return {
|
||||
// Radio
|
||||
radioSize,
|
||||
dotSize: radioDotSize,
|
||||
dotColorDisabled: colorTextDisabled,
|
||||
// Radio buttons
|
||||
buttonSolidCheckedColor: colorTextLightSolid,
|
||||
buttonSolidCheckedBg: colorPrimary,
|
||||
buttonSolidCheckedHoverBg: colorPrimaryHover,
|
||||
buttonSolidCheckedActiveBg: colorPrimaryActive,
|
||||
buttonBg: colorBgContainer,
|
||||
buttonCheckedBg: colorBgContainer,
|
||||
buttonColor: colorText,
|
||||
buttonCheckedBgDisabled: controlItemBgActiveDisabled,
|
||||
buttonCheckedColorDisabled: colorTextDisabled,
|
||||
buttonPaddingInline: padding - lineWidth,
|
||||
wrapperMarginInlineEnd: marginXS,
|
||||
// internal
|
||||
radioColor: wireframe ? colorPrimary : colorWhite,
|
||||
radioBgColor: wireframe ? colorBgContainer : colorPrimary
|
||||
};
|
||||
};
|
||||
export default genStyleHooks('Radio', token => {
|
||||
const {
|
||||
controlOutline,
|
||||
controlOutlineWidth
|
||||
} = token;
|
||||
const radioFocusShadow = `0 0 0 ${unit(controlOutlineWidth)} ${controlOutline}`;
|
||||
const radioButtonFocusShadow = radioFocusShadow;
|
||||
const radioToken = mergeToken(token, {
|
||||
radioFocusShadow,
|
||||
radioButtonFocusShadow
|
||||
});
|
||||
return [getGroupRadioStyle(radioToken), getRadioBasicStyle(radioToken), getRadioButtonStyle(radioToken)];
|
||||
}, prepareComponentToken, {
|
||||
unitless: {
|
||||
radioSize: true,
|
||||
dotSize: true
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user