feat: menu support css variable theme (#45750)

* feat: layout support cssVar

* feat: menu support cssVar

* feat: menu support cssVar

* feat: menu support cssVar

* feat: menu support cssVar

* feat: optimize code

* feat: optimize code

* feat: optimize code

* feat: optimize code

* feat: optimize code

* feat: optimize code
This commit is contained in:
kiner-tang(文辉) 2023-11-13 09:38:05 +08:00 committed by GitHub
parent 047bc53920
commit 3128539211
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 209 additions and 167 deletions

View File

@ -18,6 +18,8 @@ import type { MenuContextProps, MenuTheme } from './MenuContext';
import MenuContext from './MenuContext';
import OverrideContext from './OverrideContext';
import useStyle from './style';
import useCSSVar from './style/cssVar';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
export interface MenuProps extends Omit<RcMenuProps, 'items'> {
theme?: MenuTheme;
@ -120,7 +122,9 @@ const InternalMenu = forwardRef<RcMenuRef, InternalMenuProps>((props, ref) => {
};
const prefixCls = getPrefixCls('menu', customizePrefixCls || overrideObj.prefixCls);
const [wrapSSR, hashId] = useStyle(prefixCls, !override);
const [, hashId] = useStyle(prefixCls, !override);
const rootCls = useCSSVarCls(prefixCls);
const wrapCSSVar = useCSSVar(rootCls);
const menuClassName = classNames(`${prefixCls}-${theme}`, menu?.className, className);
// ====================== Expand Icon ========================
@ -156,7 +160,7 @@ const InternalMenu = forwardRef<RcMenuRef, InternalMenuProps>((props, ref) => {
);
// ========================= Render ==========================
return wrapSSR(
return wrapCSSVar(
<OverrideContext.Provider value={null}>
<MenuContext.Provider value={contextValue}>
<RcMenu
@ -179,7 +183,7 @@ const InternalMenu = forwardRef<RcMenuRef, InternalMenuProps>((props, ref) => {
defaultMotions={defaultMotions}
expandIcon={mergedExpandIcon}
ref={ref}
rootClassName={classNames(rootClassName, hashId, overrideObj.rootClassName)}
rootClassName={classNames(rootClassName, hashId, overrideObj.rootClassName, rootCls)}
>
{mergedChildren}
</RcMenu>

View File

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

View File

@ -1,3 +1,4 @@
import { unit } from '@ant-design/cssinjs';
import type { MenuToken } from '.';
import type { GenerateStyle } from '../../theme/internal';
@ -16,7 +17,7 @@ const getHorizontalStyle: GenerateStyle<MenuToken> = (token) => {
[`${componentCls}-horizontal`]: {
lineHeight: horizontalLineHeight,
border: 0,
borderBottom: `${lineWidth}px ${lineType} ${colorSplit}`,
borderBottom: `${unit(lineWidth)} ${lineType} ${colorSplit}`,
boxShadow: 'none',
'&::after': {

View File

@ -1,14 +1,20 @@
import type { CSSObject } from '@ant-design/cssinjs';
import { unit, type CSSObject } from '@ant-design/cssinjs';
import { TinyColor } from '@ctrl/tinycolor';
import type { CSSProperties } from 'react';
import { clearFix, resetComponent, resetIcon } from '../../style';
import { genCollapseMotion, initSlideMotion, initZoomMotion } from '../../style/motion';
import type { FullToken, GenerateStyle, UseComponentStyleResult } from '../../theme/internal';
import type {
FullToken,
GenerateStyle,
GetDefaultToken,
UseComponentStyleResult,
} from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import getHorizontalStyle from './horizontal';
import getRTLStyle from './rtl';
import getThemeStyle from './theme';
import getVerticalStyle from './vertical';
import type { CssUtil } from 'antd-style';
/** Component only token. Which will handle additional calculation of alias token */
export interface ComponentToken {
@ -35,7 +41,7 @@ export interface ComponentToken {
* @desc
* @descEN line-height of group title
*/
groupTitleLineHeight: CSSProperties['lineHeight'];
groupTitleLineHeight: string | number;
/**
* @desc
* @descEN font-size of group title
@ -357,12 +363,14 @@ export interface ComponentToken {
* @descEN Background of active danger menu item in dark mode
*/
darkDangerItemActiveBg: string;
/** @internal */
subMenuTitleWidth: number | string;
}
export interface MenuToken extends FullToken<'Menu'> {
menuHorizontalHeight: number;
menuArrowSize: number;
menuArrowOffset: string;
menuHorizontalHeight: number | string;
menuArrowSize: number | string;
menuArrowOffset: number | string;
menuPanelMaskInset: number;
menuSubMenuBg: string;
}
@ -471,8 +479,8 @@ const genSubMenuArrowStyle = (token: MenuToken): CSSObject => {
// →
'&::before, &::after': {
position: 'absolute',
width: menuArrowSize * 0.6,
height: menuArrowSize * 0.15,
width: token.calc(menuArrowSize).mul(0.6).equal(),
height: token.calc(menuArrowSize).mul(0.15).equal(),
backgroundColor: 'currentcolor',
borderRadius,
transition: [
@ -485,11 +493,13 @@ const genSubMenuArrowStyle = (token: MenuToken): CSSObject => {
},
'&::before': {
transform: `rotate(45deg) translateY(-${menuArrowOffset})`,
transform: `rotate(45deg) translateY(${unit(
token.calc(menuArrowOffset).mul(-1).equal(),
)})`,
},
'&::after': {
transform: `rotate(-45deg) translateY(${menuArrowOffset})`,
transform: `rotate(-45deg) translateY(${unit(menuArrowOffset)})`,
},
},
},
@ -570,7 +580,7 @@ const getBaseStyle: GenerateStyle<MenuToken> = (token) => {
},
[`${componentCls}-item-group-title`]: {
padding: `${paddingXS}px ${padding}px`,
padding: `${unit(paddingXS)} ${unit(padding)}`,
fontSize: groupTitleFontSize,
lineHeight: groupTitleLineHeight,
transition: `all ${motionDurationSlow}`,
@ -646,7 +656,7 @@ const getBaseStyle: GenerateStyle<MenuToken> = (token) => {
padding: 0,
[`${componentCls}-item, ${componentCls}-submenu-title`]: {
paddingInline: `${fontSize * 2}px ${padding}px`,
paddingInline: `${unit(token.calc(fontSize).mul(2).equal())} ${unit(padding)}`,
},
},
},
@ -667,7 +677,7 @@ const getBaseStyle: GenerateStyle<MenuToken> = (token) => {
// https://github.com/ant-design/ant-design/issues/13955
'&::before': {
position: 'absolute',
inset: `${menuPanelMaskInset}px 0 0`,
inset: `${unit(menuPanelMaskInset)} 0 0`,
zIndex: -1,
width: '100%',
height: '100%',
@ -760,25 +770,29 @@ const getBaseStyle: GenerateStyle<MenuToken> = (token) => {
&-inline ${componentCls}-submenu-arrow`]: {
// ↓
'&::before': {
transform: `rotate(-45deg) translateX(${menuArrowOffset})`,
transform: `rotate(-45deg) translateX(${unit(menuArrowOffset)})`,
},
'&::after': {
transform: `rotate(45deg) translateX(-${menuArrowOffset})`,
transform: `rotate(45deg) translateX(${unit(
token.calc(menuArrowOffset).mul(-1).equal(),
)})`,
},
},
[`${componentCls}-submenu-open${componentCls}-submenu-inline > ${componentCls}-submenu-title > ${componentCls}-submenu-arrow`]:
{
// ↑
transform: `translateY(-${menuArrowSize * 0.2}px)`,
transform: `translateY(${unit(token.calc(menuArrowSize).mul(0.2).mul(-1).equal())})`,
'&::after': {
transform: `rotate(-45deg) translateX(-${menuArrowOffset})`,
transform: `rotate(-45deg) translateX(${unit(
token.calc(menuArrowOffset).mul(-1).equal(),
)})`,
},
'&::before': {
transform: `rotate(45deg) translateX(${menuArrowOffset})`,
transform: `rotate(45deg) translateX(${unit(menuArrowOffset)})`,
},
},
},
@ -795,106 +809,7 @@ const getBaseStyle: GenerateStyle<MenuToken> = (token) => {
];
};
// ============================== Export ==============================
export default (prefixCls: string, injectStyle: boolean): UseComponentStyleResult => {
const useOriginHook = genComponentStyleHook(
'Menu',
(token) => {
// Dropdown will handle menu style self. We do not need to handle this.
if (injectStyle === false) {
return [];
}
const {
colorBgElevated,
colorPrimary,
colorTextLightSolid,
controlHeightLG,
fontSize,
darkItemColor,
darkDangerItemColor,
darkItemBg,
darkSubMenuItemBg,
darkItemSelectedColor,
darkItemSelectedBg,
darkDangerItemSelectedBg,
darkItemHoverBg,
darkGroupTitleColor,
darkItemHoverColor,
darkItemDisabledColor,
darkDangerItemHoverColor,
darkDangerItemSelectedColor,
darkDangerItemActiveBg,
} = token;
const menuArrowSize = (fontSize / 7) * 5;
// Menu Token
const menuToken = mergeToken<MenuToken>(token, {
menuArrowSize,
menuHorizontalHeight: controlHeightLG * 1.15,
menuArrowOffset: `${menuArrowSize * 0.25}px`,
menuPanelMaskInset: -7, // Still a hardcode here since it's offset by rc-align
menuSubMenuBg: colorBgElevated,
});
const menuDarkToken = mergeToken<MenuToken>(menuToken, {
itemColor: darkItemColor,
itemHoverColor: darkItemHoverColor,
groupTitleColor: darkGroupTitleColor,
itemSelectedColor: darkItemSelectedColor,
itemBg: darkItemBg,
popupBg: darkItemBg,
subMenuItemBg: darkSubMenuItemBg,
itemActiveBg: 'transparent',
itemSelectedBg: darkItemSelectedBg,
activeBarHeight: 0,
activeBarBorderWidth: 0,
itemHoverBg: darkItemHoverBg,
// Disabled
itemDisabledColor: darkItemDisabledColor,
// Danger
dangerItemColor: darkDangerItemColor,
dangerItemHoverColor: darkDangerItemHoverColor,
dangerItemSelectedColor: darkDangerItemSelectedColor,
dangerItemActiveBg: darkDangerItemActiveBg,
dangerItemSelectedBg: darkDangerItemSelectedBg,
menuSubMenuBg: darkSubMenuItemBg,
// Horizontal
horizontalItemSelectedColor: colorTextLightSolid,
horizontalItemSelectedBg: colorPrimary,
});
return [
// Basic
getBaseStyle(menuToken),
// Horizontal
getHorizontalStyle(menuToken), // Hard code for some light style
// Vertical
getVerticalStyle(menuToken), // Hard code for some light style
// Theme
getThemeStyle(menuToken, 'light'),
getThemeStyle(menuDarkToken, 'dark'),
// RTL
getRTLStyle(menuToken),
// Motion
genCollapseMotion(menuToken),
initSlideMotion(menuToken, 'slide-up'),
initSlideMotion(menuToken, 'slide-down'),
initZoomMotion(menuToken, 'zoom-big'),
];
},
(token) => {
export const prepareComponentToken: GetDefaultToken<'Menu'> = (token) => {
const {
colorPrimary,
colorError,
@ -1010,8 +925,116 @@ export default (prefixCls: string, injectStyle: boolean): UseComponentStyleResul
darkDangerItemHoverColor: colorErrorHover,
darkDangerItemSelectedColor: colorTextLightSolid,
darkDangerItemActiveBg: colorError,
subMenuTitleWidth: `calc(100% - ${token.marginXXS * 2}px)`,
};
};
// ============================== Export ==============================
export default (prefixCls: string, injectStyle: boolean): UseComponentStyleResult => {
const useOriginHook = genComponentStyleHook(
'Menu',
(token) => {
// Dropdown will handle menu style self. We do not need to handle this.
if (injectStyle === false) {
return [];
}
const {
colorBgElevated,
colorPrimary,
colorTextLightSolid,
controlHeightLG,
fontSize,
darkItemColor,
darkDangerItemColor,
darkItemBg,
darkSubMenuItemBg,
darkItemSelectedColor,
darkItemSelectedBg,
darkDangerItemSelectedBg,
darkItemHoverBg,
darkGroupTitleColor,
darkItemHoverColor,
darkItemDisabledColor,
darkDangerItemHoverColor,
darkDangerItemSelectedColor,
darkDangerItemActiveBg,
} = token;
const menuArrowSize = token.calc(fontSize).div(7).mul(5).equal();
// Menu Token
const menuToken = mergeToken<MenuToken & CssUtil>(token, {
menuArrowSize,
menuHorizontalHeight: token.calc(controlHeightLG).mul(1.15).equal(),
menuArrowOffset: token.calc(menuArrowSize).mul(0.25).equal(),
menuPanelMaskInset: -7, // Still a hardcode here since it's offset by rc-align
menuSubMenuBg: colorBgElevated,
calc: token.calc,
subMenuTitleWidth:
token.activeBarWidth && token.activeBarBorderWidth
? `calc(100% + ${token.activeBarBorderWidth}px)`
: `calc(100% - ${token.marginXXS * 2}px)`,
});
const menuDarkToken = mergeToken<MenuToken>(menuToken, {
itemColor: darkItemColor,
itemHoverColor: darkItemHoverColor,
groupTitleColor: darkGroupTitleColor,
itemSelectedColor: darkItemSelectedColor,
itemBg: darkItemBg,
popupBg: darkItemBg,
subMenuItemBg: darkSubMenuItemBg,
itemActiveBg: 'transparent',
itemSelectedBg: darkItemSelectedBg,
activeBarHeight: 0,
activeBarBorderWidth: 0,
itemHoverBg: darkItemHoverBg,
// Disabled
itemDisabledColor: darkItemDisabledColor,
// Danger
dangerItemColor: darkDangerItemColor,
dangerItemHoverColor: darkDangerItemHoverColor,
dangerItemSelectedColor: darkDangerItemSelectedColor,
dangerItemActiveBg: darkDangerItemActiveBg,
dangerItemSelectedBg: darkDangerItemSelectedBg,
menuSubMenuBg: darkSubMenuItemBg,
// Horizontal
horizontalItemSelectedColor: colorTextLightSolid,
horizontalItemSelectedBg: colorPrimary,
});
return [
// Basic
getBaseStyle(menuToken),
// Horizontal
getHorizontalStyle(menuToken), // Hard code for some light style
// Vertical
getVerticalStyle(menuToken), // Hard code for some light style
// Theme
getThemeStyle(menuToken, 'light'),
getThemeStyle(menuDarkToken, 'dark'),
// RTL
getRTLStyle(menuToken),
// Motion
genCollapseMotion(menuToken),
initSlideMotion(menuToken, 'slide-up'),
initSlideMotion(menuToken, 'slide-down'),
initZoomMotion(menuToken, 'zoom-big'),
];
},
prepareComponentToken,
{
deprecatedTokens: [
['colorGroupTitle', 'groupTitleColor'],

View File

@ -1,7 +1,13 @@
import type { CssUtil } from 'antd-style';
import type { MenuToken } from '.';
import type { GenerateStyle } from '../../theme/internal';
import { unit } from '@ant-design/cssinjs';
const getRTLStyle: GenerateStyle<MenuToken> = ({ componentCls, menuArrowOffset }) => ({
const getRTLStyle: GenerateStyle<MenuToken & CssUtil> = ({
componentCls,
menuArrowOffset,
calc,
}) => ({
[`${componentCls}-rtl`]: {
direction: 'rtl',
},
@ -15,11 +21,11 @@ const getRTLStyle: GenerateStyle<MenuToken> = ({ componentCls, menuArrowOffset }
${componentCls}-submenu-rtl ${componentCls}-vertical`]: {
[`${componentCls}-submenu-arrow`]: {
'&::before': {
transform: `rotate(-45deg) translateY(-${menuArrowOffset})`,
transform: `rotate(-45deg) translateY(${unit(calc(menuArrowOffset).mul(-1).equal())})`,
},
'&::after': {
transform: `rotate(45deg) translateY(${menuArrowOffset})`,
transform: `rotate(45deg) translateY(${unit(menuArrowOffset)})`,
},
},
},

View File

@ -1,4 +1,5 @@
import type { CSSInterpolation } from '@ant-design/cssinjs';
import { unit, type CSSInterpolation } from '@ant-design/cssinjs';
import type { MenuToken } from '.';
import { genFocusOutline } from '../../style';
@ -172,7 +173,7 @@ const getThemeStyle = (token: MenuToken, themeSuffix: string): CSSInterpolation
[`> ${componentCls}-item, > ${componentCls}-submenu`]: {
top: activeBarBorderWidth,
marginTop: -activeBarBorderWidth,
marginTop: token.calc(activeBarBorderWidth).mul(-1).equal(),
marginBottom: 0,
borderRadius: horizontalItemBorderRadius,
@ -180,7 +181,7 @@ const getThemeStyle = (token: MenuToken, themeSuffix: string): CSSInterpolation
position: 'absolute',
insetInline: itemPaddingInline,
bottom: 0,
borderBottom: `${activeBarHeight}px solid transparent`,
borderBottom: `${unit(activeBarHeight)} solid transparent`,
transition: `border-color ${motionDurationSlow} ${motionEaseInOut}`,
content: '""',
},
@ -210,7 +211,7 @@ const getThemeStyle = (token: MenuToken, themeSuffix: string): CSSInterpolation
//
[`&${componentCls}-root`]: {
[`&${componentCls}-inline, &${componentCls}-vertical`]: {
borderInlineEnd: `${activeBarBorderWidth}px ${lineType} ${colorSplit}`,
borderInlineEnd: `${unit(activeBarBorderWidth)} ${lineType} ${colorSplit}`,
},
},
@ -222,12 +223,9 @@ const getThemeStyle = (token: MenuToken, themeSuffix: string): CSSInterpolation
},
// Item
[`${componentCls}-item, ${componentCls}-submenu-title`]:
activeBarBorderWidth && activeBarWidth
? {
width: `calc(100% + ${activeBarBorderWidth}px)`,
}
: {},
[`${componentCls}-item, ${componentCls}-submenu-title`]: {
width: token.subMenuTitleWidth,
},
[`${componentCls}-item`]: {
position: 'relative',
@ -236,7 +234,7 @@ const getThemeStyle = (token: MenuToken, themeSuffix: string): CSSInterpolation
position: 'absolute',
insetBlock: 0,
insetInlineEnd: 0,
borderInlineEnd: `${activeBarWidth}px solid ${itemSelectedColor}`,
borderInlineEnd: `${unit(activeBarWidth)} solid ${itemSelectedColor}`,
transform: 'scaleY(0.0001)',
opacity: 0,
transition: [

View File

@ -1,4 +1,4 @@
import type { CSSObject } from '@ant-design/cssinjs';
import { unit, type CSSObject } from '@ant-design/cssinjs';
import type { MenuToken } from '.';
import { textEllipsis } from '../../style';
import type { GenerateStyle } from '../../theme/internal';
@ -14,7 +14,7 @@ const getVerticalInlineStyle: GenerateStyle<MenuToken, CSSObject> = (token) => {
itemMarginBlock,
} = token;
const paddingWithArrow = padding + menuArrowSize + marginXS;
const paddingWithArrow = token.calc(menuArrowSize).add(padding).add(marginXS).equal();
return {
[`${componentCls}-item`]: {
@ -24,19 +24,19 @@ const getVerticalInlineStyle: GenerateStyle<MenuToken, CSSObject> = (token) => {
[`${componentCls}-item, ${componentCls}-submenu-title`]: {
height: itemHeight,
lineHeight: `${itemHeight}px`,
lineHeight: `${unit(itemHeight)}`,
paddingInline: padding,
overflow: 'hidden',
textOverflow: 'ellipsis',
marginInline: itemMarginInline,
marginBlock: itemMarginBlock,
width: `calc(100% - ${itemMarginInline * 2}px)`,
width: token.subMenuTitleWidth,
},
[`> ${componentCls}-item,
> ${componentCls}-submenu > ${componentCls}-submenu-title`]: {
height: itemHeight,
lineHeight: `${itemHeight}px`,
lineHeight: `${unit(itemHeight)}`,
},
[`${componentCls}-item-group-list ${componentCls}-submenu-title,
@ -68,7 +68,7 @@ const getVerticalStyle: GenerateStyle<MenuToken> = (token) => {
const inlineItemStyle: CSSObject = {
height: itemHeight,
lineHeight: `${itemHeight}px`,
lineHeight: `${unit(itemHeight)}`,
listStylePosition: 'inside',
listStyleType: 'disc',
};
@ -97,7 +97,7 @@ const getVerticalStyle: GenerateStyle<MenuToken> = (token) => {
{
[`${componentCls}-submenu-popup ${componentCls}-vertical${componentCls}-sub`]: {
minWidth: dropdownWidth,
maxHeight: `calc(100vh - ${controlHeightLG * 2.5}px)`,
maxHeight: `calc(100vh - ${unit(token.calc(controlHeightLG).mul(2.5).equal())})`,
padding: '0',
overflow: 'hidden',
borderInlineEnd: 0,
@ -178,7 +178,9 @@ const getVerticalStyle: GenerateStyle<MenuToken> = (token) => {
> ${componentCls}-item-group > ${componentCls}-item-group-list > ${componentCls}-submenu > ${componentCls}-submenu-title,
> ${componentCls}-submenu > ${componentCls}-submenu-title`]: {
insetInlineStart: 0,
paddingInline: `calc(50% - ${fontSizeLG / 2}px - ${itemMarginInline}px)`,
paddingInline: `calc(50% - ${unit(token.calc(fontSizeLG).div(2).equal())} - ${unit(
itemMarginInline,
)})`,
textOverflow: 'clip',
[`
@ -191,7 +193,7 @@ const getVerticalStyle: GenerateStyle<MenuToken> = (token) => {
[`${componentCls}-item-icon, ${iconCls}`]: {
margin: 0,
fontSize: collapsedIconSize,
lineHeight: `${itemHeight}px`,
lineHeight: `${unit(itemHeight)}`,
'+ span': {
display: 'inline-block',