diff --git a/build/config.js b/build/config.js index 452a79999..ddbb5d3d1 100644 --- a/build/config.js +++ b/build/config.js @@ -1,5 +1,5 @@ module.exports = { dev: { - componentName: 'back-top', // dev components + componentName: 'badge', // dev components }, }; diff --git a/components/badge/Badge.jsx b/components/badge/Badge.jsx index 7041cacd0..0ae40a38e 100644 --- a/components/badge/Badge.jsx +++ b/components/badge/Badge.jsx @@ -1,5 +1,6 @@ import PropTypes from '../_util/vue-types'; import ScrollNumber from './ScrollNumber'; +import { PresetColorTypes } from '../_util/colors'; import classNames from 'classnames'; import { initDefaultProps, @@ -23,12 +24,15 @@ export const BadgeProps = { prefixCls: PropTypes.string, scrollNumberPrefixCls: PropTypes.string, status: PropTypes.oneOf(['success', 'processing', 'default', 'error', 'warning']), + color: PropTypes.string, text: PropTypes.string, offset: PropTypes.array, numberStyle: PropTypes.object.def({}), title: PropTypes.string, }; - +function isPresetColor(color) { + return PresetColorTypes.indexOf(color) !== -1; +} export default { name: 'ABadge', props: initDefaultProps(BadgeProps, { @@ -40,35 +44,6 @@ export default { configProvider: { default: () => ConfigConsumerProps }, }, methods: { - getBadgeClassName(prefixCls) { - const { status } = this.$props; - const children = filterEmpty(this.$slots.default); - return classNames(prefixCls, { - [`${prefixCls}-status`]: !!status, - [`${prefixCls}-not-a-wrapper`]: !children.length, - }); - }, - - isZero() { - const numberedDispayCount = this.getNumberedDispayCount(); - return numberedDispayCount === '0' || numberedDispayCount === 0; - }, - - isDot() { - const { dot, status } = this.$props; - const isZero = this.isZero(); - return (dot && !isZero) || status; - }, - - isHidden() { - const { showZero } = this.$props; - const displayCount = this.getDispayCount(); - const isZero = this.isZero(); - const isDot = this.isDot(); - const isEmpty = displayCount === null || displayCount === undefined || displayCount === ''; - return (isEmpty || (isZero && !showZero)) && !isDot; - }, - getNumberedDispayCount() { const { overflowCount } = this.$props; const count = this.badgeCount; @@ -104,6 +79,37 @@ export default { } : numberStyle; }, + getBadgeClassName(prefixCls) { + const { status } = this.$props; + const children = filterEmpty(this.$slots.default); + return classNames(prefixCls, { + [`${prefixCls}-status`]: !!status, + [`${prefixCls}-not-a-wrapper`]: !children.length, + }); + }, + hasStatus() { + 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 isZero = this.isZero(); + return (dot && !isZero) || this.hasStatus(); + }, + + isHidden() { + const { showZero } = this.$props; + const displayCount = this.getDispayCount(); + const isZero = this.isZero(); + const isDot = this.isDot(); + const isEmpty = displayCount === null || displayCount === undefined || displayCount === ''; + return (isEmpty || (isZero && !showZero)) && !isDot; + }, renderStatusText(prefixCls) { const { text } = this.$props; @@ -134,7 +140,7 @@ export default { [`${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 : ( @@ -159,6 +165,7 @@ export default { scrollNumberPrefixCls: customizeScrollNumberPrefixCls, status, text, + color, $slots, } = this; @@ -175,20 +182,28 @@ export default { const scrollNumber = this.renderBadgeNumber(prefixCls, scrollNumberPrefixCls); 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 = {}; + if (color && !isPresetColor(color)) { + statusStyle.background = color; + } // - if (!children.length && status) { + if (!children.length && this.hasStatus()) { + const styleWithOffset = this.getStyleWithOffset(); + const statusTextColor = styleWithOffset && styleWithOffset.color; return ( - - {text} + + + {text} + ); } diff --git a/components/badge/ScrollNumber.jsx b/components/badge/ScrollNumber.jsx index e4d661f4e..fec3615f8 100644 --- a/components/badge/ScrollNumber.jsx +++ b/components/badge/ScrollNumber.jsx @@ -12,7 +12,10 @@ function getNumberArray(num) { .toString() .split('') .reverse() - .map(i => Number(i)) + .map(i => { + const current = Number(i); + return isNaN(current) ? i : current; + }) : []; } @@ -38,42 +41,38 @@ export default { }; }, watch: { - count(val) { - if (this.sCount !== val) { - this.lastCount = this.sCount; - // 复原数字初始位置 - this.setState( - { - animateStarted: true, - }, - () => { - // 等待数字位置复原完毕 - // 开始设置完整的数字 - setTimeout(() => { - this.setState( - { - animateStarted: false, - sCount: val, - }, - () => { - this.$emit('animated'); - }, - ); - }, 5); - }, - ); - } + count() { + this.lastCount = this.sCount; + this.setState({ + animateStarted: true, + }); }, }, + updated() { + const { animateStarted, count } = this; + if (animateStarted) { + this.setState( + { + animateStarted: false, + sCount: count, + }, + this.onAnimated, + ); + } + }, methods: { getPositionByNum(num, i) { + const { sCount } = this; + const currentCount = Math.abs(Number(sCount)); + const lastCount = Math.abs(Number(this.lastCount)); + const currentDigit = Math.abs(getNumberArray(sCount)[i]); + const lastDigit = Math.abs(getNumberArray(this.lastCount)[i]); + if (this.animateStarted) { return 10 + num; } - const currentDigit = getNumberArray(this.sCount)[i]; - const lastDigit = getNumberArray(this.lastCount)[i]; // 同方向则在同一侧切换数字 - if (this.sCount > this.lastCount) { + if (currentCount > lastCount) { if (currentDigit >= lastDigit) { return 10 + num; } @@ -84,6 +83,10 @@ export default { } return num; }, + onAnimated() { + this.$emit('animated'); + }, + renderNumberList(position) { const childrenToReturn = []; for (let i = 0; i < 30; i++) { @@ -94,22 +97,29 @@ export default {

, ); } + return childrenToReturn; }, - renderCurrentNumber(prefixCls, num, i) { - const position = this.getPositionByNum(num, i); - const removeTransition = - this.animateStarted || getNumberArray(this.lastCount)[i] === undefined; - const style = { - transition: removeTransition ? 'none' : undefined, - msTransform: `translateY(${-position * 100}%)`, - WebkitTransform: `translateY(${-position * 100}%)`, - transform: `translateY(${-position * 100}%)`, - }; + if (typeof num === 'number') { + const position = this.getPositionByNum(num, i); + const removeTransition = + this.animateStarted || getNumberArray(this.lastCount)[i] === undefined; + const style = { + transition: removeTransition ? 'none' : undefined, + msTransform: `translateY(${-position * 100}%)`, + WebkitTransform: `translateY(${-position * 100}%)`, + transform: `translateY(${-position * 100}%)`, + }; + return ( + + {this.renderNumberList(position)} + + ); + } return ( - - {this.renderNumberList(position)} + + {num} ); }, diff --git a/components/badge/__tests__/__snapshots__/demo.test.js.snap b/components/badge/__tests__/__snapshots__/demo.test.js.snap index 75c640c23..7b1d2ec17 100644 --- a/components/badge/__tests__/__snapshots__/demo.test.js.snap +++ b/components/badge/__tests__/__snapshots__/demo.test.js.snap @@ -16,6 +16,29 @@ exports[`renders ./components/badge/demo/change.md correctly 1`] = ` `; +exports[`renders ./components/badge/demo/colors.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`] = ``; exports[`renders ./components/badge/demo/link.md correctly 1`] = `

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

`; diff --git a/components/badge/__tests__/__snapshots__/index.test.js.snap b/components/badge/__tests__/__snapshots__/index.test.js.snap index 63783d156..e5457550f 100644 --- a/components/badge/__tests__/__snapshots__/index.test.js.snap +++ b/components/badge/__tests__/__snapshots__/index.test.js.snap @@ -4,6 +4,8 @@ exports[`Badge badge should support float number 1`] = `"3.5"`; exports[`Badge badge should support float number 2`] = `3.5`; +exports[`Badge render correct with negative number 1`] = `
-

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

-

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

`; + exports[`Badge should be compatible with borderColor style 1`] = `

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

`; exports[`Badge should render when count is changed 1`] = `

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

`; @@ -16,4 +18,4 @@ exports[`Badge should render when count is changed 4`] = `

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

`; -exports[`Badge should support offset when count is a ReactNode 1`] = ``; +exports[`Badge should support offset when count is a VueNode 1`] = `head`; diff --git a/components/badge/__tests__/index.test.js b/components/badge/__tests__/index.test.js index 8768a4e0f..ac1361d47 100644 --- a/components/badge/__tests__/index.test.js +++ b/components/badge/__tests__/index.test.js @@ -109,16 +109,32 @@ describe('Badge', () => { }); // https://github.com/ant-design/ant-design/issues/13694 - it('should support offset when count is a ReactNode', () => { + it('should support offset when count is a VueNode', () => { const wrapper = mount({ render() { return ( } offset={[10, 20]}> - + + head + ); }, }); expect(wrapper.html()).toMatchSnapshot(); }); + // https://github.com/ant-design/ant-design/issues/15799 + it('render correct with negative number', () => { + const wrapper = mount({ + render() { + return ( +
+ + +
+ ); + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); }); diff --git a/components/badge/demo/colors.md b/components/badge/demo/colors.md new file mode 100644 index 000000000..17f882cd3 --- /dev/null +++ b/components/badge/demo/colors.md @@ -0,0 +1,56 @@ + +#### 多彩徽标 +1.5.0 后新增。我们添加了多种预设色彩的徽标样式,用作不同场景使用。如果预设值不能满足你的需求,可以设置为具体的色值。 + + + +#### Colorful Badge +New feature after 3.16.0. We preset a series of colorful Badge styles for use in different situations. You can also set it to a hex color string for custom color. + + +```tpl + + +``` diff --git a/components/badge/demo/index.vue b/components/badge/demo/index.vue index 52d0151d1..d81ed6905 100644 --- a/components/badge/demo/index.vue +++ b/components/badge/demo/index.vue @@ -6,6 +6,7 @@ import Change from './change'; import Overflow from './overflow'; import Status from './status'; import Title from './title'; +import Colors from './colors'; import CN from './../index.zh-CN.md'; import US from './../index.en_US.md'; @@ -42,6 +43,7 @@ export default { + <Colors /> <api> <CN slot="cn" /> <US /> diff --git a/components/badge/index.en_US.md b/components/badge/index.en_US.md index 2494012cd..72dc6f3c8 100644 --- a/components/badge/index.en_US.md +++ b/components/badge/index.en_US.md @@ -10,14 +10,15 @@ <a-badge :count="5" /> ``` -| Property | Description | Type | Default | -| --- | --- | --- | --- | -| count | Number to show in badge | number\|string \| slot | | -| dot | Whether to display a red dot instead of `count` | boolean | `false` | -| offset | set offset of the badge dot, like [x, y] | [number\|string, number\|string] | - | -| 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 | `''` | -| numberStyle | sets the display style of the status `dot` | object | '' | -| title | Text to show when hovering over the badge | string | `count` | +| Property | Description | Type | Default | Version | +| --- | --- | --- | --- | --- | +| color | Customize Badge dot color | string | - | 1.5.0 | +| count | Number to show in badge | number\|string \| slot | | | +| dot | Whether to display a red dot instead of `count` | boolean | `false` | | +| offset | set offset of the badge dot, like [x, y] | [number\|string, number\|string] | - | | +| 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 | `''` | | +| numberStyle | sets the display style of the status `dot` | object | '' | | +| title | Text to show when hovering over the badge | string | `count` | | diff --git a/components/badge/index.zh-CN.md b/components/badge/index.zh-CN.md index 10d20d80c..f4fcb665e 100644 --- a/components/badge/index.zh-CN.md +++ b/components/badge/index.zh-CN.md @@ -10,14 +10,15 @@ <a-badge :count="5" /> ``` -| 参数 | 说明 | 类型 | 默认值 | -| --- | --- | --- | --- | -| count | 展示的数字,大于 overflowCount 时显示为 `${overflowCount}+`,为 0 时隐藏 | number \| string \| slot | | -| dot | 不展示数字,只有一个小红点 | boolean | false | -| offset | 设置状态点的位置偏移,格式为 [x, y] | [number\|string, number\|string] | - | -| overflowCount | 展示封顶的数字值 | number | 99 | -| showZero | 当数值为 0 时,是否展示 Badge | boolean | false | -| status | 设置 Badge 为状态点 | Enum{ 'success', 'processing, 'default', 'error', 'warning' } | '' | -| text | 在设置了 `status` 的前提下有效,设置状态点的文本 | string | '' | -| numberStyle | 设置状态点的样式 | object | '' | -| title | 设置鼠标放在状态点上时显示的文字 | string | `count` | +| 参数 | 说明 | 类型 | 默认值 | 版本 | +| --- | --- | --- | --- | --- | +| color | 自定义小圆点的颜色 | string | - | 1.5.0 | +| count | 展示的数字,大于 overflowCount 时显示为 `${overflowCount}+`,为 0 时隐藏 | number \| string \| slot | | | +| dot | 不展示数字,只有一个小红点 | boolean | false | | +| offset | 设置状态点的位置偏移,格式为 [x, y] | [number\|string, number\|string] | - | | +| overflowCount | 展示封顶的数字值 | number | 99 | | +| showZero | 当数值为 0 时,是否展示 Badge | boolean | false | | +| status | 设置 Badge 为状态点 | Enum{ 'success', 'processing, 'default', 'error', 'warning' } | '' | | +| text | 在设置了 `status` 的前提下有效,设置状态点的文本 | string | '' | | +| numberStyle | 设置状态点的样式 | object | '' | | +| title | 设置鼠标放在状态点上时显示的文字 | string | `count` | |