Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Badge): use new style #487

Merged
merged 2 commits into from
Aug 23, 2024
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 site/mobile/mobile.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export default {
{
title: 'Badge 徽标',
name: 'badge',
component: () => import('tdesign-mobile-react/badge/_example/index.jsx'),
component: () => import('tdesign-mobile-react/badge/_example/index.tsx'),
},
{
title: 'CountDown 倒计时',
Expand Down
138 changes: 77 additions & 61 deletions src/badge/Badge.tsx
Original file line number Diff line number Diff line change
@@ -1,90 +1,106 @@
import React, { forwardRef, useContext, useMemo } from 'react';
import cls from 'classnames';
import { ConfigContext } from '../config-provider';
import React, { forwardRef, useMemo } from 'react';
import classNames from 'classnames';
import isNumber from 'lodash/isNumber';
import isString from 'lodash/isString';
import type { StyledProps } from '../common';
import type { TdBadgeProps } from './type';
import { badgeDefaultProps } from './defaultProps';
import parseTNode from '../_util/parseTNode';
import useDefaultProps from '../hooks/useDefaultProps';
import useConfig from '../hooks/useConfig';
import { usePrefixClass } from '../hooks/useClass';

export interface BadgeProps extends TdBadgeProps, StyledProps {}

function resolveOffset(ofs): number | string | undefined {
if (typeof ofs === 'undefined') return ofs;
if (Number.isNaN(ofs)) return;
if (typeof ofs === 'number') return (ofs * -1) as number;
return (ofs as string).startsWith('-') ? ofs.replace(/^-/, '') : `-${ofs}`;
}
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;

anlyyao marked this conversation as resolved.
Show resolved Hide resolved
const Badge = forwardRef<HTMLDivElement, BadgeProps>((props, ref) => {
const {
children,
className,
style,
color = '',
content,
count = 0,
dot = false,
maxCount = 99,
offset,
shape = 'circle',
showZero = false,
size = 'medium',
...resetProps
} = props;
const { classPrefix } = useContext(ConfigContext);
const name = useMemo(() => `${classPrefix}-badge`, [classPrefix]);
const hasChildren = useMemo(() => !!(content || children), [content, children]);
// const isRibbon = useMemo(() => !dot && shape === 'ribbon', [shape, dot]);
const Badge = forwardRef<HTMLDivElement, BadgeProps>((originProps, ref) => {
const props = useDefaultProps(originProps, badgeDefaultProps);
const { children, className, style, color, content, count, dot, maxCount, offset, shape, showZero, size } = props;

const { classPrefix } = useConfig();
const badgeClass = usePrefixClass('badge');

// 徽标自定义样式
const computedStyle = useMemo(() => {
const mergedStyle = { ...style };
const badgeInnerStyles = useMemo(() => {
const mergedStyle: React.CSSProperties = {};
if (color) mergedStyle.backgroundColor = color;
if (offset && Array.isArray(offset)) {
const [right, top] = offset;
mergedStyle.right = resolveOffset(right);
mergedStyle.top = resolveOffset(top);
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`;
}
return mergedStyle;
}, [style, color, offset]);
}, [color, offset]);

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

// 徽标内层样式类
const badgeInnerClasses = cls(
{
[`${name}__inner`]: true,
[`${name}--dot`]: dot,
[`${name}--${shape}`]: !dot && shape,
[`${classPrefix}-badge--has-children`]: hasChildren,
},
size === 'small' ? `${name}--${size}` : `${name}--medium`,
className,
);
const badgeInnerClasses = classNames({
[`${badgeClass}--basic`]: true,
[`${badgeClass}--dot`]: dot,
[`${badgeClass}--${size}`]: true,
[`${badgeClass}--${shape}`]: true,
[`${badgeClass}--count`]: !dot && count,
[`${classPrefix}-has-count`]: true,
});

// 是否展示角标
const isShowBadge = useMemo(() => {
if (dot) {
return true;
}
if (!showZero && Number(count) === 0) {
return false;
}
if (count == null) return false;
return true;
}, [dot, count, showZero]);

const renderBadgeContent = useMemo(() => {
const readerCount = useMemo(() => {
if (dot) return null;
if (typeof count === 'number') {
if (count === 0) {
if (isString(count) || isNumber(count)) {
if (Number(count) === 0) {
return showZero ? count : null;
}
return count >= +maxCount ? maxCount : count;
return Number(count) > Number(maxCount) ? `${maxCount}+` : count;
}
return count;
return parseTNode(count);
}, [dot, count, maxCount, showZero]);

const renderBadge = (
<div className={badgeInnerClasses} style={computedStyle}>
{renderBadgeContent}
</div>
);
const childNode = content || children;

const readerContent = () =>
childNode && (
<div className={`${badgeClass}__content`}>
<span className={`${badgeClass}__content-text`}>{parseTNode(childNode)}</span>
</div>
);

const readerBadge = () => {
if (!isShowBadge) return null;
return (
<div className={badgeInnerClasses} style={badgeInnerStyles}>
{readerCount}
</div>
);
};

return (
<div ref={ref} className={badgeClasses} {...resetProps}>
{renderBadge}
{content || children}
<div ref={ref} className={badgeClasses} style={style}>
{readerContent()}
{readerBadge()}
</div>
);
});
Expand Down
23 changes: 0 additions & 23 deletions src/badge/_example/base.jsx

This file was deleted.

38 changes: 38 additions & 0 deletions src/badge/_example/base.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import { Badge, Button } from 'tdesign-mobile-react';
import { NotificationIcon } from 'tdesign-icons-react';

export default function BaseBadge() {
return (
<>
<div className="summary">红点徽标</div>
<div className="badge-demo">
<Badge dot className="badge-item" content="消息" />
<Badge dot offset={[1, -1]} className="badge-item">
<NotificationIcon size="24" />
</Badge>
<Badge dot offset={[1, 1]} className="badge-item">
<Button>按钮</Button>
</Badge>
</div>

<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">
<NotificationIcon size="24" />
</Badge>
<Badge count="8" offset={[2, 2]} className="badge-item">
<Button>按钮</Button>
</Badge>
</div>

<div className="summary">自定义徽标</div>
<div className="badge-demo">
<Badge count="NEW" offset={[0, 2]}>
<Button icon={<NotificationIcon />} shape="square" size="large" />
</Badge>
</div>
</>
);
}
40 changes: 0 additions & 40 deletions src/badge/_example/button.jsx

This file was deleted.

24 changes: 0 additions & 24 deletions src/badge/_example/cell.jsx

This file was deleted.

22 changes: 12 additions & 10 deletions src/badge/_example/index.jsx → src/badge/_example/index.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import React from 'react';
import TDemoBlock from '../../../site/mobile/components/DemoBlock';
import TDemoHeader from '../../../site/mobile/components/DemoHeader';
import BaseDemo from './base';
import ButtonDemo from './button';
import CellDemo from './cell';

import BaseBadge from './base';
import ThemeBadge from './theme';
import SizeBadge from './size';

import './style/index.less';

export default function BadgeDemo() {
return (
<div className="badge-demo">
<div className="tdesign-mobile-demo">
<TDemoHeader title="Badge 徽标" summary="用于告知用户,该区域的状态变化或者待处理任务的数量。" />
<TDemoBlock title="01 类型" summary="徽标主要分红点、数字、文字和角标提醒">
<BaseDemo />
<TDemoBlock title="01 组件类型">
<BaseBadge />
</TDemoBlock>
<TDemoBlock>
<ButtonDemo />
<TDemoBlock title="02 组件样式">
<ThemeBadge />
</TDemoBlock>
<TDemoBlock>
<CellDemo />
<TDemoBlock title="03 组件尺寸">
<SizeBadge />
</TDemoBlock>
</div>
);
Expand Down
19 changes: 19 additions & 0 deletions src/badge/_example/size.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import { Avatar } from 'tdesign-mobile-react';
import { UserIcon } from 'tdesign-icons-react';

export default function SizeBadge() {
return (
<>
<div className="summary">Large</div>
<div className="block">
<Avatar icon={<UserIcon />} size="large" badgeProps={{ count: 8, size: 'large', offset: [7, 7] }} />
</div>

<div className="summary">Middle</div>
<div className="block">
<Avatar icon={<UserIcon />} badgeProps={{ count: 8, offset: [5, 5] }} />
</div>
</>
);
}
28 changes: 13 additions & 15 deletions src/badge/_example/style/index.less
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
.tdesign-mobile-demo {
.badge-demo {
display: flex;
padding: 16px 0;
width: 100%;
.badge-demo {
display: flex;
margin-top: 28px;
margin-bottom: 24px;
margin-left: 16px;
align-items: center;
color: var(--td-text-color-primary, rgba(0, 0, 0, 0.9));

&__container {
width: 24%;
text-align: center;
position: relative;
}

.badge-item {
width: 24%;
text-align: center;
}
.badge-item {
margin-right: 48px;
}
}

.block {
padding: 16px 16px 24px;
}
Loading
Loading