diff --git a/components/_util/colors.ts b/components/_util/colors.ts new file mode 100644 index 0000000000..acac6caec6 --- /dev/null +++ b/components/_util/colors.ts @@ -0,0 +1,18 @@ +import { tuple } from './type'; + +export const PresetColorTypes = tuple( + 'pink', + 'red', + 'yellow', + 'orange', + 'cyan', + 'green', + 'blue', + 'purple', + 'geekblue', + 'magenta', + 'volcano', + 'gold', + 'lime' +); +export type PresetColorType = (typeof PresetColorTypes)[number]; diff --git a/components/badge/__tests__/__snapshots__/demo.test.js.snap b/components/badge/__tests__/__snapshots__/demo.test.js.snap index fbf8fc85f9..ed6f89e39c 100644 --- a/components/badge/__tests__/__snapshots__/demo.test.js.snap +++ b/components/badge/__tests__/__snapshots__/demo.test.js.snap @@ -475,6 +475,262 @@ exports[`renders ./components/badge/demo/change.md correctly 1`] = ` `; +exports[`renders ./components/badge/demo/colorful.md correctly 1`] = ` +
+

+ Presets: +

+
+
+ + + + pink + + +
+
+ + + + red + + +
+
+ + + + yellow + + +
+
+ + + + orange + + +
+
+ + + + cyan + + +
+
+ + + + green + + +
+
+ + + + blue + + +
+
+ + + + purple + + +
+
+ + + + geekblue + + +
+
+ + + + magenta + + +
+
+ + + + volcano + + +
+
+ + + + gold + + +
+
+ + + + lime + + +
+
+

+ Custom: +

+
+ + + + #f50 + + +
+ + + + #2db7f5 + + +
+ + + + #87d068 + + +
+ + + + #108ee9 + + +
+
+`; + exports[`renders ./components/badge/demo/dot.md correctly 1`] = `
+

Presets:

+
+ {colors.map((color) => ( +
+ +
+ ))} +
+

Custom:

+
+ +
+ +
+ +
+ +
+
, + mountNode +); +```` + +````css +.ant-tag { + margin-bottom: 8px; +} +```` diff --git a/components/badge/index.en-US.md b/components/badge/index.en-US.md index 84981843d4..a337cf256b 100644 --- a/components/badge/index.en-US.md +++ b/components/badge/index.en-US.md @@ -22,13 +22,14 @@ Badge normally appears in proximity to notifications or user avatars with eye-ca ``` -| Property | Description | Type | Default | -| -------- | ----------- | ---- | ------- | -| count | Number to show in badge | ReactNode | | -| dot | Whether to display a red dot instead of `count` | boolean | `false` | -| offset | set offset of the badge dot, like`[x, y]` | `[number, number]` | - | -| overflowCount | Max count to show | number | 99 | -| showZero | Whether to show badge when `count` is zero | boolean | `false` | -| status | Set Badge as a status dot | `success` \| `processing` \| `default` \| `error` \| `warning` | `''` | -| text | If `status` is set, `text` sets the display text of the status `dot` | string | `''` | -| title | Text to show when hovering over the badge | string | `count` | +| Property | Description | Type | Default | Version | +| -------- | ----------- | ---- | ------- | ------- | +| color | Customize Badge dot color | string | - | 3.16.0 | +| count | Number to show in badge | ReactNode | | | +| dot | Whether to display a red dot instead of `count` | boolean | `false` | | +| offset | set offset of the badge dot, like`[x, y]` | `[number, number]` | - | | +| overflowCount | Max count to show | number | 99 | | +| showZero | Whether to show badge when `count` is zero | boolean | `false` | | +| status | Set Badge as a status dot | `success` \| `processing` \| `default` \| `error` \| `warning` | `''` | | +| text | If `status` is set, `text` sets the display text of the status `dot` | string | `''` | | +| title | Text to show when hovering over the badge | string | `count` | | diff --git a/components/badge/index.tsx b/components/badge/index.tsx index 381bf72453..2c5060c924 100644 --- a/components/badge/index.tsx +++ b/components/badge/index.tsx @@ -3,6 +3,7 @@ import * as PropTypes from 'prop-types'; import Animate from 'rc-animate'; import classNames from 'classnames'; import ScrollNumber from './ScrollNumber'; +import { PresetColorTypes } from '../_util/colors'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; export { ScrollNumberProps } from './ScrollNumber'; @@ -20,11 +21,16 @@ export interface BadgeProps { scrollNumberPrefixCls?: string; className?: string; status?: 'success' | 'processing' | 'default' | 'error' | 'warning'; + color?: string; text?: React.ReactNode; offset?: [number | string, number | string]; title?: string; } +function isPresetColor(color?: string): boolean { + return (PresetColorTypes as any[]).indexOf(color) !== -1; +} + export default class Badge extends React.Component { static defaultProps = { count: null, @@ -41,22 +47,27 @@ export default class Badge extends React.Component { }; getBadgeClassName(prefixCls: string) { - const { className, status, children } = this.props; + const { className, children } = this.props; return classNames(className, prefixCls, { - [`${prefixCls}-status`]: !!status, + [`${prefixCls}-status`]: this.hasStatus(), [`${prefixCls}-not-a-wrapper`]: !children, }) as string; } + hasStatus(): boolean { + const { status, color } = this.props; + return !!status || !!color; + } + isZero() { const numberedDispayCount = this.getNumberedDispayCount(); return numberedDispayCount === '0' || numberedDispayCount === 0; } isDot() { - const { dot, status } = this.props; + const { dot } = this.props; const isZero = this.isZero(); - return (dot && !isZero) || status; + return (dot && !isZero) || this.hasStatus(); } isHidden() { @@ -124,7 +135,7 @@ export default class Badge extends React.Component { } renderBadgeNumber(prefixCls: string, scrollNumberPrefixCls: string) { - const { count, status } = this.props; + const { status, count } = this.props; const displayCount = this.getDispayCount(); const isDot = this.isDot(); @@ -135,7 +146,7 @@ export default class Badge extends React.Component { [`${prefixCls}-count`]: !isDot, [`${prefixCls}-multiple-words`]: !isDot && count && count.toString && count.toString().length > 1, - [`${prefixCls}-status-${status}`]: !!status, + [`${prefixCls}-status-${status}`]: this.hasStatus(), }); return hidden ? null : ( @@ -167,6 +178,7 @@ export default class Badge extends React.Component { text, offset, title, + color, ...restProps } = this.props; @@ -177,17 +189,22 @@ export default class Badge extends React.Component { const statusText = this.renderStatusText(prefixCls); const statusCls = classNames({ - [`${prefixCls}-status-dot`]: !!status, + [`${prefixCls}-status-dot`]: this.hasStatus(), [`${prefixCls}-status-${status}`]: !!status, + [`${prefixCls}-status-${color}`]: isPresetColor(color), }); + const statusStyle: React.CSSProperties = {}; + if (color && !isPresetColor(color)) { + statusStyle.background = color; + } // - if (!children && status) { + if (!children && this.hasStatus()) { const styleWithOffset = this.getStyleWithOffset(); const statusTextColor = styleWithOffset && styleWithOffset.color; return ( - + {text} diff --git a/components/badge/index.zh-CN.md b/components/badge/index.zh-CN.md index 967b78b66c..18f70c7ed9 100644 --- a/components/badge/index.zh-CN.md +++ b/components/badge/index.zh-CN.md @@ -23,13 +23,14 @@ title: Badge ``` -| 参数 | 说明 | 类型 | 默认值 | -| --- | --- | --- | --- | -| count | 展示的数字,大于 overflowCount 时显示为 `${overflowCount}+`,为 0 时隐藏 | ReactNode | | -| dot | 不展示数字,只有一个小红点 | boolean | false | -| offset | 设置状态点的位置偏移,格式为 `[x, y]` | `[number, number]` | - | -| overflowCount | 展示封顶的数字值 | number | 99 | -| showZero | 当数值为 0 时,是否展示 Badge | boolean | false | -| status | 设置 Badge 为状态点 | Enum{ 'success', 'processing, 'default', 'error', 'warning' } | '' | -| text | 在设置了 `status` 的前提下有效,设置状态点的文本 | string | '' | -| title | 设置鼠标放在状态点上时显示的文字 | string | `count` | +| 参数 | 说明 | 类型 | 默认值 | 版本 | +| --- | --- | --- | --- | --- | +| color | 自定义小圆点的颜色 | string | - | 3.16.0 | +| count | 展示的数字,大于 overflowCount 时显示为 `${overflowCount}+`,为 0 时隐藏 | ReactNode | | | +| dot | 不展示数字,只有一个小红点 | boolean | false | | +| offset | 设置状态点的位置偏移,格式为 `[x, y]` | `[number, number]` | - | | +| overflowCount | 展示封顶的数字值 | number | 99 | | +| showZero | 当数值为 0 时,是否展示 Badge | boolean | false | | +| status | 设置 Badge 为状态点 | Enum{ 'success', 'processing, 'default', 'error', 'warning' } | '' | | +| text | 在设置了 `status` 的前提下有效,设置状态点的文本 | string | '' | | +| title | 设置鼠标放在状态点上时显示的文字 | string | `count` | | diff --git a/components/badge/style/index.less b/components/badge/style/index.less index 73d1cd3592..aabac9b0d6 100644 --- a/components/badge/style/index.less +++ b/components/badge/style/index.less @@ -94,6 +94,18 @@ &-warning { background-color: @warning-color; } + + // mixin to iterate over colors and create CSS class for each one + .make-color-classes(@i: length(@preset-colors)) when (@i > 0) { + .make-color-classes(@i - 1); + @color: extract(@preset-colors, @i); + @darkColor: '@{color}-6'; + &-@{color} { + background: @@darkColor; + } + } + .make-color-classes(); + &-text { margin-left: 8px; color: @text-color; diff --git a/components/style/color/colors.less b/components/style/color/colors.less index b489eed0cb..86c5697f64 100644 --- a/components/style/color/colors.less +++ b/components/style/color/colors.less @@ -144,3 +144,6 @@ @gold-8: color(~`colorPalette('@{gold-6}', 8) `); @gold-9: color(~`colorPalette('@{gold-6}', 9) `); @gold-10: color(~`colorPalette('@{gold-6}', 10) `); + +@preset-colors: pink, magenta, red, volcano, orange, yellow, gold, cyan, lime, green, blue, geekblue, + purple; diff --git a/components/tag/index.tsx b/components/tag/index.tsx index 74e36184f8..943085e568 100644 --- a/components/tag/index.tsx +++ b/components/tag/index.tsx @@ -6,6 +6,7 @@ import { polyfill } from 'react-lifecycles-compat'; import Icon from '../icon'; import CheckableTag from './CheckableTag'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; +import { PresetColorTypes } from '../_util/colors'; import Wave from '../_util/wave'; export { CheckableTagProps } from './CheckableTag'; @@ -29,6 +30,8 @@ interface InnterTagProps extends TagProps { show: boolean; } +const PresetColorRegex = new RegExp(`^(${PresetColorTypes.join('|')})(-inverse)?$`); + const InnerTag = ({ show, ...restProps }: InnterTagProps) => { const divProps = omit(restProps, ['onClose', 'afterClose', 'color', 'visible', 'closable']); return
; @@ -83,9 +86,7 @@ class Tag extends React.Component { if (!color) { return false; } - return /^(pink|red|yellow|orange|cyan|green|blue|purple|geekblue|magenta|volcano|gold|lime)(-inverse)?$/.test( - color, - ); + return PresetColorRegex.test(color); } getTagStyle() { diff --git a/components/tag/style/index.less b/components/tag/style/index.less index aa588c6b0e..f6caf20d3f 100644 --- a/components/tag/style/index.less +++ b/components/tag/style/index.less @@ -98,13 +98,10 @@ display: none; } - @colors: pink, magenta, red, volcano, orange, yellow, gold, cyan, lime, green, blue, geekblue, - purple; - // mixin to iterate over colors and create CSS class for each one - .make-color-classes(@i: length(@colors)) when (@i > 0) { + .make-color-classes(@i: length(@preset-colors)) when (@i > 0) { .make-color-classes(@i - 1); - @color: extract(@colors, @i); + @color: extract(@preset-colors, @i); @lightColor: '@{color}-1'; @lightBorderColor: '@{color}-3'; @darkColor: '@{color}-6';