diff --git a/components/_util/colors.ts b/components/_util/colors.ts index 8a3a6714ac74..5c21c663cc78 100644 --- a/components/_util/colors.ts +++ b/components/_util/colors.ts @@ -1,3 +1,9 @@ +import type { PresetColorKey } from '../theme/interface'; +import { PresetColors } from '../theme/interface'; + +type InverseColor = `${PresetColorKey}-inverse`; +const inverseColors = PresetColors.map((color) => `${color}-inverse`); + export const PresetStatusColorTypes = [ 'success', 'processing', @@ -6,22 +12,23 @@ export const PresetStatusColorTypes = [ 'warning', ] as const; -export const PresetColorTypes = [ - 'pink', - 'red', - 'yellow', - 'orange', - 'cyan', - 'green', - 'blue', - 'purple', - 'geekblue', - 'magenta', - 'volcano', - 'gold', - 'lime', -] as const; - -export type PresetColorType = typeof PresetColorTypes[number]; +export type PresetColorType = PresetColorKey | InverseColor; export type PresetStatusColorType = typeof PresetStatusColorTypes[number]; + +/** + * determine if the color keyword belongs to the `Ant Design` {@link PresetColors}. + * @param color color to be judged + * @param includeInverse whether to include reversed colors + */ +export function isPresetColor(color?: any, includeInverse = true) { + if (includeInverse) { + return [...inverseColors, ...PresetColors].includes(color); + } + + return PresetColors.includes(color); +} + +export function isPresetStatusColor(color?: any): color is PresetStatusColorType { + return PresetStatusColorTypes.includes(color); +} diff --git a/components/badge/Ribbon.tsx b/components/badge/Ribbon.tsx index 08e6b12fc717..3fce7008bc6a 100644 --- a/components/badge/Ribbon.tsx +++ b/components/badge/Ribbon.tsx @@ -4,7 +4,7 @@ import { ConfigContext } from '../config-provider'; import type { PresetColorType } from '../_util/colors'; import type { LiteralUnion } from '../_util/type'; import useStyle from './style'; -import { isPresetColor } from './utils'; +import { isPresetColor } from '../_util/colors'; type RibbonPlacement = 'start' | 'end'; @@ -29,7 +29,7 @@ const Ribbon: React.FC = function Ribbon({ }) { const { getPrefixCls, direction } = React.useContext(ConfigContext); const prefixCls = getPrefixCls('ribbon', customizePrefixCls); - const colorInPreset = isPresetColor(color); + const colorInPreset = isPresetColor(color, false); const ribbonCls = classNames( prefixCls, `${prefixCls}-placement-${placement}`, diff --git a/components/badge/index.tsx b/components/badge/index.tsx index d61f9e654994..28395bab51f9 100644 --- a/components/badge/index.tsx +++ b/components/badge/index.tsx @@ -3,13 +3,14 @@ import CSSMotion from 'rc-motion'; import * as React from 'react'; import { useMemo, useRef } from 'react'; import { ConfigContext } from '../config-provider'; -import type { PresetColorType, PresetStatusColorType } from '../_util/colors'; +import type { PresetStatusColorType } from '../_util/colors'; import { cloneElement } from '../_util/reactNode'; import type { LiteralUnion } from '../_util/type'; import Ribbon from './Ribbon'; import ScrollNumber from './ScrollNumber'; import useStyle from './style'; -import { isPresetColor } from './utils'; +import { isPresetColor } from '../_util/colors'; +import type { PresetColorKey } from '../theme/internal'; export type { ScrollNumberProps } from './ScrollNumber'; @@ -30,7 +31,7 @@ export interface BadgeProps { scrollNumberPrefixCls?: string; className?: string; status?: PresetStatusColorType; - color?: LiteralUnion; + color?: LiteralUnion; text?: React.ReactNode; size?: 'default' | 'small'; offset?: [number | string, number | string]; @@ -144,15 +145,18 @@ const Badge: CompoundedComponent = ({ }, })); + // InternalColor + const isInternalColor = isPresetColor(color, false); + // Shared styles const statusCls = classNames({ [`${prefixCls}-status-dot`]: hasStatus, [`${prefixCls}-status-${status}`]: !!status, - [`${prefixCls}-status-${color}`]: isPresetColor(color), + [`${prefixCls}-status-${color}`]: isInternalColor, }); const statusStyle: React.CSSProperties = {}; - if (color && !isPresetColor(color)) { + if (color && !isInternalColor) { statusStyle.color = color; statusStyle.background = color; } @@ -207,11 +211,11 @@ const Badge: CompoundedComponent = ({ [`${prefixCls}-multiple-words`]: !isDot && displayCount && displayCount.toString().length > 1, [`${prefixCls}-status-${status}`]: !!status, - [`${prefixCls}-status-${color}`]: isPresetColor(color), + [`${prefixCls}-status-${color}`]: isInternalColor, }); let scrollNumberStyle: React.CSSProperties = { ...mergedStyle }; - if (color && !isPresetColor(color)) { + if (color && !isInternalColor) { scrollNumberStyle = scrollNumberStyle || {}; scrollNumberStyle.background = color; } diff --git a/components/badge/style/index.ts b/components/badge/style/index.ts index 89b51f4835c5..131da83c4c5b 100644 --- a/components/badge/style/index.ts +++ b/components/badge/style/index.ts @@ -1,8 +1,8 @@ import type { CSSObject } from '@ant-design/cssinjs'; import { Keyframes } from '@ant-design/cssinjs'; -import type { FullToken, GenerateStyle, PresetColorType } from '../../theme/internal'; -import { genComponentStyleHook, mergeToken, PresetColors } from '../../theme/internal'; -import { resetComponent } from '../../style'; +import type { FullToken, GenerateStyle } from '../../theme/internal'; +import { genComponentStyleHook, mergeToken } from '../../theme/internal'; +import { genPresetColor, resetComponent } from '../../style'; interface BadgeToken extends FullToken<'Badge'> { badgeFontHeight: number; @@ -73,28 +73,18 @@ const genSharedBadgeStyle: GenerateStyle = (token: BadgeToken): CSSO const ribbonPrefixCls = `${antCls}-ribbon`; const ribbonWrapperPrefixCls = `${antCls}-ribbon-wrapper`; - const statusPreset = PresetColors.reduce((prev: CSSObject, colorKey: keyof PresetColorType) => { - const darkColor = token[`${colorKey}-6`]; - return { - ...prev, - [`${componentCls}-status-${colorKey}`]: { - background: darkColor, - }, - }; - }, {} as CSSObject); - const statusRibbonPreset = PresetColors.reduce( - (prev: CSSObject, colorKey: keyof PresetColorType) => { - const darkColor = token[`${colorKey}-6`]; - return { - ...prev, - [`&${ribbonPrefixCls}-color-${colorKey}`]: { - background: darkColor, - color: darkColor, - }, - }; + const statusPreset = genPresetColor(token, (colorKey, { darkColor }) => ({ + [`${componentCls}-status-${colorKey}`]: { + background: darkColor, + }, + })); + + const statusRibbonPreset = genPresetColor(token, (colorKey, { darkColor }) => ({ + [`&${ribbonPrefixCls}-color-${colorKey}`]: { + background: darkColor, + color: darkColor, }, - {} as CSSObject, - ); + })); return { [componentCls]: { diff --git a/components/badge/utils.ts b/components/badge/utils.ts deleted file mode 100644 index bb495314e7d3..000000000000 --- a/components/badge/utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { PresetColorTypes } from '../_util/colors'; - -// eslint-disable-next-line import/prefer-default-export -export function isPresetColor(color?: string): boolean { - return PresetColorTypes.includes(color as any); -} diff --git a/components/style/index.ts b/components/style/index.ts index 743ac86d504e..730216928112 100644 --- a/components/style/index.ts +++ b/components/style/index.ts @@ -4,6 +4,7 @@ import type { DerivativeToken } from '../theme/internal'; export { operationUnit } from './operationUnit'; export { roundedArrow } from './roundedArrow'; +export { genPresetColor } from './presetColor'; export const textEllipsis: CSSObject = { overflow: 'hidden', diff --git a/components/style/presetColor.tsx b/components/style/presetColor.tsx new file mode 100644 index 000000000000..0704bd4f8ecf --- /dev/null +++ b/components/style/presetColor.tsx @@ -0,0 +1,35 @@ +/* eslint-disable import/prefer-default-export */ +import type { CSSObject } from '@ant-design/cssinjs'; +import type { AliasToken, PresetColorKey } from '../theme/internal'; +import { PresetColors } from '../theme/internal'; +import type { TokenWithCommonCls } from '../theme/util/genComponentStyleHook'; + +interface CalcColor { + /** token[`${colorKey}-1`] */ + lightColor: string; + /** token[`${colorKey}-3`] */ + lightBorderColor: string; + /** token[`${colorKey}-6`] */ + darkColor: string; + /** token[`${colorKey}-7`] */ + textColor: string; +} + +type GenCSS = (colorKey: PresetColorKey, calcColor: CalcColor) => CSSObject; + +export function genPresetColor>( + token: Token, + genCss: GenCSS, +): CSSObject { + return PresetColors.reduce((prev: CSSObject, colorKey: PresetColorKey) => { + const lightColor = token[`${colorKey}-1`]; + const lightBorderColor = token[`${colorKey}-3`]; + const darkColor = token[`${colorKey}-6`]; + const textColor = token[`${colorKey}-7`]; + + return { + ...prev, + ...genCss(colorKey, { lightColor, lightBorderColor, darkColor, textColor }), + }; + }, {} as CSSObject); +} diff --git a/components/tag/index.tsx b/components/tag/index.tsx index c543d886af9f..09997d5e0d99 100644 --- a/components/tag/index.tsx +++ b/components/tag/index.tsx @@ -3,7 +3,7 @@ import classNames from 'classnames'; import * as React from 'react'; import { ConfigContext } from '../config-provider'; import type { PresetColorType, PresetStatusColorType } from '../_util/colors'; -import { PresetColorTypes, PresetStatusColorTypes } from '../_util/colors'; +import { isPresetColor, isPresetStatusColor } from '../_util/colors'; import Wave from '../_util/wave'; import warning from '../_util/warning'; import CheckableTag from './CheckableTag'; @@ -25,9 +25,6 @@ export interface TagProps extends React.HTMLAttributes { icon?: React.ReactNode; } -const PresetColorRegex = new RegExp(`^(${PresetColorTypes.join('|')})(-inverse)?$`); -const PresetStatusColorRegex = new RegExp(`^(${PresetStatusColorTypes.join('|')})$`); - export interface TagType extends React.ForwardRefExoticComponent> { CheckableTag: typeof CheckableTag; @@ -66,19 +63,13 @@ const InternalTag: React.ForwardRefRenderFunction = ( } }, [props.visible]); - const isPresetColor = (): boolean => { - if (!color) { - return false; - } - return PresetColorRegex.test(color) || PresetStatusColorRegex.test(color); - }; + const isInternalColor = isPresetColor(color) || isPresetStatusColor(color); const tagStyle = { - backgroundColor: color && !isPresetColor() ? color : undefined, + backgroundColor: color && !isInternalColor ? color : undefined, ...style, }; - const presetColor = isPresetColor(); const prefixCls = getPrefixCls('tag', customizePrefixCls); // Style const [wrapSSR, hashId] = useStyle(prefixCls); @@ -86,8 +77,8 @@ const InternalTag: React.ForwardRefRenderFunction = ( const tagClassName = classNames( prefixCls, { - [`${prefixCls}-${color}`]: presetColor, - [`${prefixCls}-has-color`]: color && !presetColor, + [`${prefixCls}-${color}`]: isInternalColor, + [`${prefixCls}-has-color`]: color && !isInternalColor, [`${prefixCls}-hidden`]: !visible, [`${prefixCls}-rtl`]: direction === 'rtl', }, diff --git a/components/tag/style/index.ts b/components/tag/style/index.ts index 649a15cd6aff..16379c4891ba 100644 --- a/components/tag/style/index.ts +++ b/components/tag/style/index.ts @@ -1,9 +1,9 @@ -import type { CSSInterpolation, CSSObject } from '@ant-design/cssinjs'; +import type { CSSInterpolation } from '@ant-design/cssinjs'; import type React from 'react'; -import type { FullToken, PresetColorType } from '../../theme/internal'; -import { genComponentStyleHook, mergeToken, PresetColors } from '../../theme/internal'; +import type { FullToken } from '../../theme/internal'; +import { genComponentStyleHook, mergeToken } from '../../theme/internal'; import capitalize from '../../_util/capitalize'; -import { resetComponent } from '../../style'; +import { genPresetColor, resetComponent } from '../../style'; export interface ComponentToken {} @@ -35,27 +35,21 @@ const genTagStatusStyle = ( }; }; -// FIXME: special preset colors -const genTagColorStyle = (token: TagToken): CSSInterpolation => - PresetColors.reduce((prev: CSSObject, colorKey: keyof PresetColorType) => { - const lightColor = token[`${colorKey}-1`]; - const lightBorderColor = token[`${colorKey}-3`]; - const darkColor = token[`${colorKey}-6`]; - const textColor = token[`${colorKey}-7`]; - return { - ...prev, - [`${token.componentCls}-${colorKey}`]: { - color: textColor, - background: lightColor, - borderColor: lightBorderColor, - }, - [`${token.componentCls}-${colorKey}-inverse`]: { +const genPresetStyle = (token: TagToken) => + genPresetColor(token, (colorKey, { textColor, lightBorderColor, lightColor, darkColor }) => ({ + [`${token.componentCls}-${colorKey}`]: { + color: textColor, + background: lightColor, + borderColor: lightBorderColor, + + // Inverse color + '&-inverse': { color: token.colorTextLightSolid, background: darkColor, borderColor: darkColor, }, - }; - }, {} as CSSObject); + }, + })); const genBaseStyle = (token: TagToken): CSSInterpolation => { const { paddingXXS, lineWidth, tagPaddingHorizontal } = token; @@ -168,7 +162,7 @@ export default genComponentStyleHook('Tag', (token) => { return [ genBaseStyle(tagToken), - genTagColorStyle(tagToken), + genPresetStyle(tagToken), genTagStatusStyle(tagToken, 'success', 'Success'), genTagStatusStyle(tagToken, 'processing', 'Info'), genTagStatusStyle(tagToken, 'error', 'Error'), diff --git a/components/theme/interface/index.ts b/components/theme/interface/index.ts index 4c168b9fb2db..a63b53ca2893 100644 --- a/components/theme/interface/index.ts +++ b/components/theme/interface/index.ts @@ -9,7 +9,7 @@ export type OverrideToken = { export type GlobalToken = AliasToken & ComponentTokenMap; export { PresetColors } from './presetColors'; -export type { PresetColorType, ColorPalettes } from './presetColors'; +export type { PresetColorType, ColorPalettes, PresetColorKey } from './presetColors'; export type { SeedToken } from './seeds'; export type { MapToken, diff --git a/components/theme/interface/presetColors.ts b/components/theme/interface/presetColors.ts index 3f1fc59676f2..2cff21512c25 100644 --- a/components/theme/interface/presetColors.ts +++ b/components/theme/interface/presetColors.ts @@ -14,7 +14,7 @@ export const PresetColors = [ 'gold', ] as const; -type PresetColorKey = typeof PresetColors[number]; +export type PresetColorKey = typeof PresetColors[number]; export type PresetColorType = Record; diff --git a/components/theme/internal.ts b/components/theme/internal.ts index 7c04b5f170b5..24f378a51823 100644 --- a/components/theme/internal.ts +++ b/components/theme/internal.ts @@ -8,6 +8,7 @@ import type { MapToken, OverrideToken, PresetColorType, + PresetColorKey, SeedToken, } from './interface'; import { PresetColors } from './interface'; @@ -35,6 +36,7 @@ export type { SeedToken, AliasToken, PresetColorType, + PresetColorKey, // FIXME: Remove this type AliasToken as DerivativeToken, FullToken, diff --git a/components/tooltip/style/index.ts b/components/tooltip/style/index.ts index 65f932cb2635..5304ba7cbfec 100644 --- a/components/tooltip/style/index.ts +++ b/components/tooltip/style/index.ts @@ -1,13 +1,7 @@ -import type { CSSObject } from '@ant-design/cssinjs'; import { initZoomMotion } from '../../style/motion'; -import type { - FullToken, - GenerateStyle, - PresetColorType, - UseComponentStyleResult, -} from '../../theme/internal'; -import { genComponentStyleHook, mergeToken, PresetColors } from '../../theme/internal'; -import { resetComponent } from '../../style'; +import type { FullToken, GenerateStyle, UseComponentStyleResult } from '../../theme/internal'; +import { genComponentStyleHook, mergeToken } from '../../theme/internal'; +import { genPresetColor, resetComponent } from '../../style'; import getArrowStyle, { MAX_VERTICAL_CONTENT_RADIUS } from '../../style/placementArrow'; export interface ComponentToken { @@ -24,23 +18,6 @@ interface TooltipToken extends FullToken<'Tooltip'> { tooltipRadiusOuter: number; } -const generatorTooltipPresetColor: GenerateStyle = (token) => { - const { componentCls } = token; - - return PresetColors.reduce((previousValue: any, currentValue: keyof PresetColorType) => { - const lightColor = token[`${currentValue}-6`]; - previousValue[`&${componentCls}-${currentValue}`] = { - [`${componentCls}-inner`]: { - backgroundColor: lightColor, - }, - [`${componentCls}-arrow`]: { - '--antd-arrow-background-color': lightColor, - }, - }; - return previousValue; - }, {}); -}; - const genTooltipStyle: GenerateStyle = (token) => { const { componentCls, // ant-tooltip @@ -105,7 +82,16 @@ const genTooltipStyle: GenerateStyle = (token) => { }, // generator for preset color - ...generatorTooltipPresetColor(token), + ...genPresetColor(token, (colorKey, { darkColor }) => ({ + [`&${componentCls}-${colorKey}`]: { + [`${componentCls}-inner`]: { + backgroundColor: darkColor, + }, + [`${componentCls}-arrow`]: { + '--antd-arrow-background-color': darkColor, + }, + }, + })), // RTL '&-rtl': { diff --git a/components/tooltip/util.ts b/components/tooltip/util.ts index 49af5026172e..7fa1f65b2e40 100644 --- a/components/tooltip/util.ts +++ b/components/tooltip/util.ts @@ -2,19 +2,19 @@ import type * as React from 'react'; import classNames from 'classnames'; -import { PresetColorTypes } from '../_util/colors'; - -const PresetColorRegex = new RegExp(`^(${PresetColorTypes.join('|')})(-inverse)?$`); +import { isPresetColor } from '../_util/colors'; export function parseColor(prefixCls: string, color?: string) { + const isInternalColor = isPresetColor(color); + const className = classNames({ - [`${prefixCls}-${color}`]: color && PresetColorRegex.test(color), + [`${prefixCls}-${color}`]: color && isInternalColor, }); const overlayStyle: React.CSSProperties = {}; const arrowStyle: React.CSSProperties = {}; - if (color && !PresetColorRegex.test(color)) { + if (color && !isInternalColor) { overlayStyle.background = color; // @ts-ignore arrowStyle['--antd-arrow-background-color'] = color;