diff --git a/components/badge/Badge.jsx b/components/badge/Badge.jsx index e9c9d5f25..c1c577599 100644 --- a/components/badge/Badge.jsx +++ b/components/badge/Badge.jsx @@ -2,12 +2,14 @@ import PropTypes from '../_util/vue-types' import ScrollNumber from './ScrollNumber' import classNames from 'classnames' -import { initDefaultProps, filterEmpty } from '../_util/props-util' +import { initDefaultProps, filterEmpty, getComponentFromProp } from '../_util/props-util' +import { cloneElement } from '../_util/vnode' import getTransitionProps from '../_util/getTransitionProps' +import isNumeric from '../_util/isNumeric' export const BadgeProps = { /** Number to show in badge */ - count: PropTypes.oneOfType([PropTypes.number, PropTypes.string, null]), + count: PropTypes.any, showZero: PropTypes.bool, /** Max count to show */ overflowCount: PropTypes.number, @@ -27,85 +29,162 @@ export default { props: initDefaultProps(BadgeProps, { prefixCls: 'ant-badge', scrollNumberPrefixCls: 'ant-scroll-number', - count: null, showZero: false, dot: false, overflowCount: 99, }), + methods: { + getBadgeClassName () { + const { prefixCls, 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 + const displayCount = + count > overflowCount ? `${overflowCount}+` : count + return displayCount + }, + + getDispayCount () { + const isDot = this.isDot() + // dot mode don't need count + if (isDot) { + return '' + } + return this.getNumberedDispayCount() + }, + + getScollNumberTitle () { + const { title } = this.$props + const count = this.badgeCount + if (title) { + return title + } + return typeof count === 'string' || typeof count === 'number' ? count : undefined + }, + + getStyleWithOffset () { + const { offset, numberStyle } = this.$props + return offset + ? { + right: `${-parseInt(offset[0], 10)}px`, + marginTop: isNumeric(offset[1]) ? `${offset[1]}px` : offset[1], + ...numberStyle, + } + : numberStyle + }, + + renderStatusText () { + const { prefixCls, text } = this.$props + const hidden = this.isHidden() + return hidden || !text ? null : {text} + }, + + renderDispayComponent () { + const count = this.badgeCount + const customNode = count + if (!customNode || typeof customNode !== 'object') { + return undefined + } + return cloneElement(customNode, { + style: this.getStyleWithOffset(), + }) + }, + + renderBadgeNumber () { + const { prefixCls, scrollNumberPrefixCls, status } = this.$props + const count = this.badgeCount + const displayCount = this.getDispayCount() + const isDot = this.isDot() + const hidden = this.isHidden() + + const scrollNumberCls = { + [`${prefixCls}-dot`]: isDot, + [`${prefixCls}-count`]: !isDot, + [`${prefixCls}-multiple-words`]: + !isDot && count && count.toString && count.toString().length > 1, + [`${prefixCls}-status-${status}`]: !!status, + } + + return hidden ? null : ( + }> + title={this.getScollNumberTitle()} + style={this.getStyleWithOffset()} + key='scrollNumber' + /> + ) + }, + }, render () { const { - count, - showZero, prefixCls, - scrollNumberPrefixCls, - overflowCount, - dot, status, text, - offset, $slots, - numberStyle, - title, } = this - let displayCount = count > overflowCount ? `${overflowCount}+` : count - const isZero = displayCount === '0' || displayCount === 0 - const isDot = (dot && !isZero) || status - // dot mode don't need count - if (isDot) { - displayCount = '' - } const children = filterEmpty($slots.default) - const isEmpty = displayCount === null || displayCount === undefined || displayCount === '' - const hidden = (isEmpty || (isZero && !showZero)) && !isDot + let count = getComponentFromProp(this, 'count') + if (Array.isArray(count)) { + count = count[0] + } + this.badgeCount = count + const scrollNumber = this.renderBadgeNumber() + const statusText = this.renderStatusText() const statusCls = classNames({ [`${prefixCls}-status-dot`]: !!status, [`${prefixCls}-status-${status}`]: !!status, }) - const scrollNumberCls = classNames({ - [`${prefixCls}-dot`]: isDot, - [`${prefixCls}-count`]: !isDot, - [`${prefixCls}-multiple-words`]: !isDot && count && count.toString && count.toString().length > 1, - [`${prefixCls}-status-${status}`]: !!status, - }) - const badgeCls = classNames(prefixCls, { - [`${prefixCls}-status`]: !!status, - [`${prefixCls}-not-a-wrapper`]: !children.length, - }) - const styleWithOffset = offset ? { - right: -parseInt(offset[0], 10), - marginTop: typeof offset[1] === 'number' ? `${offset[1]}px` : offset[1], - ...numberStyle, - } : numberStyle - // + // if (!children.length && status) { return ( - + {text} ) } - const scrollNumber = hidden ? null : ( - - ) - - const statusText = (hidden || !text) ? null : ( - {text} - ) const transitionProps = getTransitionProps(children.length ? `${prefixCls}-zoom` : '') - return ( + return ( {children} {scrollNumber} diff --git a/components/badge/ScrollNumber.jsx b/components/badge/ScrollNumber.jsx index 341c1e1c7..4ed3ff060 100644 --- a/components/badge/ScrollNumber.jsx +++ b/components/badge/ScrollNumber.jsx @@ -1,8 +1,9 @@ - +import classNames from 'classnames' import PropTypes from '../_util/vue-types' import BaseMixin from '../_util/BaseMixin' import { getStyle } from '../_util/props-util' import omit from 'omit.js' +import { cloneElement } from '../_util/vnode' function getNumberArray (num) { return num @@ -14,9 +15,11 @@ function getNumberArray (num) { const ScrollNumberProps = { prefixCls: PropTypes.string.def('ant-scroll-number'), - count: PropTypes.oneOfType([PropTypes.number, PropTypes.string, null]).def(null), + count: PropTypes.any, component: PropTypes.string, title: PropTypes.oneOfType([PropTypes.number, PropTypes.string, null]), + displayComponent: PropTypes.any, + className: PropTypes.object, } export default { @@ -105,13 +108,19 @@ export default { }, render () { - const { prefixCls, title, component: Tag = 'sup' } = this + const { prefixCls, title, component: Tag = 'sup', displayComponent, className } = this + if (displayComponent) { + return cloneElement(displayComponent, { + class: `${prefixCls}-custom-component`, + }) + } const style = getStyle(this, true) // fix https://fb.me/react-unknown-prop const restProps = omit(this.$props, [ 'count', 'component', 'prefixCls', + 'displayComponent', ]) const newProps = { props: { @@ -120,8 +129,8 @@ export default { attrs: { title, }, - class: prefixCls, style, + class: classNames(prefixCls, className), } // allow specify the border // mock border-color by box-shadow for compatible with old usage: @@ -129,6 +138,7 @@ export default { if (style && style.borderColor) { newProps.style.boxShadow = `0 0 0 1px ${style.borderColor} inset` } + return ( { this.renderNumberElement()} diff --git a/components/badge/__tests__/__snapshots__/demo.test.js.snap b/components/badge/__tests__/__snapshots__/demo.test.js.snap index 6e4863e27..a2c8a7a5a 100644 --- a/components/badge/__tests__/__snapshots__/demo.test.js.snap +++ b/components/badge/__tests__/__snapshots__/demo.test.js.snap @@ -1,28 +1,28 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders ./components/badge/demo/basic.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

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[`renders ./components/badge/demo/basic.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

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[`renders ./components/badge/demo/change.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

+

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[`renders ./components/badge/demo/dot.md correctly 1`] = ``; +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

`; +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

`; -exports[`renders ./components/badge/demo/no-wrapper.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

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

99+
`; +exports[`renders ./components/badge/demo/no-wrapper.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

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

99+
`; -exports[`renders ./components/badge/demo/overflow.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

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

99+ 10+ 999+
`; +exports[`renders ./components/badge/demo/overflow.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

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

99+ 10+ 999+
`; exports[`renders ./components/badge/demo/status.md correctly 1`] = `

Success
Error
Default
Processing
warning
`; -exports[`renders ./components/badge/demo/title.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

`; +exports[`renders ./components/badge/demo/title.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 5d8f7e0d0..23c9ae8c0 100644 --- a/components/badge/__tests__/__snapshots__/index.test.js.snap +++ b/components/badge/__tests__/__snapshots__/index.test.js.snap @@ -1,13 +1,15 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -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 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

`; +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

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

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 render when count is changed 2`] = `

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 render when count is changed 3`] = `

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 render when count is changed 3`] = `

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 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

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 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

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 5`] = `

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 5`] = `

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`] = ``; diff --git a/components/badge/__tests__/index.test.js b/components/badge/__tests__/index.test.js index ecc4e262f..0c8577b4d 100644 --- a/components/badge/__tests__/index.test.js +++ b/components/badge/__tests__/index.test.js @@ -85,4 +85,18 @@ describe('Badge', () => { expect(wrapper.html()).toMatchSnapshot() }) + + // https://github.com/ant-design/ant-design/issues/13694 + it('should support offset when count is a ReactNode', () => { + const wrapper = mount({ + render () { + return ( + } offset={[10, 20]}> + + + ) + }, + }) + expect(wrapper.html()).toMatchSnapshot() + }) }) diff --git a/components/badge/demo/basic.md b/components/badge/demo/basic.md index f14ddd221..cea87fe5a 100644 --- a/components/badge/demo/basic.md +++ b/components/badge/demo/basic.md @@ -17,6 +17,10 @@ Simplest Usage. Badge will be hidden when `count` is `0`, but we can use `showZe + + + +
``` diff --git a/components/badge/index.en_US.md b/components/badge/index.en_US.md index f86f2de85..60f3f8714 100644 --- a/components/badge/index.en_US.md +++ b/components/badge/index.en_US.md @@ -12,7 +12,7 @@ | Property | Description | Type | Default | | -------- | ----------- | ---- | ------- | -| count | Number to show in badge | number\|string | | +| 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 | diff --git a/components/badge/index.zh-CN.md b/components/badge/index.zh-CN.md index f03075a6d..bc9bc5ffb 100644 --- a/components/badge/index.zh-CN.md +++ b/components/badge/index.zh-CN.md @@ -13,7 +13,7 @@ | 参数 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | -| count | 展示的数字,大于 overflowCount 时显示为 `${overflowCount}+`,为 0 时隐藏 | number\|string | | +| count | 展示的数字,大于 overflowCount 时显示为 `${overflowCount}+`,为 0 时隐藏 | number \| string \| slot | | | dot | 不展示数字,只有一个小红点 | boolean | false | | offset | 设置状态点的位置偏移,格式为 [x, y] | [number\|string, number\|string] | - | | overflowCount | 展示封顶的数字值 | number | 99 |