refactor: select support css var (#45727)

* refactor: select support css var

* chore: code clean

* chore: add useCSSVarCls

* chore: update style

* chore: perf code
This commit is contained in:
MadCcc 2023-11-09 09:35:45 +08:00 committed by GitHub
parent 66df407415
commit 452bdcdb53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 146 additions and 92 deletions

View File

@ -0,0 +1,9 @@
import { useToken } from '../../theme/internal';
const useCSSVarCls = (prefixCls: string) => {
const [, , , , cssVar] = useToken();
return cssVar ? `${prefixCls}-css-var` : '';
};
export default useCSSVarCls;

View File

@ -22,9 +22,11 @@ import type { SizeType } from '../config-provider/SizeContext';
import { FormItemInputContext } from '../form/context';
import { useCompactItemContext } from '../space/Compact';
import useStyle from './style';
import useCSSVar from './style/cssVar';
import useBuiltinPlacements from './useBuiltinPlacements';
import useIcons from './useIcons';
import useShowArrow from './useShowArrow';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
type RawValue = string | number;
@ -121,7 +123,9 @@ const InternalSelect = <
const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction);
const [wrapSSR, hashId] = useStyle(prefixCls);
const [, hashId] = useStyle(prefixCls);
const rootCls = useCSSVarCls(rootPrefixCls);
const wrapCSSVar = useCSSVar(rootCls);
const mode = React.useMemo(() => {
const { mode: m } = props as InternalSelectProps<OptionType>;
@ -181,12 +185,13 @@ const InternalSelect = <
'itemIcon',
]);
const rcSelectRtlDropdownClassName = classNames(
const mergedPopupClassName = classNames(
popupClassName || dropdownClassName,
{
[`${prefixCls}-dropdown-${direction}`]: direction === 'rtl',
},
rootClassName,
rootCls,
hashId,
);
@ -209,6 +214,7 @@ const InternalSelect = <
select?.className,
className,
rootClassName,
rootCls,
hashId,
);
@ -245,7 +251,7 @@ const InternalSelect = <
const [zIndex] = useZIndex('SelectLike', props.dropdownStyle?.zIndex as number);
// ====================== Render =======================
return wrapSSR(
return wrapCSSVar(
<RcSelect<ValueType, OptionType>
ref={ref}
virtual={virtual}
@ -268,7 +274,7 @@ const InternalSelect = <
notFoundContent={mergedNotFound}
className={mergedClassName}
getPopupContainer={getPopupContainer || getContextPopupContainer}
dropdownClassName={rcSelectRtlDropdownClassName}
dropdownClassName={mergedPopupClassName}
disabled={mergedDisabled}
dropdownStyle={{
...props?.dropdownStyle,

View File

@ -0,0 +1,8 @@
import { genCSSVarRegister } from '../../theme/internal';
import { prepareComponentToken } from '.';
export default genCSSVarRegister('Select', prepareComponentToken, {
unitless: {
optionLineHeight: true,
},
});

View File

@ -142,7 +142,7 @@ const genSingleStyle: GenerateStyle<SelectToken> = (token) => {
},
'&-grouped': {
paddingInlineStart: token.controlPaddingHorizontal * 2,
paddingInlineStart: token.calc(token.controlPaddingHorizontal).mul(2).equal(),
},
},
},

View File

@ -2,11 +2,12 @@ import type { CSSObject } from '@ant-design/cssinjs';
import type { CSSProperties } from 'react';
import { resetComponent, resetIcon, textEllipsis } from '../../style';
import { genCompactItemStyle } from '../../style/compact-item';
import type { FullToken, GenerateStyle } from '../../theme/internal';
import type { FullToken, GenerateStyle, GetDefaultToken } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import genDropdownStyle from './dropdown';
import genMultipleStyle from './multiple';
import genSingleStyle from './single';
import { unit } from '@ant-design/cssinjs';
export interface ComponentToken {
/**
@ -104,11 +105,15 @@ export interface ComponentToken {
* @descEN Border color of multiple tag when disabled
*/
multipleItemBorderColorDisabled: string;
/**
* @internal
*/
showArrowPaddingInlineEnd: number;
}
export interface SelectToken extends FullToken<'Select'> {
rootPrefixCls: string;
inputPaddingHorizontalBase: number;
inputPaddingHorizontalBase: number | string;
multipleSelectItemHeight: number;
selectHeight: number;
}
@ -120,7 +125,7 @@ const genSelectorStyle: GenerateStyle<SelectToken, CSSObject> = (token) => {
return {
position: 'relative',
backgroundColor: selectorBg,
border: `${token.lineWidth}px ${token.lineType} ${token.colorBorder}`,
border: `${unit(token.lineWidth)} ${token.lineType} ${token.colorBorder}`,
transition: `all ${token.motionDurationMid} ${token.motionEaseInOut}`,
input: {
@ -192,7 +197,7 @@ const genStatusStyle = (
[`${componentCls}-focused& ${componentCls}-selector`]: {
borderColor: borderActiveColor,
boxShadow: `0 0 0 ${controlOutlineWidth}px ${outlineColor}`,
boxShadow: `0 0 0 ${unit(controlOutlineWidth)} ${outlineColor}`,
outline: 0,
},
},
@ -273,7 +278,7 @@ const genBaseStyle: GenerateStyle<SelectToken> = (token) => {
insetInlineStart: 'auto',
insetInlineEnd: inputPaddingHorizontalBase,
height: token.fontSizeIcon,
marginTop: -token.fontSizeIcon / 2,
marginTop: token.calc(token.fontSizeIcon).mul(-1).div(2).equal(),
color: token.colorTextQuaternary,
fontSize: token.fontSizeIcon,
lineHeight: 1,
@ -314,7 +319,7 @@ const genBaseStyle: GenerateStyle<SelectToken> = (token) => {
display: 'inline-block',
width: token.fontSizeIcon,
height: token.fontSizeIcon,
marginTop: -token.fontSizeIcon / 2,
marginTop: token.calc(token.fontSizeIcon).mul(-1).div(2).equal(),
color: token.colorTextQuaternary,
fontSize: token.fontSizeIcon,
fontStyle: 'normal',
@ -346,7 +351,11 @@ const genBaseStyle: GenerateStyle<SelectToken> = (token) => {
// ========================= Feedback ==========================
[`${componentCls}-has-feedback`]: {
[`${componentCls}-clear`]: {
insetInlineEnd: inputPaddingHorizontalBase + token.fontSize + token.paddingXS,
insetInlineEnd: token
.calc(inputPaddingHorizontalBase)
.add(token.fontSize)
.add(token.paddingXS)
.equal(),
},
},
};
@ -437,59 +446,60 @@ const genSelectStyle: GenerateStyle<SelectToken> = (token) => {
};
// ============================== Export ==============================
export const prepareComponentToken: GetDefaultToken<'Select'> = (token) => {
const {
fontSize,
lineHeight,
controlHeight,
controlPaddingHorizontal,
zIndexPopupBase,
colorText,
fontWeightStrong,
controlItemBgActive,
controlItemBgHover,
colorBgContainer,
colorFillSecondary,
controlHeightLG,
controlHeightSM,
colorBgContainerDisabled,
colorTextDisabled,
} = token;
return {
zIndexPopup: zIndexPopupBase + 50,
optionSelectedColor: colorText,
optionSelectedFontWeight: fontWeightStrong,
optionSelectedBg: controlItemBgActive,
optionActiveBg: controlItemBgHover,
optionPadding: `${(controlHeight - fontSize * lineHeight) / 2}px ${controlPaddingHorizontal}px`,
optionFontSize: fontSize,
optionLineHeight: lineHeight,
optionHeight: controlHeight,
selectorBg: colorBgContainer,
clearBg: colorBgContainer,
singleItemHeightLG: controlHeightLG,
multipleItemBg: colorFillSecondary,
multipleItemBorderColor: 'transparent',
multipleItemHeight: controlHeightSM,
multipleItemHeightLG: controlHeight,
multipleSelectorBgDisabled: colorBgContainerDisabled,
multipleItemColorDisabled: colorTextDisabled,
multipleItemBorderColorDisabled: 'transparent',
showArrowPaddingInlineEnd: Math.ceil(token.fontSize * 1.25),
};
};
export default genComponentStyleHook(
'Select',
(token, { rootPrefixCls }) => {
const selectToken: SelectToken = mergeToken<SelectToken>(token, {
rootPrefixCls,
inputPaddingHorizontalBase: token.paddingSM - 1,
inputPaddingHorizontalBase: token.calc(token.paddingSM).sub(1).equal(),
multipleSelectItemHeight: token.multipleItemHeight,
selectHeight: token.controlHeight,
});
return [genSelectStyle(selectToken)];
},
(token) => {
const {
fontSize,
lineHeight,
controlHeight,
controlPaddingHorizontal,
zIndexPopupBase,
colorText,
fontWeightStrong,
controlItemBgActive,
controlItemBgHover,
colorBgContainer,
colorFillSecondary,
controlHeightLG,
controlHeightSM,
colorBgContainerDisabled,
colorTextDisabled,
} = token;
return {
zIndexPopup: zIndexPopupBase + 50,
optionSelectedColor: colorText,
optionSelectedFontWeight: fontWeightStrong,
optionSelectedBg: controlItemBgActive,
optionActiveBg: controlItemBgHover,
optionPadding: `${
(controlHeight - fontSize * lineHeight) / 2
}px ${controlPaddingHorizontal}px`,
optionFontSize: fontSize,
optionLineHeight: lineHeight,
optionHeight: controlHeight,
selectorBg: colorBgContainer,
clearBg: colorBgContainer,
singleItemHeightLG: controlHeightLG,
multipleItemBg: colorFillSecondary,
multipleItemBorderColor: 'transparent',
multipleItemHeight: controlHeightSM,
multipleItemHeightLG: controlHeight,
multipleSelectorBgDisabled: colorBgContainerDisabled,
multipleItemColorDisabled: colorTextDisabled,
multipleItemBorderColorDisabled: 'transparent',
};
},
prepareComponentToken,
);

View File

@ -2,17 +2,19 @@ import type { CSSInterpolation, CSSObject } from '@ant-design/cssinjs';
import type { SelectToken } from '.';
import { resetIcon } from '../../style';
import { mergeToken } from '../../theme/internal';
import { unit } from '@ant-design/cssinjs';
const FIXED_ITEM_MARGIN = 2;
const getSelectItemStyle = ({
multipleSelectItemHeight,
selectHeight,
lineWidth: borderWidth,
}: SelectToken): readonly [number, number] => {
const selectItemDist = (selectHeight - multipleSelectItemHeight) / 2 - borderWidth;
const selectItemMargin = Math.ceil(selectItemDist / 2);
return [selectItemDist, selectItemMargin] as const;
const getSelectItemStyle = (token: SelectToken): number | string => {
const { multipleSelectItemHeight, selectHeight, lineWidth } = token;
const selectItemDist = token
.calc(selectHeight)
.sub(multipleSelectItemHeight)
.div(2)
.sub(lineWidth)
.equal();
return selectItemDist;
};
function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
@ -21,7 +23,7 @@ function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
const selectOverflowPrefixCls = `${componentCls}-selection-overflow`;
const selectItemHeight = token.multipleSelectItemHeight;
const [selectItemDist] = getSelectItemStyle(token);
const selectItemDist = getSelectItemStyle(token);
const suffixCls = suffix ? `${componentCls}-${suffix}` : '';
@ -56,7 +58,9 @@ function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
alignItems: 'center',
height: '100%',
// Multiple is little different that horizontal is follow the vertical
padding: `${selectItemDist - FIXED_ITEM_MARGIN}px ${FIXED_ITEM_MARGIN * 2}px`,
padding: `${unit(token.calc(selectItemDist).sub(FIXED_ITEM_MARGIN).equal())} ${unit(
token.calc(FIXED_ITEM_MARGIN).mul(2).equal(),
)}`,
borderRadius: token.borderRadius,
[`${componentCls}-show-search&`]: {
@ -71,8 +75,8 @@ function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
'&:after': {
display: 'inline-block',
width: 0,
margin: `${FIXED_ITEM_MARGIN}px 0`,
lineHeight: `${selectItemHeight}px`,
margin: `${unit(FIXED_ITEM_MARGIN)} 0`,
lineHeight: unit(selectItemHeight),
visibility: 'hidden',
content: '"\\a0"',
},
@ -82,7 +86,10 @@ function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
&${componentCls}-show-arrow ${componentCls}-selector,
&${componentCls}-allow-clear ${componentCls}-selector
`]: {
paddingInlineEnd: token.fontSizeIcon + token.controlPaddingHorizontal,
paddingInlineEnd: token
.calc(token.fontSizeIcon)
.add(token.controlPaddingHorizontal)
.equal(),
},
// ======================== Selections ========================
@ -95,15 +102,17 @@ function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
height: selectItemHeight,
marginTop: FIXED_ITEM_MARGIN,
marginBottom: FIXED_ITEM_MARGIN,
lineHeight: `${selectItemHeight - token.lineWidth * 2}px`,
lineHeight: unit(
token.calc(selectItemHeight).sub(token.calc(token.lineWidth).mul(2)).equal(),
),
background: token.multipleItemBg,
border: `${token.lineWidth}px ${token.lineType} ${token.multipleItemBorderColor}`,
border: `${unit(token.lineWidth)} ${token.lineType} ${token.multipleItemBorderColor}`,
borderRadius: token.borderRadiusSM,
cursor: 'default',
transition: `font-size ${token.motionDurationSlow}, line-height ${token.motionDurationSlow}, height ${token.motionDurationSlow}`,
marginInlineEnd: FIXED_ITEM_MARGIN * 2,
marginInlineEnd: token.calc(FIXED_ITEM_MARGIN).mul(2).equal(),
paddingInlineStart: token.paddingXS,
paddingInlineEnd: token.paddingXS / 2,
paddingInlineEnd: token.calc(token.paddingXS).div(2).equal(),
[`${componentCls}-disabled&`]: {
color: token.multipleItemColorDisabled,
@ -114,7 +123,7 @@ function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
// It's ok not to do this, but 24px makes bottom narrow in view should adjust
'&-content': {
display: 'inline-block',
marginInlineEnd: token.paddingXS / 2,
marginInlineEnd: token.calc(token.paddingXS).div(2).equal(),
overflow: 'hidden',
whiteSpace: 'pre', // fix whitespace wrapping. custom tags display all whitespace within.
textOverflow: 'ellipsis',
@ -157,7 +166,7 @@ function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
display: 'inline-flex',
position: 'relative',
maxWidth: '100%',
marginInlineStart: token.inputPaddingHorizontalBase - selectItemDist,
marginInlineStart: token.calc(token.inputPaddingHorizontalBase).sub(selectItemDist).equal(),
[`
&-input,
@ -165,7 +174,7 @@ function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
`]: {
height: selectItemHeight,
fontFamily: token.fontFamily,
lineHeight: `${selectItemHeight}px`,
lineHeight: unit(selectItemHeight),
transition: `all ${token.motionDurationSlow}`,
},
@ -216,8 +225,6 @@ const genMultipleStyle = (token: SelectToken): CSSInterpolation => {
borderRadiusSM: token.borderRadius,
});
const [, smSelectItemMargin] = getSelectItemStyle(token);
return [
genSizeStyle(token),
// ======================== Small ========================
@ -227,12 +234,12 @@ const genMultipleStyle = (token: SelectToken): CSSInterpolation => {
{
[`${componentCls}-multiple${componentCls}-sm`]: {
[`${componentCls}-selection-placeholder`]: {
insetInline: token.controlPaddingHorizontalSM - token.lineWidth,
insetInline: token.calc(token.controlPaddingHorizontalSM).sub(token.lineWidth).equal(),
},
// https://github.com/ant-design/ant-design/issues/29559
[`${componentCls}-selection-search`]: {
marginInlineStart: smSelectItemMargin,
marginInlineStart: 2, // Magic Number
},
},
},

View File

@ -2,13 +2,15 @@ import type { CSSInterpolation, CSSObject } from '@ant-design/cssinjs';
import { resetComponent } from '../../style';
import type { SelectToken } from '.';
import { mergeToken } from '../../theme/internal';
import { unit } from '@ant-design/cssinjs';
function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
const { componentCls, inputPaddingHorizontalBase, borderRadius } = token;
const selectHeightWithoutBorder = token.controlHeight - token.lineWidth * 2;
const selectionItemPadding = Math.ceil(token.fontSize * 1.25);
const selectHeightWithoutBorder = token
.calc(token.controlHeight)
.sub(token.calc(token.lineWidth).mul(2))
.equal();
const suffixCls = suffix ? `${componentCls}-${suffix}` : '';
@ -41,7 +43,7 @@ function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
${componentCls}-selection-placeholder
`]: {
padding: 0,
lineHeight: `${selectHeightWithoutBorder}px`,
lineHeight: unit(selectHeightWithoutBorder),
transition: `all ${token.motionDurationSlow}, visibility 0s`,
alignSelf: 'center',
},
@ -70,7 +72,7 @@ function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
&${componentCls}-show-arrow ${componentCls}-selection-item,
&${componentCls}-show-arrow ${componentCls}-selection-placeholder
`]: {
paddingInlineEnd: selectionItemPadding,
paddingInlineEnd: token.showArrowPaddingInlineEnd,
},
// Opacity selection if open
@ -85,14 +87,14 @@ function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
[`${componentCls}-selector`]: {
width: '100%',
height: '100%',
padding: `0 ${inputPaddingHorizontalBase}px`,
padding: `0 ${unit(inputPaddingHorizontalBase)}`,
[`${componentCls}-selection-search-input`]: {
height: selectHeightWithoutBorder,
},
'&:after': {
lineHeight: `${selectHeightWithoutBorder}px`,
lineHeight: unit(selectHeightWithoutBorder),
},
},
},
@ -112,7 +114,7 @@ function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
position: 'absolute',
insetInlineStart: 0,
insetInlineEnd: 0,
padding: `0 ${inputPaddingHorizontalBase}px`,
padding: `0 ${unit(inputPaddingHorizontalBase)}`,
'&:after': {
display: 'none',
@ -127,7 +129,10 @@ function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
export default function genSingleStyle(token: SelectToken): CSSInterpolation {
const { componentCls } = token;
const inputPaddingHorizontalSM = token.controlPaddingHorizontalSM - token.lineWidth;
const inputPaddingHorizontalSM = token
.calc(token.controlPaddingHorizontalSM)
.sub(token.lineWidth)
.equal();
return [
genSizeStyle(token),
@ -152,19 +157,22 @@ export default function genSingleStyle(token: SelectToken): CSSInterpolation {
},
[`${componentCls}-selector`]: {
padding: `0 ${inputPaddingHorizontalSM}px`,
padding: `0 ${unit(inputPaddingHorizontalSM)}`,
},
// With arrow should provides `padding-right` to show the arrow
[`&${componentCls}-show-arrow ${componentCls}-selection-search`]: {
insetInlineEnd: inputPaddingHorizontalSM + token.fontSize * 1.5,
insetInlineEnd: token
.calc(inputPaddingHorizontalSM)
.add(token.calc(token.fontSize).mul(1.5))
.equal(),
},
[`
&${componentCls}-show-arrow ${componentCls}-selection-item,
&${componentCls}-show-arrow ${componentCls}-selection-placeholder
`]: {
paddingInlineEnd: token.fontSize * 1.5,
paddingInlineEnd: token.calc(token.fontSize).mul(1.5).equal(),
},
},
},

View File

@ -292,6 +292,11 @@ export type CSSVarRegisterProps = {
export const genCSSVarRegister = <C extends OverrideComponent>(
component: C,
getDefaultToken: GetDefaultToken<C>,
options?: {
unitless?: {
[key in ComponentTokenKey<C>]: boolean;
};
},
) => {
const CSSVarRegister: FC<CSSVarRegisterProps> = ({ rootCls, cssVar }) => {
const [, realToken] = useToken();
@ -302,6 +307,7 @@ export const genCSSVarRegister = <C extends OverrideComponent>(
key: cssVar?.key!,
unitless: {
...unitless,
...options?.unitless,
zIndexPopup: true,
},
ignore,