import type { CSSObject } from '@ant-design/cssinjs'; import type { FullToken, GenerateStyle } from '../../theme/internal'; import { genComponentStyleHook, mergeToken } from '../../theme/internal'; import { clearFix, resetComponent, textEllipsis } from '../../style'; export interface ComponentToken {} interface CardToken extends FullToken<'Card'> { cardHeadHeight: number; cardHeadHeightSM: number; cardShadow: string; cardHeadPadding: number; cardPaddingSM: number; cardPaddingBase: number; cardHeadTabsMarginBottom: number; cardActionsLiMargin: string; cardActionsIconSize: number; } // ============================== Styles ============================== // ============================== Head ============================== const genCardHeadStyle: GenerateStyle = (token): CSSObject => { const { antCls, componentCls, cardHeadHeight, cardPaddingBase, cardHeadTabsMarginBottom } = token; return { display: 'flex', justifyContent: 'center', flexDirection: 'column', minHeight: cardHeadHeight, marginBottom: -1, // Fix card grid overflow bug: https://gw.alipayobjects.com/zos/rmsportal/XonYxBikwpgbqIQBeuhk.png padding: `0 ${cardPaddingBase}px`, 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`, ...clearFix(), '&-wrapper': { width: '100%', display: 'flex', alignItems: 'center', }, '&-title': { display: 'inline-block', flex: 1, ...textEllipsis, [` > ${componentCls}-typography, > ${componentCls}-typography-edit-content `]: { insetInlineStart: 0, marginTop: 0, marginBottom: 0, }, }, [`${antCls}-tabs-top`]: { clear: 'both', marginBottom: cardHeadTabsMarginBottom, color: token.colorText, fontWeight: 'normal', fontSize: token.fontSize, '&-bar': { borderBottom: `${token.lineWidth}px ${token.lineType} ${token.colorBorderSecondary}`, }, }, }; }; // ============================== Grid ============================== const genCardGridStyle: GenerateStyle = (token): CSSObject => { const { cardPaddingBase, colorBorderSecondary, cardShadow, lineWidth } = token; return { width: '33.33%', padding: cardPaddingBase, 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; `, transition: `all ${token.motionDurationMid}`, '&-hoverable:hover': { position: 'relative', zIndex: 1, boxShadow: cardShadow, }, }; }; // ============================== Actions ============================== const genCardActionsStyle: GenerateStyle = (token): CSSObject => { const { componentCls, iconCls, cardActionsLiMargin, cardActionsIconSize, colorBorderSecondary } = token; return { margin: 0, padding: 0, listStyle: 'none', background: token.colorBgContainer, borderTop: `${token.lineWidth}px ${token.lineType} ${colorBorderSecondary}`, display: 'flex', borderRadius: `0 0 ${token.borderRadiusLG}px ${token.borderRadiusLG}px `, ...clearFix(), '& > li': { margin: cardActionsLiMargin, color: token.colorTextDescription, textAlign: 'center', '> span': { position: 'relative', display: 'block', minWidth: token.cardActionsIconSize * 2, fontSize: token.fontSize, lineHeight: token.lineHeight, cursor: 'pointer', '&:hover': { color: token.colorPrimary, transition: `color ${token.motionDurationMid}`, }, [`a:not(${componentCls}-btn), > ${iconCls}`]: { display: 'inline-block', width: '100%', color: token.colorTextDescription, lineHeight: `${token.fontSize * token.lineHeight}px`, transition: `color ${token.motionDurationMid}`, '&:hover': { color: token.colorPrimary, }, }, [`> ${iconCls}`]: { fontSize: cardActionsIconSize, lineHeight: `${cardActionsIconSize * token.lineHeight}px`, }, }, '&:not(:last-child)': { borderInlineEnd: `${token.lineWidth}px ${token.lineType} ${colorBorderSecondary}`, }, }, }; }; // ============================== Meta ============================== const genCardMetaStyle: GenerateStyle = (token): CSSObject => ({ margin: `-${token.marginXXS}px 0`, display: 'flex', ...clearFix(), '&-avatar': { paddingInlineEnd: token.padding, }, '&-detail': { overflow: 'hidden', flex: 1, '> div:not(:last-child)': { marginBottom: token.marginXS, }, }, '&-title': { color: token.colorTextHeading, fontWeight: token.fontWeightStrong, fontSize: token.fontSizeLG, ...textEllipsis, }, '&-description': { color: token.colorTextDescription, }, }); // ============================== Inner ============================== const genCardTypeInnerStyle: GenerateStyle = (token): CSSObject => { const { componentCls, cardPaddingBase, colorFillAlter } = token; return { [`${componentCls}-head`]: { padding: `0 ${cardPaddingBase}px`, background: colorFillAlter, '&-title': { fontSize: token.fontSize, }, }, [`${componentCls}-body`]: { padding: `${token.padding}px ${cardPaddingBase}px`, }, }; }; // ============================== Loading ============================== const genCardLoadingStyle: GenerateStyle = (token): CSSObject => { const { componentCls } = token; return { overflow: 'hidden', [`${componentCls}-body`]: { userSelect: 'none', }, }; }; // ============================== Basic ============================== const genCardStyle: GenerateStyle = (token): CSSObject => { const { componentCls, cardShadow, cardHeadPadding, colorBorderSecondary, boxShadowTertiary, cardPaddingBase, } = token; return { [componentCls]: { ...resetComponent(token), position: 'relative', background: token.colorBgContainer, borderRadius: token.borderRadiusLG, [`&:not(${componentCls}-bordered)`]: { boxShadow: boxShadowTertiary, }, [`${componentCls}-head`]: genCardHeadStyle(token), [`${componentCls}-extra`]: { // https://stackoverflow.com/a/22429853/3040605 marginInlineStart: 'auto', color: '', fontWeight: 'normal', fontSize: token.fontSize, }, [`${componentCls}-body`]: { padding: cardPaddingBase, borderRadius: ` 0 0 ${token.borderRadiusLG}px ${token.borderRadiusLG}px`, ...clearFix(), }, [`${componentCls}-grid`]: genCardGridStyle(token), [`${componentCls}-cover`]: { '> *': { display: 'block', width: '100%', }, img: { borderRadius: `${token.borderRadiusLG}px ${token.borderRadiusLG}px 0 0`, }, }, [`${componentCls}-actions`]: genCardActionsStyle(token), [`${componentCls}-meta`]: genCardMetaStyle(token), }, [`${componentCls}-bordered`]: { border: `${token.lineWidth}px ${token.lineType} ${colorBorderSecondary}`, [`${componentCls}-cover`]: { marginTop: -1, marginInlineStart: -1, marginInlineEnd: -1, }, }, [`${componentCls}-hoverable`]: { cursor: 'pointer', transition: `box-shadow ${token.motionDurationMid}, border-color ${token.motionDurationMid}`, '&:hover': { borderColor: 'transparent', boxShadow: cardShadow, }, }, [`${componentCls}-contain-grid`]: { [`${componentCls}-body`]: { display: 'flex', flexWrap: 'wrap', }, [`&:not(${componentCls}-loading) ${componentCls}-body`]: { marginBlockStart: -token.lineWidth, marginInlineStart: -token.lineWidth, padding: 0, }, }, [`${componentCls}-contain-tabs`]: { [`> ${componentCls}-head`]: { [`${componentCls}-head-title, ${componentCls}-extra`]: { paddingTop: cardHeadPadding, }, }, }, [`${componentCls}-type-inner`]: genCardTypeInnerStyle(token), [`${componentCls}-loading`]: genCardLoadingStyle(token), [`${componentCls}-rtl`]: { direction: 'rtl', }, }; }; // ============================== Size ============================== const genCardSizeStyle: GenerateStyle = (token): CSSObject => { const { componentCls, cardPaddingSM, cardHeadHeightSM } = token; return { [`${componentCls}-small`]: { [`> ${componentCls}-head`]: { minHeight: cardHeadHeightSM, padding: `0 ${cardPaddingSM}px`, fontSize: token.fontSize, [`> ${componentCls}-head-wrapper`]: { [`> ${componentCls}-extra`]: { fontSize: token.fontSize, }, }, }, [`> ${componentCls}-body`]: { padding: cardPaddingSM, }, }, [`${componentCls}-small${componentCls}-contain-tabs`]: { [`> ${componentCls}-head`]: { [`${componentCls}-head-title, ${componentCls}-extra`]: { minHeight: cardHeadHeightSM, paddingTop: 0, display: 'flex', alignItems: 'center', }, }, }, }; }; // ============================== 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), ]; });