Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/avatar/__tests__/avatar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe('Avatar', () => {
const count = 8;
const { container } = render(<Avatar badgeProps={{ count, offset: [6, 6] }} icon={<UserIcon />} />);
expect(container.querySelector('.t-badge--basic')).toBeTruthy();
expect(container.querySelector('.t-badge--basic').innerHTML.trim()).toBe(`${count}`);
expect(container.querySelector('.t-badge--basic > .t-badge__count').innerHTML.trim()).toBe(`${count}`);
});

it(': size', () => {
Expand Down
50 changes: 31 additions & 19 deletions src/badge/Badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,12 @@ import { usePrefixClass } from '../hooks/useClass';

export interface BadgeProps extends TdBadgeProps, StyledProps {}

const hasUnit = (unit: string) =>
unit.indexOf('px') > 0 ||
unit.indexOf('rpx') > 0 ||
unit.indexOf('em') > 0 ||
unit.indexOf('rem') > 0 ||
unit.indexOf('%') > 0 ||
unit.indexOf('vh') > 0 ||
unit.indexOf('vm') > 0;
const hasUnit = (value: string): boolean => /px|rpx|em|rem|%|vh|vw/.test(value);

const addUnit = (value: string | number): string => {
const strValue = value.toString();
return hasUnit(strValue) ? strValue : `${value}px`;
};

const Badge = forwardRef<HTMLDivElement, BadgeProps>((originProps, ref) => {
const props = useDefaultProps(originProps, badgeDefaultProps);
Expand All @@ -27,22 +25,38 @@ const Badge = forwardRef<HTMLDivElement, BadgeProps>((originProps, ref) => {
const { classPrefix } = useConfig();
const badgeClass = usePrefixClass('badge');

const childNode = content || children;

// 徽标自定义样式
const badgeInnerStyles = useMemo(() => {
const mergedStyle: React.CSSProperties = {};
if (color) mergedStyle.backgroundColor = color;
if (offset && Array.isArray(offset)) {
const [right = 0, top = 0]: Array<string | number> = offset;
mergedStyle.right = hasUnit(right.toString()) ? right : `${right}px`;
mergedStyle.top = hasUnit(top.toString()) ? top : `${top}px`;
const styles: React.CSSProperties = {};
if (color) styles.backgroundColor = color;
const [xOffset = 0, yOffset = 0]: Array<string | number> = offset || [];

if (xOffset) {
styles.left = `calc(100% + ${addUnit(xOffset)})`;
}

if (yOffset) {
styles.top = addUnit(yOffset);
}
return mergedStyle;

return styles;
}, [color, offset]);

// 是否使用外层类名
const useOuterClass = useMemo(() => {
const target = ['ribbon', 'ribbon-right', 'ribbon-left', 'triangle-right', 'triangle-left'];
if (content || !target.includes(shape)) {
return false;
}
return !parseTNode(childNode);
}, [content, shape, childNode]);

// 徽标外层样式类
const badgeClasses = classNames(className, {
[`${badgeClass}`]: true,
[`${badgeClass}__ribbon-outer`]: shape === 'ribbon',
[`${badgeClass}__${shape}-outer`]: useOuterClass,
});

// 徽标内层样式类
Expand Down Expand Up @@ -78,8 +92,6 @@ const Badge = forwardRef<HTMLDivElement, BadgeProps>((originProps, ref) => {
return parseTNode(count);
}, [dot, count, maxCount, showZero]);

const childNode = content || children;

const readerContent = () => {
if (typeof content === 'string') {
return <span className={`${badgeClass}__content-text`}>{content}</span>;
Expand All @@ -91,7 +103,7 @@ const Badge = forwardRef<HTMLDivElement, BadgeProps>((originProps, ref) => {
if (!isShowBadge) return null;
return (
<div className={badgeInnerClasses} style={badgeInnerStyles}>
{readerCount}
<div className={`${badgeClass}__count`}>{readerCount}</div>
</div>
);
};
Expand Down
16 changes: 7 additions & 9 deletions src/badge/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ describe('Badge', () => {

it(':count', () => {
const { container } = render(<Badge count={99} />);
expect(container.querySelector('.t-badge--basic').innerHTML.trim()).toBe('99');
expect(container.querySelector('.t-badge--basic > .t-badge__count').innerHTML.trim()).toBe('99');
const { container: container2 } = render(<Badge count={() => 33} />);
expect(container2.querySelector('.t-badge--basic').innerHTML.trim()).toBe('33');
expect(container2.querySelector('.t-badge--basic > .t-badge__count').innerHTML.trim()).toBe('33');
const { container: container3 } = render(<Badge count={null} />);
expect(container3.querySelector('.t-badge--basic')).toBe(null);
expect(container3.querySelector('.t-badge--basic > .t-badge__count')).toBe(null);
});

it(':dot', () => {
Expand All @@ -67,21 +67,19 @@ describe('Badge', () => {

it(':maxCount', () => {
const { container } = render(<Badge count={100} maxCount={99} />);
expect(container.querySelector('.t-badge--basic').innerHTML.trim()).toBe('99+');
expect(container.querySelector('.t-badge--basic > .t-badge__count').innerHTML.trim()).toBe('99+');
});

it(':offset', () => {
const { container } = render(<Badge count={3} offset={[10, 10]} />);
expect(container.querySelector('.t-badge--basic').getAttribute('style'))
.contain('right: 10px;')
.contain('left: calc(100% + 10px);')
.contain('top: 10px;');
const { container: container1 } = render(<Badge count={3} offset={[]} />);
expect(container1.querySelector('.t-badge--basic').getAttribute('style'))
.contain('right: 0px;')
.contain('top: 0px;');
expect(container1.querySelector('.t-badge--basic').getAttribute('style')).toBe(null);
const { container: container2 } = render(<Badge count={3} offset={['10em', '10rem']} />);
expect(container2.querySelector('.t-badge--basic').getAttribute('style'))
.contain('right: 10em;')
.contain('left: calc(100% + 10em);')
.contain('top: 10rem;');
});

Expand Down
4 changes: 2 additions & 2 deletions src/badge/_example/base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ export default function BaseBadge() {

<div className="summary">数字徽标</div>
<div className="badge-demo">
<Badge count="8" content="消息" offset={[-8, 0]} className="badge-item" />
<Badge count="2" offset={[-2, -2]} className="badge-item">
<Badge count="8" content="消息" offset={[4, 0]} className="badge-item" />
<Badge count="2" offset={[2, -2]} className="badge-item">
<NotificationIcon size={24} />
</Badge>
<Badge count="8" offset={[2, 2]} className="badge-item">
Expand Down
2 changes: 1 addition & 1 deletion src/badge/_example/size.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default function SizeBadge() {
<Avatar icon={<UserIcon />} size="large" badgeProps={{ count: 8, size: 'large', offset: [7, 7] }} />
</div>

<div className="summary">Middle</div>
<div className="summary">Medium</div>
<div className="block">
<Avatar icon={<UserIcon />} badgeProps={{ count: 8, offset: [5, 5] }} />
</div>
Expand Down
17 changes: 14 additions & 3 deletions src/badge/_example/theme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ export default function ThemeBadge() {
<>
<div className="summary">圆形徽标</div>
<div className="badge-demo">
<Badge count="2" offset={[-2, -2]}>
<Badge count="2" offset={[2, -2]}>
<NotificationIcon size={24} />
</Badge>
</div>

<div className="summary">方形徽标</div>
<div className="badge-demo">
<Badge count="2" shape="square" offset={[-2, -2]}>
<Badge count="2" shape="square" offset={[1, -2]}>
<NotificationIcon size={24} />
</Badge>
</div>
Expand All @@ -29,7 +29,18 @@ export default function ThemeBadge() {
<div className="summary" style={{ marginBottom: '16px' }}>
角标
</div>
<Cell title="单行标题" note={<Badge count="New" offset={[0, 0]} shape="ribbon" />}></Cell>
<Cell title="单行标题" note={<Badge count="NEW" offset={[0, 0]} shape="ribbon-left" />}></Cell>
<Cell title="单行标题" bordered={false} note={<Badge count="NEW" offset={[0, 0]} shape="ribbon" />}></Cell>

<div className="summary" style={{ marginBottom: '16px' }}>
三角角标
</div>
<Cell title="单行标题" note={<Badge count="NEW" offset={[0, 0]} shape="triangle-left" />}></Cell>
<Cell
title="单行标题"
bordered={false}
note={<Badge count="NEW" offset={[0, 0]} shape="triangle-right" />}
></Cell>
</>
);
}
16 changes: 8 additions & 8 deletions src/badge/badge.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@

## API


### Badge Props

name | type | default | description | required
-- | -- | -- | -- | --
className | String | - | className of component | N
style | Object | - | CSS(Cascading Style Sheets),Typescript`React.CSSProperties` | N
children | TNode | - | Typescript`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N
style | Object | - | CSS(Cascading Style Sheets),Typescript: `React.CSSProperties` | N
children | TNode | - | Typescript: `string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N
color | String | - | \- | N
content | TNode | - | Typescript`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N
count | TNode | 0 | Typescript`string \| number \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N
content | TNode | - | Typescript: `string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N
count | TNode | 0 | Typescript: `string \| number \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N
dot | Boolean | false | \- | N
maxCount | Number | 99 | \- | N
offset | Array | - | Typescript`Array<string \| number>` | N
shape | String | circle | options: circle/square/bubble/ribbon | N
offset | Array | - | Typescript: `Array<string \| number>` | N
shape | String | circle | options: circle/square/bubble/ribbon/ribbon-right/ribbon-left/triangle-right/triangle-left | N
showZero | Boolean | false | \- | N
size | String | medium | options: medium/large | N

Expand All @@ -31,9 +30,10 @@ Name | Default Value | Description
--td-badge-bg-color | @error-color | -
--td-badge-border-radius | 2px | -
--td-badge-bubble-border-radius | 10px 10px 10px 1px | -
--td-badge-content-text-color | @text-color-primary | -
--td-badge-dot-size | 8px | -
--td-badge-font | @font-mark-extraSmall | -
--td-badge-large-font | @font-mark-small | -
--td-badge-large-height | 20px | -
--td-badge-large-padding | 5px | -
--td-badge-text-color | @font-white-1 | -
--td-badge-text-color | @text-color-anti | -
5 changes: 3 additions & 2 deletions src/badge/badge.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ count | TNode | 0 | 徽标右上角内容。可以是数字,也可以是文字
dot | Boolean | false | 是否为红点 | N
maxCount | Number | 99 | 封顶的数字值 | N
offset | Array | - | 设置状态点的位置偏移,示例:[-10, 20] 或 ['10em', '8rem']。TS 类型:`Array<string \| number>` | N
shape | String | circle | 形状。可选项:circle/square/bubble/ribbon | N
shape | String | circle | 徽标形状,其中 ribbon 和 ribbon-right 等效。可选项:circle/square/bubble/ribbon/ribbon-right/ribbon-left/triangle-right/triangle-left | N
showZero | Boolean | false | 当数值为 0 时,是否展示徽标 | N
size | String | medium | 尺寸。可选项:medium/large | N

Expand All @@ -30,9 +30,10 @@ size | String | medium | 尺寸。可选项:medium/large | N
--td-badge-bg-color | @error-color | -
--td-badge-border-radius | 2px | -
--td-badge-bubble-border-radius | 10px 10px 10px 1px | -
--td-badge-content-text-color | @text-color-primary | -
--td-badge-dot-size | 8px | -
--td-badge-font | @font-mark-extraSmall | -
--td-badge-large-font | @font-mark-small | -
--td-badge-large-height | 20px | -
--td-badge-large-padding | 5px | -
--td-badge-text-color | @font-white-1 | -
--td-badge-text-color | @text-color-anti | -
12 changes: 10 additions & 2 deletions src/badge/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,18 @@ export interface TdBadgeProps {
*/
offset?: Array<string | number>;
/**
* 形状
* 徽标形状,其中 ribbon 和 ribbon-right 等效
* @default circle
*/
shape?: 'circle' | 'square' | 'bubble' | 'ribbon';
shape?:
| 'circle'
| 'square'
| 'bubble'
| 'ribbon'
| 'ribbon-right'
| 'ribbon-left'
| 'triangle-right'
| 'triangle-left';
/**
* 当数值为 0 时,是否展示徽标
* @default false
Expand Down
4 changes: 2 additions & 2 deletions src/tabs/_example/badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { Tabs, TabPanel } from 'tdesign-mobile-react';
export default () => (
<div>
<Tabs defaultValue={'first'}>
<TabPanel value={'first'} badgeProps={{ dot: true, offset: [-4, 1] }} label="选项"></TabPanel>
<TabPanel value={'second'} badgeProps={{ count: 8, offset: [-8, 3] }} label="选项"></TabPanel>
<TabPanel value={'first'} badgeProps={{ dot: true, offset: [0, 1] }} label="选项"></TabPanel>
<TabPanel value={'second'} badgeProps={{ count: 8, offset: [0, 1] }} label="选项"></TabPanel>
<TabPanel value={'third'} label="选项"></TabPanel>
</Tabs>
</div>
Expand Down
Loading
Loading