From 6ac95e1c32386e1d9a795723bd8347f04da9aaf3 Mon Sep 17 00:00:00 2001 From: nishu Date: Tue, 29 Apr 2025 16:30:53 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20QIANXING1-787=20card=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81=20css=20var?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/card/Card.tsx | 6 +- components/card/style/index.tsx | 257 ++++++++++++++++++++++---------- 2 files changed, 183 insertions(+), 80 deletions(-) diff --git a/components/card/Card.tsx b/components/card/Card.tsx index a5f83c8496..c05bdc97f7 100644 --- a/components/card/Card.tsx +++ b/components/card/Card.tsx @@ -7,6 +7,7 @@ import type { SizeType } from '../config-provider'; import isPlainObject from 'lodash-es/isPlainObject'; import useConfigInject from '../config-provider/hooks/useConfigInject'; import devWarning from '../vc-util/devWarning'; +import useCSSVarCls from '../config-provider/hooks/useCssVarCls'; import useStyle from './style'; import Skeleton from '../skeleton'; import type { CustomSlotsType } from '../_util/type'; @@ -67,7 +68,8 @@ const Card = defineComponent({ }>, setup(props, { slots, attrs }) { const { prefixCls, direction, size } = useConfigInject('card', props); - const [wrapSSR, hashId] = useStyle(prefixCls); + const rootCls = useCSSVarCls(prefixCls); + const [wrapSSR, hashId, cssVarCls] = useStyle(prefixCls, rootCls); const getAction = (actions: VNodeTypes[]) => { const actionList = actions.map((action, index) => (isVNode(action) && !isEmptyElement(action)) || !isVNode(action) ? ( @@ -112,6 +114,8 @@ const Card = defineComponent({ const pre = prefixCls.value; const classString = { [`${pre}`]: true, + [cssVarCls.value]: true, + [rootCls.value]: true, [hashId.value]: true, [`${pre}-loading`]: loading, [`${pre}-bordered`]: bordered, diff --git a/components/card/style/index.tsx b/components/card/style/index.tsx index 447bcd148f..15a418b9ea 100644 --- a/components/card/style/index.tsx +++ b/components/card/style/index.tsx @@ -1,19 +1,98 @@ +import { unit } from '../../_util/cssinjs'; import type { CSSObject } from '../../_util/cssinjs'; -import type { FullToken, GenerateStyle } from '../../theme/internal'; -import { genComponentStyleHook, mergeToken } from '../../theme/internal'; -import { clearFix, resetComponent, textEllipsis } from '../../style'; -export interface ComponentToken {} +import { clearFix, resetComponent, textEllipsis } from '../../style'; +import type { FullToken, GenerateStyle, GetDefaultToken } from '../../theme/internal'; +import { genStyleHooks, mergeToken } from '../../theme/internal'; + +export interface ComponentToken { + /** + * @desc 卡片头部背景色 + * @descEN Background color of card header + */ + headerBg: string; + /** + * @desc 卡片头部文字大小 + * @descEN Font size of card header + */ + headerFontSize: number | string; + /** + * @desc 小号卡片头部文字大小 + * @descEN Font size of small card header + */ + headerFontSizeSM: number | string; + /** + * @desc 卡片头部高度 + * @descEN Height of card header + */ + headerHeight: number | string; + /** + * @desc 小号卡片头部高度 + * @descEN Height of small card header + */ + headerHeightSM: number | string; + /** + * @desc 小号卡片内边距 + * @descEN Padding of small card body + */ + bodyPaddingSM: number; + /** + * @desc 小号卡片头部内边距 + * @descEN Padding of small card head + */ + headerPaddingSM: number; + /** + * @desc 卡片内边距 + * @descEN Padding of card body + */ + bodyPadding: number; + /** + * @desc 卡片头部内边距 + * @descEN Padding of card head + */ + headerPadding: number; + /** + * @desc 操作区背景色 + * @descEN Background color of card actions + */ + actionsBg: string; + /** + * @desc 操作区每一项的外间距 + * @descEN Margin of each item in card actions + */ + actionsLiMargin: string; + /** + * @desc 内置标签页组件下间距 + * @descEN Margin bottom of tabs component + */ + tabsMarginBottom: number; + /** + * @desc 额外区文字颜色 + * @descEN Text color of extra area + */ + extraColor: string; +} interface CardToken extends FullToken<'Card'> { - cardHeadHeight: number; - cardHeadHeightSM: number; + /** + * @desc 卡片阴影 + * @descEN Shadow of card + */ cardShadow: string; + /** + * @desc 卡片头部内边距 + * @descEN Padding of card header + */ cardHeadPadding: number; - cardPaddingSM: number; + /** + * @desc 卡片基础内边距 + * @descEN Padding of base card + */ cardPaddingBase: number; - cardHeadTabsMarginBottom: number; - cardActionsLiMargin: string; + /** + * @desc 卡片操作区图标大小 + * @descEN Size of card actions icon + */ cardActionsIconSize: number; } @@ -21,21 +100,21 @@ interface CardToken extends FullToken<'Card'> { // ============================== Head ============================== const genCardHeadStyle: GenerateStyle = (token): CSSObject => { - const { antCls, componentCls, cardHeadHeight, cardPaddingBase, cardHeadTabsMarginBottom } = token; + const { antCls, componentCls, headerHeight, headerPadding, tabsMarginBottom } = token; return { display: 'flex', justifyContent: 'center', flexDirection: 'column', - minHeight: cardHeadHeight, + minHeight: headerHeight, marginBottom: -1, // Fix card grid overflow bug: https://gw.alipayobjects.com/zos/rmsportal/XonYxBikwpgbqIQBeuhk.png - padding: `0 ${cardPaddingBase}px`, + padding: `0 ${unit(headerPadding)}`, color: token.colorTextHeading, fontWeight: token.fontWeightStrong, - fontSize: token.fontSizeLG, - background: 'transparent', - borderBottom: `${token.lineWidth}px ${token.lineType} ${token.colorBorderSecondary}`, - borderRadius: `${token.borderRadiusLG}px ${token.borderRadiusLG}px 0 0`, + fontSize: token.headerFontSize, + background: token.headerBg, + borderBottom: `${unit(token.lineWidth)} ${token.lineType} ${token.colorBorderSecondary}`, + borderRadius: `${unit(token.borderRadiusLG)} ${unit(token.borderRadiusLG)} 0 0`, ...clearFix(), @@ -62,13 +141,13 @@ const genCardHeadStyle: GenerateStyle = (token): CSSObject => { [`${antCls}-tabs-top`]: { clear: 'both', - marginBottom: cardHeadTabsMarginBottom, + marginBottom: tabsMarginBottom, color: token.colorText, fontWeight: 'normal', fontSize: token.fontSize, '&-bar': { - borderBottom: `${token.lineWidth}px ${token.lineType} ${token.colorBorderSecondary}`, + borderBottom: `${unit(token.lineWidth)} ${token.lineType} ${token.colorBorderSecondary}`, }, }, }; @@ -83,11 +162,11 @@ const genCardGridStyle: GenerateStyle = (token): CSSObject => { border: 0, borderRadius: 0, boxShadow: ` - ${lineWidth}px 0 0 0 ${colorBorderSecondary}, - 0 ${lineWidth}px 0 0 ${colorBorderSecondary}, - ${lineWidth}px ${lineWidth}px 0 0 ${colorBorderSecondary}, - ${lineWidth}px 0 0 0 ${colorBorderSecondary} inset, - 0 ${lineWidth}px 0 0 ${colorBorderSecondary} inset; + ${unit(lineWidth)} 0 0 0 ${colorBorderSecondary}, + 0 ${unit(lineWidth)} 0 0 ${colorBorderSecondary}, + ${unit(lineWidth)} ${unit(lineWidth)} 0 0 ${colorBorderSecondary}, + ${unit(lineWidth)} 0 0 0 ${colorBorderSecondary} inset, + 0 ${unit(lineWidth)} 0 0 ${colorBorderSecondary} inset; `, transition: `all ${token.motionDurationMid}`, @@ -101,27 +180,33 @@ const genCardGridStyle: GenerateStyle = (token): CSSObject => { // ============================== Actions ============================== const genCardActionsStyle: GenerateStyle = (token): CSSObject => { - const { componentCls, iconCls, cardActionsLiMargin, cardActionsIconSize, colorBorderSecondary } = - token; + const { + componentCls, + iconCls, + actionsLiMargin, + cardActionsIconSize, + colorBorderSecondary, + actionsBg, + } = token; return { margin: 0, padding: 0, listStyle: 'none', - background: token.colorBgContainer, - borderTop: `${token.lineWidth}px ${token.lineType} ${colorBorderSecondary}`, + background: actionsBg, + borderTop: `${unit(token.lineWidth)} ${token.lineType} ${colorBorderSecondary}`, display: 'flex', - borderRadius: `0 0 ${token.borderRadiusLG}px ${token.borderRadiusLG}px `, + borderRadius: `0 0 ${unit(token.borderRadiusLG)} ${unit(token.borderRadiusLG)}`, ...clearFix(), '& > li': { - margin: cardActionsLiMargin, + margin: actionsLiMargin, color: token.colorTextDescription, textAlign: 'center', '> span': { position: 'relative', display: 'block', - minWidth: token.cardActionsIconSize * 2, + minWidth: token.calc(token.cardActionsIconSize).mul(2).equal(), fontSize: token.fontSize, lineHeight: token.lineHeight, cursor: 'pointer', @@ -134,8 +219,8 @@ const genCardActionsStyle: GenerateStyle = (token): CSSObject => { [`a:not(${componentCls}-btn), > ${iconCls}`]: { display: 'inline-block', width: '100%', - color: token.colorTextDescription, - lineHeight: `${token.fontSize * token.lineHeight}px`, + color: token.colorIcon, + lineHeight: unit(token.fontHeight), transition: `color ${token.motionDurationMid}`, '&:hover': { @@ -145,12 +230,12 @@ const genCardActionsStyle: GenerateStyle = (token): CSSObject => { [`> ${iconCls}`]: { fontSize: cardActionsIconSize, - lineHeight: `${cardActionsIconSize * token.lineHeight}px`, + lineHeight: unit(token.calc(cardActionsIconSize).mul(token.lineHeight).equal()), }, }, '&:not(:last-child)': { - borderInlineEnd: `${token.lineWidth}px ${token.lineType} ${colorBorderSecondary}`, + borderInlineEnd: `${unit(token.lineWidth)} ${token.lineType} ${colorBorderSecondary}`, }, }, }; @@ -158,7 +243,7 @@ const genCardActionsStyle: GenerateStyle = (token): CSSObject => { // ============================== Meta ============================== const genCardMetaStyle: GenerateStyle = (token): CSSObject => ({ - margin: `-${token.marginXXS}px 0`, + margin: `${unit(token.calc(token.marginXXS).mul(-1).equal())} 0`, display: 'flex', ...clearFix(), @@ -189,11 +274,11 @@ const genCardMetaStyle: GenerateStyle = (token): CSSObject => ({ // ============================== Inner ============================== const genCardTypeInnerStyle: GenerateStyle = (token): CSSObject => { - const { componentCls, cardPaddingBase, colorFillAlter } = token; + const { componentCls, colorFillAlter, headerPadding, bodyPadding } = token; return { [`${componentCls}-head`]: { - padding: `0 ${cardPaddingBase}px`, + padding: `0 ${unit(headerPadding)}`, background: colorFillAlter, '&-title': { @@ -202,7 +287,7 @@ const genCardTypeInnerStyle: GenerateStyle = (token): CSSObject => { }, [`${componentCls}-body`]: { - padding: `${token.padding}px ${cardPaddingBase}px`, + padding: `${unit(token.padding)} ${unit(bodyPadding)}`, }, }; }; @@ -227,8 +312,9 @@ const genCardStyle: GenerateStyle = (token): CSSObject => { cardShadow, cardHeadPadding, colorBorderSecondary, - boxShadow, - cardPaddingBase, + boxShadowTertiary, + bodyPadding, + extraColor, } = token; return { @@ -240,7 +326,7 @@ const genCardStyle: GenerateStyle = (token): CSSObject => { borderRadius: token.borderRadiusLG, [`&:not(${componentCls}-bordered)`]: { - boxShadow, + boxShadow: boxShadowTertiary, }, [`${componentCls}-head`]: genCardHeadStyle(token), @@ -248,14 +334,14 @@ const genCardStyle: GenerateStyle = (token): CSSObject => { [`${componentCls}-extra`]: { // https://stackoverflow.com/a/22429853/3040605 marginInlineStart: 'auto', - color: '', + color: extraColor, fontWeight: 'normal', fontSize: token.fontSize, }, [`${componentCls}-body`]: { - padding: cardPaddingBase, - borderRadius: ` 0 0 ${token.borderRadiusLG}px ${token.borderRadiusLG}px`, + padding: bodyPadding, + borderRadius: `0 0 ${unit(token.borderRadiusLG)} ${unit(token.borderRadiusLG)}`, ...clearFix(), }, @@ -265,10 +351,7 @@ const genCardStyle: GenerateStyle = (token): CSSObject => { '> *': { display: 'block', width: '100%', - }, - - img: { - borderRadius: `${token.borderRadiusLG}px ${token.borderRadiusLG}px 0 0`, + borderRadius: `${unit(token.borderRadiusLG)} ${unit(token.borderRadiusLG)} 0 0`, }, }, @@ -278,7 +361,7 @@ const genCardStyle: GenerateStyle = (token): CSSObject => { }, [`${componentCls}-bordered`]: { - border: `${token.lineWidth}px ${token.lineType} ${colorBorderSecondary}`, + border: `${unit(token.lineWidth)} ${token.lineType} ${colorBorderSecondary}`, [`${componentCls}-cover`]: { marginTop: -1, @@ -298,20 +381,22 @@ const genCardStyle: GenerateStyle = (token): CSSObject => { }, [`${componentCls}-contain-grid`]: { + borderRadius: `${unit(token.borderRadiusLG)} ${unit(token.borderRadiusLG)} 0 0 `, [`${componentCls}-body`]: { display: 'flex', flexWrap: 'wrap', }, [`&:not(${componentCls}-loading) ${componentCls}-body`]: { - marginBlockStart: -token.lineWidth, - marginInlineStart: -token.lineWidth, + marginBlockStart: token.calc(token.lineWidth).mul(-1).equal(), + marginInlineStart: token.calc(token.lineWidth).mul(-1).equal(), padding: 0, }, }, [`${componentCls}-contain-tabs`]: { - [`> ${componentCls}-head`]: { + [`> div${componentCls}-head`]: { + minHeight: 0, [`${componentCls}-head-title, ${componentCls}-extra`]: { paddingTop: cardHeadPadding, }, @@ -330,14 +415,14 @@ const genCardStyle: GenerateStyle = (token): CSSObject => { // ============================== Size ============================== const genCardSizeStyle: GenerateStyle = (token): CSSObject => { - const { componentCls, cardPaddingSM, cardHeadHeightSM } = token; + const { componentCls, bodyPaddingSM, headerPaddingSM, headerHeightSM, headerFontSizeSM } = token; return { [`${componentCls}-small`]: { [`> ${componentCls}-head`]: { - minHeight: cardHeadHeightSM, - padding: `0 ${cardPaddingSM}px`, - fontSize: token.fontSize, + minHeight: headerHeightSM, + padding: `0 ${unit(headerPaddingSM)}`, + fontSize: headerFontSizeSM, [`> ${componentCls}-head-wrapper`]: { [`> ${componentCls}-extra`]: { @@ -347,13 +432,12 @@ const genCardSizeStyle: GenerateStyle = (token): CSSObject => { }, [`> ${componentCls}-body`]: { - padding: cardPaddingSM, + padding: bodyPaddingSM, }, }, [`${componentCls}-small${componentCls}-contain-tabs`]: { [`> ${componentCls}-head`]: { [`${componentCls}-head-title, ${componentCls}-extra`]: { - minHeight: cardHeadHeightSM, paddingTop: 0, display: 'flex', alignItems: 'center', @@ -363,25 +447,40 @@ const genCardSizeStyle: GenerateStyle = (token): CSSObject => { }; }; -// ============================== Export ============================== -export default genComponentStyleHook('Card', token => { - const cardToken = mergeToken(token, { - cardShadow: token.boxShadowCard, - cardHeadHeight: token.fontSizeLG * token.lineHeightLG + token.padding * 2, - cardHeadHeightSM: token.fontSize * token.lineHeight + token.paddingXS * 2, - cardHeadPadding: token.padding, - cardPaddingBase: token.paddingLG, - cardHeadTabsMarginBottom: -token.padding - token.lineWidth, - cardActionsLiMargin: `${token.paddingSM}px 0`, - cardActionsIconSize: token.fontSize, - cardPaddingSM: 12, // Fixed padding. - }); - - return [ - // Style - genCardStyle(cardToken), - - // Size - genCardSizeStyle(cardToken), - ]; +export const prepareComponentToken: GetDefaultToken<'Card'> = token => ({ + headerBg: 'transparent', + headerFontSize: token.fontSizeLG, + headerFontSizeSM: token.fontSize, + headerHeight: token.fontSizeLG * token.lineHeightLG + token.padding * 2, + headerHeightSM: token.fontSize * token.lineHeight + token.paddingXS * 2, + actionsBg: token.colorBgContainer, + actionsLiMargin: `${token.paddingSM}px 0`, + tabsMarginBottom: -token.padding - token.lineWidth, + extraColor: token.colorText, + bodyPaddingSM: 12, // Fixed padding. + headerPaddingSM: 12, + bodyPadding: token.bodyPadding ?? token.paddingLG, + headerPadding: token.headerPadding ?? token.paddingLG, }); + +// ============================== Export ============================== +export default genStyleHooks( + 'Card', + token => { + const cardToken = mergeToken(token, { + cardShadow: token.boxShadowCard, + cardHeadPadding: token.padding, + cardPaddingBase: token.paddingLG, + cardActionsIconSize: token.fontSize, + }); + + return [ + // Style + genCardStyle(cardToken), + + // Size + genCardSizeStyle(cardToken), + ]; + }, + prepareComponentToken, +);