feat: tabs css var (#8330)

Co-authored-by: nishu <nishu@qianxin.com>
feat-4.3
Shuhari 2025-08-26 00:08:38 +08:00 committed by GitHub
parent 35f832ced7
commit 10382c4526
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 301 additions and 116 deletions

View File

@ -41,6 +41,7 @@ import pick from 'lodash-es/pick';
import PropTypes from '../../_util/vue-types'; import PropTypes from '../../_util/vue-types';
import type { MouseEventHandler } from '../../_util/EventInterface'; import type { MouseEventHandler } from '../../_util/EventInterface';
import omit from '../../_util/omit'; import omit from '../../_util/omit';
import useCSSVarCls from '../../config-provider/hooks/useCssVarCls';
import useStyle from '../style'; import useStyle from '../style';
export type TabsType = 'line' | 'card' | 'editable-card'; export type TabsType = 'line' | 'card' | 'editable-card';
export type TabsPosition = 'top' | 'right' | 'bottom' | 'left'; export type TabsPosition = 'top' | 'right' | 'bottom' | 'left';
@ -169,7 +170,8 @@ const InternalTabs = defineComponent({
'tabs', 'tabs',
props, props,
); );
const [wrapSSR, hashId] = useStyle(prefixCls); const rootCls = useCSSVarCls(prefixCls);
const [wrapSSR, hashId, cssVarCls] = useStyle(prefixCls, rootCls);
const rtl = computed(() => direction.value === 'rtl'); const rtl = computed(() => direction.value === 'rtl');
const mergedAnimated = computed<AnimatedConfig>(() => { const mergedAnimated = computed<AnimatedConfig>(() => {
const { animated, tabPosition } = props; const { animated, tabPosition } = props;
@ -322,6 +324,8 @@ const InternalTabs = defineComponent({
pre, pre,
`${pre}-${mergedTabPosition.value}`, `${pre}-${mergedTabPosition.value}`,
{ {
[cssVarCls.value]: true,
[rootCls.value]: true,
[hashId.value]: true, [hashId.value]: true,
[`${pre}-${size.value}`]: size.value, [`${pre}-${size.value}`]: size.value,
[`${pre}-card`]: ['card', 'editable-card'].includes(type as string), [`${pre}-card`]: ['card', 'editable-card'].includes(type as string),

View File

@ -1,55 +1,181 @@
import { CSSObject } from '../../_util/cssinjs'; import { unit } from '../../_util/cssinjs';
import type { FullToken, GenerateStyle } from '../../theme/internal'; import type { CSSObject } from '../../_util/cssinjs';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { genFocusStyle, resetComponent, textEllipsis } from '../../style'; import { genFocusOutline, genFocusStyle, resetComponent, textEllipsis } from '../../style';
import type { FullToken, GenerateStyle, GetDefaultToken } from '../../theme/internal';
import { genStyleHooks, mergeToken } from '../../theme/internal';
import genMotionStyle from './motion'; import genMotionStyle from './motion';
export interface ComponentToken { export interface ComponentToken {
/**
* @desc z-index
* @descEN z-index of dropdown menu
*/
zIndexPopup: number; zIndexPopup: number;
/**
* @desc
* @descEN Background color of card tab
*/
cardBg: string;
/**
* @desc
* @descEN Height of card tab
*/
cardHeight: number | string;
/**
* @desc
* @descEN Padding of card tab
*/
cardPadding: string;
/**
* @desc
* @descEN Padding of small card tab
*/
cardPaddingSM: string;
/**
* @desc
* @descEN Padding of large card tab
*/
cardPaddingLG: string;
/**
* @desc
* @descEN Font size of title
*/
titleFontSize: number;
/**
* @desc
* @descEN Font size of large title
*/
titleFontSizeLG: number;
/**
* @desc
* @descEN Font size of small title
*/
titleFontSizeSM: number;
/**
* @desc
* @descEN Color of indicator
*/
inkBarColor: string;
/**
* @desc
* @descEN Horizontal margin of horizontal tab
*/
horizontalMargin: string;
/**
* @desc
* @descEN Horizontal gutter of horizontal tab
*/
horizontalItemGutter: number;
/**
* @desc
* @descEN Horizontal margin of horizontal tab item
*/
horizontalItemMargin: string;
/**
* @desc RTL
* @descEN Horizontal margin of horizontal tab item (RTL)
*/
horizontalItemMarginRTL: string;
/**
* @desc
* @descEN Horizontal padding of horizontal tab item
*/
horizontalItemPadding: string;
/**
* @desc
* @descEN Horizontal padding of large horizontal tab item
*/
horizontalItemPaddingLG: string;
/**
* @desc
* @descEN Horizontal padding of small horizontal tab item
*/
horizontalItemPaddingSM: string;
/**
* @desc
* @descEN Vertical padding of vertical tab item
*/
verticalItemPadding: string;
/**
* @desc
* @descEN Vertical margin of vertical tab item
*/
verticalItemMargin: string;
/**
* @desc
* @descEN Text color of tab
*/
itemColor: string;
/**
* @desc
* @descEN Text color of active tab
*/
itemActiveColor: string;
/**
* @desc
* @descEN Text color of hover tab
*/
itemHoverColor: string;
/**
* @desc
* @descEN Text color of selected tab
*/
itemSelectedColor: string;
/**
* @desc
* @descEN Gutter of card tab
*/
cardGutter: number;
} }
export interface TabsToken extends FullToken<'Tabs'> { export interface TabsToken extends FullToken<'Tabs'> {
tabsCardHorizontalPadding: string; tabsCardPadding: string;
tabsCardHeight: number;
tabsCardGutter: number;
tabsHoverColor: string;
tabsActiveColor: string;
tabsHorizontalGutter: number;
tabsCardHeadBackground: string;
dropdownEdgeChildVerticalPadding: number; dropdownEdgeChildVerticalPadding: number;
tabsNavWrapPseudoWidth: number; tabsNavWrapPseudoWidth: number;
tabsActiveTextShadow: string; tabsActiveTextShadow: string;
tabsDropdownHeight: number; tabsDropdownHeight: number | string;
tabsDropdownWidth: number; tabsDropdownWidth: number | string;
tabsHorizontalItemMargin: string;
tabsHorizontalItemMarginRTL: string;
} }
const genCardStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject => { const genCardStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject => {
const { const {
componentCls, componentCls,
tabsCardHorizontalPadding, tabsCardPadding,
tabsCardHeadBackground, cardBg,
tabsCardGutter, cardGutter,
colorSplit, colorBorderSecondary,
itemSelectedColor,
} = token; } = token;
return { return {
[`${componentCls}-card`]: { [`${componentCls}-card`]: {
[`> ${componentCls}-nav, > div > ${componentCls}-nav`]: { [`> ${componentCls}-nav, > div > ${componentCls}-nav`]: {
[`${componentCls}-tab`]: { [`${componentCls}-tab`]: {
margin: 0, margin: 0,
padding: tabsCardHorizontalPadding, padding: tabsCardPadding,
background: tabsCardHeadBackground, background: cardBg,
border: `${token.lineWidth}px ${token.lineType} ${colorSplit}`, border: `${unit(token.lineWidth)} ${token.lineType} ${colorBorderSecondary}`,
transition: `all ${token.motionDurationSlow} ${token.motionEaseInOut}`, transition: `all ${token.motionDurationSlow} ${token.motionEaseInOut}`,
}, },
[`${componentCls}-tab-active`]: { [`${componentCls}-tab-active`]: {
color: token.colorPrimary, color: itemSelectedColor,
background: token.colorBgContainer, background: token.colorBgContainer,
}, },
[`${componentCls}-tab-focus`]: {
...genFocusOutline(token, -3),
},
[`${componentCls}-ink-bar`]: { [`${componentCls}-ink-bar`]: {
visibility: 'hidden', visibility: 'hidden',
}, },
[`& ${componentCls}-tab${componentCls}-tab-focus ${componentCls}-tab-btn`]: {
outline: 'none',
},
}, },
// ========================== Top & Bottom ========================== // ========================== Top & Bottom ==========================
@ -58,7 +184,7 @@ const genCardStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
[`${componentCls}-tab + ${componentCls}-tab`]: { [`${componentCls}-tab + ${componentCls}-tab`]: {
marginLeft: { marginLeft: {
_skip_check_: true, _skip_check_: true,
value: `${tabsCardGutter}px`, value: unit(cardGutter),
}, },
}, },
}, },
@ -67,7 +193,7 @@ const genCardStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
[`&${componentCls}-top`]: { [`&${componentCls}-top`]: {
[`> ${componentCls}-nav, > div > ${componentCls}-nav`]: { [`> ${componentCls}-nav, > div > ${componentCls}-nav`]: {
[`${componentCls}-tab`]: { [`${componentCls}-tab`]: {
borderRadius: `${token.borderRadiusLG}px ${token.borderRadiusLG}px 0 0`, borderRadius: `${unit(token.borderRadiusLG)} ${unit(token.borderRadiusLG)} 0 0`,
}, },
[`${componentCls}-tab-active`]: { [`${componentCls}-tab-active`]: {
@ -79,7 +205,7 @@ const genCardStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
[`&${componentCls}-bottom`]: { [`&${componentCls}-bottom`]: {
[`> ${componentCls}-nav, > div > ${componentCls}-nav`]: { [`> ${componentCls}-nav, > div > ${componentCls}-nav`]: {
[`${componentCls}-tab`]: { [`${componentCls}-tab`]: {
borderRadius: `0 0 ${token.borderRadiusLG}px ${token.borderRadiusLG}px`, borderRadius: `0 0 ${unit(token.borderRadiusLG)} ${unit(token.borderRadiusLG)}`,
}, },
[`${componentCls}-tab-active`]: { [`${componentCls}-tab-active`]: {
@ -92,7 +218,7 @@ const genCardStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
[`&${componentCls}-left, &${componentCls}-right`]: { [`&${componentCls}-left, &${componentCls}-right`]: {
[`> ${componentCls}-nav, > div > ${componentCls}-nav`]: { [`> ${componentCls}-nav, > div > ${componentCls}-nav`]: {
[`${componentCls}-tab + ${componentCls}-tab`]: { [`${componentCls}-tab + ${componentCls}-tab`]: {
marginTop: `${tabsCardGutter}px`, marginTop: unit(cardGutter),
}, },
}, },
}, },
@ -102,7 +228,7 @@ const genCardStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
[`${componentCls}-tab`]: { [`${componentCls}-tab`]: {
borderRadius: { borderRadius: {
_skip_check_: true, _skip_check_: true,
value: `${token.borderRadiusLG}px 0 0 ${token.borderRadiusLG}px`, value: `${unit(token.borderRadiusLG)} 0 0 ${unit(token.borderRadiusLG)}`,
}, },
}, },
@ -120,7 +246,7 @@ const genCardStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
[`${componentCls}-tab`]: { [`${componentCls}-tab`]: {
borderRadius: { borderRadius: {
_skip_check_: true, _skip_check_: true,
value: `0 ${token.borderRadiusLG}px ${token.borderRadiusLG}px 0`, value: `0 ${unit(token.borderRadiusLG)} ${unit(token.borderRadiusLG)} 0`,
}, },
}, },
@ -137,7 +263,7 @@ const genCardStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
}; };
const genDropdownStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject => { const genDropdownStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject => {
const { componentCls, tabsHoverColor, dropdownEdgeChildVerticalPadding } = token; const { componentCls, itemHoverColor, dropdownEdgeChildVerticalPadding } = token;
return { return {
[`${componentCls}-dropdown`]: { [`${componentCls}-dropdown`]: {
...resetComponent(token), ...resetComponent(token),
@ -158,7 +284,7 @@ const genDropdownStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject
[`${componentCls}-dropdown-menu`]: { [`${componentCls}-dropdown-menu`]: {
maxHeight: token.tabsDropdownHeight, maxHeight: token.tabsDropdownHeight,
margin: 0, margin: 0,
padding: `${dropdownEdgeChildVerticalPadding}px 0`, padding: `${unit(dropdownEdgeChildVerticalPadding)} 0`,
overflowX: 'hidden', overflowX: 'hidden',
overflowY: 'auto', overflowY: 'auto',
textAlign: { textAlign: {
@ -178,7 +304,7 @@ const genDropdownStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject
alignItems: 'center', alignItems: 'center',
minWidth: token.tabsDropdownWidth, minWidth: token.tabsDropdownWidth,
margin: 0, margin: 0,
padding: `${token.paddingXXS}px ${token.paddingSM}px`, padding: `${unit(token.paddingXXS)} ${unit(token.paddingSM)}`,
color: token.colorText, color: token.colorText,
fontWeight: 'normal', fontWeight: 'normal',
fontSize: token.fontSize, fontSize: token.fontSize,
@ -197,14 +323,14 @@ const genDropdownStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject
_skip_check_: true, _skip_check_: true,
value: token.marginSM, value: token.marginSM,
}, },
color: token.colorTextDescription, color: token.colorIcon,
fontSize: token.fontSizeSM, fontSize: token.fontSizeSM,
background: 'transparent', background: 'transparent',
border: 0, border: 0,
cursor: 'pointer', cursor: 'pointer',
'&:hover': { '&:hover': {
color: tabsHoverColor, color: itemHoverColor,
}, },
}, },
@ -226,14 +352,22 @@ const genDropdownStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject
}; };
const genPositionStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject => { const genPositionStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject => {
const { componentCls, margin, colorSplit } = token; const {
componentCls,
margin,
colorBorderSecondary,
horizontalMargin,
verticalItemPadding,
verticalItemMargin,
calc,
} = token;
return { return {
// ========================== Top & Bottom ========================== // ========================== Top & Bottom ==========================
[`${componentCls}-top, ${componentCls}-bottom`]: { [`${componentCls}-top, ${componentCls}-bottom`]: {
flexDirection: 'column', flexDirection: 'column',
[`> ${componentCls}-nav, > div > ${componentCls}-nav`]: { [`> ${componentCls}-nav, > div > ${componentCls}-nav`]: {
margin: `0 0 ${margin}px 0`, margin: horizontalMargin,
'&::before': { '&::before': {
position: 'absolute', position: 'absolute',
@ -245,7 +379,7 @@ const genPositionStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject
_skip_check_: true, _skip_check_: true,
value: 0, value: 0,
}, },
borderBottom: `${token.lineWidth}px ${token.lineType} ${colorSplit}`, borderBottom: `${unit(token.lineWidth)} ${token.lineType} ${colorBorderSecondary}`,
content: "''", content: "''",
}, },
@ -307,7 +441,7 @@ const genPositionStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject
[`${componentCls}-bottom`]: { [`${componentCls}-bottom`]: {
[`> ${componentCls}-nav, > div > ${componentCls}-nav`]: { [`> ${componentCls}-nav, > div > ${componentCls}-nav`]: {
order: 1, order: 1,
marginTop: `${margin}px`, marginTop: margin,
marginBottom: 0, marginBottom: 0,
'&::before': { '&::before': {
@ -328,16 +462,16 @@ const genPositionStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject
[`${componentCls}-left, ${componentCls}-right`]: { [`${componentCls}-left, ${componentCls}-right`]: {
[`> ${componentCls}-nav, > div > ${componentCls}-nav`]: { [`> ${componentCls}-nav, > div > ${componentCls}-nav`]: {
flexDirection: 'column', flexDirection: 'column',
minWidth: token.controlHeight * 1.25, minWidth: calc(token.controlHeight).mul(1.25).equal(),
// >>>>>>>>>>> Tab // >>>>>>>>>>> Tab
[`${componentCls}-tab`]: { [`${componentCls}-tab`]: {
padding: `${token.paddingXS}px ${token.paddingLG}px`, padding: verticalItemPadding,
textAlign: 'center', textAlign: 'center',
}, },
[`${componentCls}-tab + ${componentCls}-tab`]: { [`${componentCls}-tab + ${componentCls}-tab`]: {
margin: `${token.margin}px 0 0 0`, margin: verticalItemMargin,
}, },
// >>>>>>>>>>> Nav // >>>>>>>>>>> Nav
@ -404,11 +538,11 @@ const genPositionStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject
[`> ${componentCls}-content-holder, > div > ${componentCls}-content-holder`]: { [`> ${componentCls}-content-holder, > div > ${componentCls}-content-holder`]: {
marginLeft: { marginLeft: {
_skip_check_: true, _skip_check_: true,
value: `-${token.lineWidth}px`, value: unit(calc(token.lineWidth).mul(-1).equal()),
}, },
borderLeft: { borderLeft: {
_skip_check_: true, _skip_check_: true,
value: `${token.lineWidth}px ${token.lineType} ${token.colorBorder}`, value: `${unit(token.lineWidth)} ${token.lineType} ${token.colorBorder}`,
}, },
[`> ${componentCls}-content > ${componentCls}-tabpane`]: { [`> ${componentCls}-content > ${componentCls}-tabpane`]: {
@ -436,11 +570,11 @@ const genPositionStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject
order: 0, order: 0,
marginRight: { marginRight: {
_skip_check_: true, _skip_check_: true,
value: -token.lineWidth, value: calc(token.lineWidth).mul(-1).equal(),
}, },
borderRight: { borderRight: {
_skip_check_: true, _skip_check_: true,
value: `${token.lineWidth}px ${token.lineType} ${token.colorBorder}`, value: `${unit(token.lineWidth)} ${token.lineType} ${token.colorBorder}`,
}, },
[`> ${componentCls}-content > ${componentCls}-tabpane`]: { [`> ${componentCls}-content > ${componentCls}-tabpane`]: {
@ -455,14 +589,20 @@ const genPositionStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject
}; };
const genSizeStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject => { const genSizeStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject => {
const { componentCls, padding } = token; const {
componentCls,
cardPaddingSM,
cardPaddingLG,
horizontalItemPaddingSM,
horizontalItemPaddingLG,
} = token;
return { return {
[componentCls]: { [componentCls]: {
'&-small': { '&-small': {
[`> ${componentCls}-nav`]: { [`> ${componentCls}-nav`]: {
[`${componentCls}-tab`]: { [`${componentCls}-tab`]: {
padding: `${token.paddingXS}px 0`, padding: horizontalItemPaddingSM,
fontSize: token.fontSize, fontSize: token.titleFontSizeSM,
}, },
}, },
}, },
@ -470,8 +610,8 @@ const genSizeStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
'&-large': { '&-large': {
[`> ${componentCls}-nav`]: { [`> ${componentCls}-nav`]: {
[`${componentCls}-tab`]: { [`${componentCls}-tab`]: {
padding: `${padding}px 0`, padding: horizontalItemPaddingLG,
fontSize: token.fontSizeLG, fontSize: token.titleFontSizeLG,
}, },
}, },
}, },
@ -481,24 +621,24 @@ const genSizeStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
[`&${componentCls}-small`]: { [`&${componentCls}-small`]: {
[`> ${componentCls}-nav`]: { [`> ${componentCls}-nav`]: {
[`${componentCls}-tab`]: { [`${componentCls}-tab`]: {
padding: `${token.paddingXXS * 1.5}px ${padding}px`, padding: cardPaddingSM,
}, },
}, },
[`&${componentCls}-bottom`]: { [`&${componentCls}-bottom`]: {
[`> ${componentCls}-nav ${componentCls}-tab`]: { [`> ${componentCls}-nav ${componentCls}-tab`]: {
borderRadius: `0 0 ${token.borderRadius}px ${token.borderRadius}px`, borderRadius: `0 0 ${unit(token.borderRadius)} ${unit(token.borderRadius)}`,
}, },
}, },
[`&${componentCls}-top`]: { [`&${componentCls}-top`]: {
[`> ${componentCls}-nav ${componentCls}-tab`]: { [`> ${componentCls}-nav ${componentCls}-tab`]: {
borderRadius: `${token.borderRadius}px ${token.borderRadius}px 0 0`, borderRadius: `${unit(token.borderRadius)} ${unit(token.borderRadius)} 0 0`,
}, },
}, },
[`&${componentCls}-right`]: { [`&${componentCls}-right`]: {
[`> ${componentCls}-nav ${componentCls}-tab`]: { [`> ${componentCls}-nav ${componentCls}-tab`]: {
borderRadius: { borderRadius: {
_skip_check_: true, _skip_check_: true,
value: `0 ${token.borderRadius}px ${token.borderRadius}px 0`, value: `0 ${unit(token.borderRadius)} ${unit(token.borderRadius)} 0`,
}, },
}, },
}, },
@ -506,7 +646,7 @@ const genSizeStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
[`> ${componentCls}-nav ${componentCls}-tab`]: { [`> ${componentCls}-nav ${componentCls}-tab`]: {
borderRadius: { borderRadius: {
_skip_check_: true, _skip_check_: true,
value: `${token.borderRadius}px 0 0 ${token.borderRadius}px`, value: `${unit(token.borderRadius)} 0 0 ${unit(token.borderRadius)}`,
}, },
}, },
}, },
@ -515,7 +655,7 @@ const genSizeStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
[`&${componentCls}-large`]: { [`&${componentCls}-large`]: {
[`> ${componentCls}-nav`]: { [`> ${componentCls}-nav`]: {
[`${componentCls}-tab`]: { [`${componentCls}-tab`]: {
padding: `${token.paddingXS}px ${padding}px ${token.paddingXXS * 1.5}px`, padding: cardPaddingLG,
}, },
}, },
}, },
@ -524,42 +664,56 @@ const genSizeStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
}; };
const genTabStyle: GenerateStyle<TabsToken, CSSObject> = (token: TabsToken) => { const genTabStyle: GenerateStyle<TabsToken, CSSObject> = (token: TabsToken) => {
const { componentCls, tabsActiveColor, tabsHoverColor, iconCls, tabsHorizontalGutter } = token; const {
componentCls,
itemActiveColor,
itemHoverColor,
iconCls,
tabsHorizontalItemMargin,
horizontalItemPadding,
itemSelectedColor,
itemColor,
} = token;
const tabCls = `${componentCls}-tab`; const tabCls = `${componentCls}-tab`;
return { return {
[tabCls]: { [tabCls]: {
position: 'relative', position: 'relative',
WebkitTouchCallout: 'none',
WebkitTapHighlightColor: 'transparent',
display: 'inline-flex', display: 'inline-flex',
alignItems: 'center', alignItems: 'center',
padding: `${token.paddingSM}px 0`, padding: horizontalItemPadding,
fontSize: `${token.fontSize}px`, fontSize: token.titleFontSize,
background: 'transparent', background: 'transparent',
border: 0, border: 0,
outline: 'none', outline: 'none',
cursor: 'pointer', cursor: 'pointer',
color: itemColor,
'&-btn, &-remove': { '&-btn, &-remove': {
'&:focus:not(:focus-visible), &:active': { '&:focus:not(:focus-visible), &:active': {
color: tabsActiveColor, color: itemActiveColor,
}, },
...genFocusStyle(token),
}, },
'&-btn': { '&-btn': {
outline: 'none', outline: 'none',
transition: 'all 0.3s', transition: `all ${token.motionDurationSlow}`,
[`${tabCls}-icon:not(:last-child)`]: {
marginInlineEnd: token.marginSM,
},
}, },
'&-remove': { '&-remove': {
flex: 'none', flex: 'none',
marginRight: { marginRight: {
_skip_check_: true, _skip_check_: true,
value: -token.marginXXS, value: token.calc(token.marginXXS).mul(-1).equal(),
}, },
marginLeft: { marginLeft: {
_skip_check_: true, _skip_check_: true,
value: token.marginXS, value: token.marginXS,
}, },
color: token.colorTextDescription, color: token.colorIcon,
fontSize: token.fontSizeSM, fontSize: token.fontSizeSM,
background: 'transparent', background: 'transparent',
border: 'none', border: 'none',
@ -569,16 +723,21 @@ const genTabStyle: GenerateStyle<TabsToken, CSSObject> = (token: TabsToken) => {
'&:hover': { '&:hover': {
color: token.colorTextHeading, color: token.colorTextHeading,
}, },
...genFocusStyle(token),
}, },
'&:hover': { '&:hover': {
color: tabsHoverColor, color: itemHoverColor,
}, },
[`&${tabCls}-active ${tabCls}-btn`]: { [`&${tabCls}-active ${tabCls}-btn`]: {
color: token.colorPrimary, color: itemSelectedColor,
textShadow: token.tabsActiveTextShadow, textShadow: token.tabsActiveTextShadow,
}, },
[`&${tabCls}-focus ${tabCls}-btn`]: {
...genFocusOutline(token),
},
[`&${tabCls}-disabled`]: { [`&${tabCls}-disabled`]: {
color: token.colorTextDisabled, color: token.colorTextDisabled,
cursor: 'not-allowed', cursor: 'not-allowed',
@ -591,7 +750,7 @@ const genTabStyle: GenerateStyle<TabsToken, CSSObject> = (token: TabsToken) => {
[`& ${tabCls}-remove ${iconCls}`]: { [`& ${tabCls}-remove ${iconCls}`]: {
margin: 0, margin: 0,
}, },
[iconCls]: { [`${iconCls}:not(:last-child)`]: {
marginRight: { marginRight: {
_skip_check_: true, _skip_check_: true,
value: token.marginSM, value: token.marginSM,
@ -602,14 +761,14 @@ const genTabStyle: GenerateStyle<TabsToken, CSSObject> = (token: TabsToken) => {
[`${tabCls} + ${tabCls}`]: { [`${tabCls} + ${tabCls}`]: {
margin: { margin: {
_skip_check_: true, _skip_check_: true,
value: `0 0 0 ${tabsHorizontalGutter}px`, value: tabsHorizontalItemMargin,
}, },
}, },
}; };
}; };
const genRtlStyle: GenerateStyle<TabsToken, CSSObject> = (token: TabsToken) => { const genRtlStyle: GenerateStyle<TabsToken, CSSObject> = (token: TabsToken) => {
const { componentCls, tabsHorizontalGutter, iconCls, tabsCardGutter } = token; const { componentCls, tabsHorizontalItemMarginRTL, iconCls, cardGutter, calc } = token;
const rtlCls = `${componentCls}-rtl`; const rtlCls = `${componentCls}-rtl`;
return { return {
[rtlCls]: { [rtlCls]: {
@ -619,7 +778,7 @@ const genRtlStyle: GenerateStyle<TabsToken, CSSObject> = (token: TabsToken) => {
[`${componentCls}-tab`]: { [`${componentCls}-tab`]: {
margin: { margin: {
_skip_check_: true, _skip_check_: true,
value: `0 0 0 ${tabsHorizontalGutter}px`, value: tabsHorizontalItemMarginRTL,
}, },
[`${componentCls}-tab:last-of-type`]: { [`${componentCls}-tab:last-of-type`]: {
@ -636,18 +795,18 @@ const genRtlStyle: GenerateStyle<TabsToken, CSSObject> = (token: TabsToken) => {
}, },
marginLeft: { marginLeft: {
_skip_check_: true, _skip_check_: true,
value: `${token.marginSM}px`, value: unit(token.marginSM),
}, },
}, },
[`${componentCls}-tab-remove`]: { [`${componentCls}-tab-remove`]: {
marginRight: { marginRight: {
_skip_check_: true, _skip_check_: true,
value: `${token.marginXS}px`, value: unit(token.marginXS),
}, },
marginLeft: { marginLeft: {
_skip_check_: true, _skip_check_: true,
value: `-${token.marginXXS}px`, value: unit(calc(token.marginXXS).mul(-1).equal()),
}, },
[iconCls]: { [iconCls]: {
@ -683,7 +842,7 @@ const genRtlStyle: GenerateStyle<TabsToken, CSSObject> = (token: TabsToken) => {
[`${componentCls}-tab + ${componentCls}-tab`]: { [`${componentCls}-tab + ${componentCls}-tab`]: {
marginRight: { marginRight: {
_skip_check_: true, _skip_check_: true,
value: `${tabsCardGutter}px`, value: cardGutter,
}, },
marginLeft: { _skip_check_: true, value: 0 }, marginLeft: { _skip_check_: true, value: 0 },
}, },
@ -709,12 +868,12 @@ const genRtlStyle: GenerateStyle<TabsToken, CSSObject> = (token: TabsToken) => {
const genTabsStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject => { const genTabsStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject => {
const { const {
componentCls, componentCls,
tabsCardHorizontalPadding, tabsCardPadding,
tabsCardHeight, cardHeight,
tabsCardGutter, cardGutter,
tabsHoverColor, itemHoverColor,
tabsActiveColor, itemActiveColor,
colorSplit, colorBorderSecondary,
} = token; } = token;
return { return {
@ -769,9 +928,10 @@ const genTabsStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
[`${componentCls}-nav-more`]: { [`${componentCls}-nav-more`]: {
position: 'relative', position: 'relative',
padding: tabsCardHorizontalPadding, padding: tabsCardPadding,
background: 'transparent', background: 'transparent',
border: 0, border: 0,
color: token.colorText,
'&::after': { '&::after': {
position: 'absolute', position: 'absolute',
@ -784,36 +944,36 @@ const genTabsStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
_skip_check_: true, _skip_check_: true,
value: 0, value: 0,
}, },
height: token.controlHeightLG / 8, height: token.calc(token.controlHeightLG).div(8).equal(),
transform: 'translateY(100%)', transform: 'translateY(100%)',
content: "''", content: "''",
}, },
}, },
[`${componentCls}-nav-add`]: { [`${componentCls}-nav-add`]: {
minWidth: `${tabsCardHeight}px`, minWidth: cardHeight,
marginLeft: { marginLeft: {
_skip_check_: true, _skip_check_: true,
value: `${tabsCardGutter}px`, value: cardGutter,
}, },
padding: `0 ${token.paddingXS}px`, padding: unit(token.paddingXS),
background: 'transparent', background: 'transparent',
border: `${token.lineWidth}px ${token.lineType} ${colorSplit}`, border: `${unit(token.lineWidth)} ${token.lineType} ${colorBorderSecondary}`,
borderRadius: `${token.borderRadiusLG}px ${token.borderRadiusLG}px 0 0`, borderRadius: `${unit(token.borderRadiusLG)} ${unit(token.borderRadiusLG)} 0 0`,
outline: 'none', outline: 'none',
cursor: 'pointer', cursor: 'pointer',
color: token.colorText, color: token.colorText,
transition: `all ${token.motionDurationSlow} ${token.motionEaseInOut}`, transition: `all ${token.motionDurationSlow} ${token.motionEaseInOut}`,
'&:hover': { '&:hover': {
color: tabsHoverColor, color: itemHoverColor,
}, },
'&:active, &:focus:not(:focus-visible)': { '&:active, &:focus:not(:focus-visible)': {
color: tabsActiveColor, color: itemActiveColor,
}, },
...genFocusStyle(token), ...genFocusStyle(token, -3),
}, },
}, },
@ -824,7 +984,7 @@ const genTabsStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
// ============================ InkBar ============================ // ============================ InkBar ============================
[`${componentCls}-ink-bar`]: { [`${componentCls}-ink-bar`]: {
position: 'absolute', position: 'absolute',
background: token.colorPrimary, background: token.inkBarColor,
pointerEvents: 'none', pointerEvents: 'none',
}, },
@ -834,12 +994,7 @@ const genTabsStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
// =========================== TabPanes =========================== // =========================== TabPanes ===========================
[`${componentCls}-content`]: { [`${componentCls}-content`]: {
position: 'relative', position: 'relative',
display: 'flex',
width: '100%', width: '100%',
['&-animated']: {
transition: 'margin 0.3s',
},
}, },
[`${componentCls}-content-holder`]: { [`${componentCls}-content-holder`]: {
@ -849,17 +1004,18 @@ const genTabsStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
}, },
[`${componentCls}-tabpane`]: { [`${componentCls}-tabpane`]: {
outline: 'none', ...genFocusStyle(token),
flex: 'none', '&-hidden': {
width: '100%', display: 'none',
},
}, },
}, },
[`${componentCls}-centered`]: { [`${componentCls}-centered`]: {
[`> ${componentCls}-nav, > div > ${componentCls}-nav`]: { [`> ${componentCls}-nav, > div > ${componentCls}-nav`]: {
[`${componentCls}-nav-wrap`]: { [`${componentCls}-nav-wrap`]: {
[`&:not([class*='${componentCls}-nav-wrap-ping'])`]: { [`&:not([class*='${componentCls}-nav-wrap-ping']) > ${componentCls}-nav-list`]: {
justifyContent: 'center', margin: 'auto',
}, },
}, },
}, },
@ -867,27 +1023,54 @@ const genTabsStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
}; };
}; };
export const prepareComponentToken: GetDefaultToken<'Tabs'> = token => {
const cardHeight = token.controlHeightLG;
return {
zIndexPopup: token.zIndexPopupBase + 50,
cardBg: token.colorFillAlter,
cardHeight,
// Initialize with empty string, because cardPadding will be calculated with cardHeight by default.
cardPadding: `${
(cardHeight - Math.round(token.fontSize * token.lineHeight)) / 2 - token.lineWidth
}px ${token.padding}px`,
cardPaddingSM: `${token.paddingXXS * 1.5}px ${token.padding}px`,
cardPaddingLG: `${token.paddingXS}px ${token.padding}px ${token.paddingXXS * 1.5}px`,
titleFontSize: token.fontSize,
titleFontSizeLG: token.fontSizeLG,
titleFontSizeSM: token.fontSize,
inkBarColor: token.colorPrimary,
horizontalMargin: `0 0 ${token.margin}px 0`,
horizontalItemGutter: 32, // Fixed Value
// Initialize with empty string, because horizontalItemMargin will be calculated with horizontalItemGutter by default.
horizontalItemMargin: ``,
horizontalItemMarginRTL: ``,
horizontalItemPadding: `${token.paddingSM}px 0`,
horizontalItemPaddingSM: `${token.paddingXS}px 0`,
horizontalItemPaddingLG: `${token.padding}px 0`,
verticalItemPadding: `${token.paddingXS}px ${token.paddingLG}px`,
verticalItemMargin: `${token.margin}px 0 0 0`,
itemColor: token.colorText,
itemSelectedColor: token.colorPrimary,
itemHoverColor: token.colorPrimaryHover,
itemActiveColor: token.colorPrimaryActive,
cardGutter: token.marginXXS / 2,
};
};
// ============================== Export ============================== // ============================== Export ==============================
export default genComponentStyleHook( export default genStyleHooks(
'Tabs', 'Tabs',
token => { token => {
const tabsCardHeight = token.controlHeightLG;
const tabsToken = mergeToken<TabsToken>(token, { const tabsToken = mergeToken<TabsToken>(token, {
tabsHoverColor: token.colorPrimaryHover, // `cardPadding` is empty by default, so we could calculate with dynamic `cardHeight`
tabsActiveColor: token.colorPrimaryActive, tabsCardPadding: token.cardPadding,
tabsCardHorizontalPadding: `${
(tabsCardHeight - Math.round(token.fontSize * token.lineHeight)) / 2 - token.lineWidth
}px ${token.padding}px`,
tabsCardHeight,
tabsCardGutter: token.marginXXS / 2,
tabsHorizontalGutter: 32, // Fixed Value
tabsCardHeadBackground: token.colorFillAlter,
dropdownEdgeChildVerticalPadding: token.paddingXXS, dropdownEdgeChildVerticalPadding: token.paddingXXS,
tabsActiveTextShadow: '0 0 0.25px currentcolor', tabsActiveTextShadow: '0 0 0.25px currentcolor',
tabsDropdownHeight: 200, tabsDropdownHeight: 200,
tabsDropdownWidth: 120, tabsDropdownWidth: 120,
tabsHorizontalItemMargin: `0 0 0 ${unit(token.horizontalItemGutter)}`,
tabsHorizontalItemMarginRTL: `0 0 0 ${unit(token.horizontalItemGutter)}`,
}); });
return [ return [
@ -900,7 +1083,5 @@ export default genComponentStyleHook(
genMotionStyle(tabsToken), genMotionStyle(tabsToken),
]; ];
}, },
token => ({ prepareComponentToken,
zIndexPopup: token.zIndexPopupBase + 50,
}),
); );