Преглед изворни кода

feat: tag support suffixIcon、prefixIcon (#1832)

* feat: tag support suffixIcon、prefixIcon

* docs: update tag usage
pointhalo пре 2 година
родитељ
комит
1e4b31f644

+ 106 - 68
content/show/tag/index-en-US.md

@@ -45,15 +45,13 @@ Supports two sizes: `large` and `small` (default).
 
 ```jsx live=true
 import React from 'react';
-import { Tag } from '@douyinfe/semi-ui';
+import { Tag, Space } from '@douyinfe/semi-ui';
 
 () => (
-    <div>
-        <Tag size="small" style={{ marginRight: 8 }}>
-            small tag
-        </Tag>
-        <Tag size="large"> large tag </Tag>
-    </div>
+    <Space>
+        <Tag size="small" color='light-blue'> small tag </Tag>
+        <Tag size="large" color='cyan'> large tag </Tag>
+    </Space>
 );
 ```
 
@@ -67,8 +65,8 @@ import { Tag, Space } from '@douyinfe/semi-ui';
 
 () => (
     <Space>
-        <Tag size="small" shape='circle'> small circle tag </Tag>
-        <Tag size="large" shape='circle'> large circle tag </Tag>
+        <Tag size="small" shape='circle' color='amber'> small circle tag </Tag>
+        <Tag size="large" shape='circle' color='violet'> large circle tag </Tag>
     </Space>
 );
 ```
@@ -88,7 +86,7 @@ import { Tag, Space } from '@douyinfe/semi-ui';
                 ['amber', 'blue', 'cyan', 'green', 'grey', 'indigo',  
                     'light-blue', 'light-green', 'lime', 'orange', 'pink',  
                     'purple', 'red', 'teal', 'violet', 'yellow', 'white'
-                ].map(item => (<Tag color={item} key={item}> {item} tag</Tag>))
+                ].map(item => (<Tag color={item} key={item}> {item} </Tag>))
             }
         </Space>
     );
@@ -102,18 +100,61 @@ Tag supports three different types, including: `light`(default), `ghost`, `solid
 ```jsx live=true
 import React from 'react';
 import { Tag, Space } from '@douyinfe/semi-ui';
+import { IconGithubLogo, IconSemiLogo } from '@douyinfe/semi-icons';
 
 () => (
-    <Space>
-        <Tag color="blue" type="light">
-            light tag
+    <Space wrap>
+        <Tag
+            color='light-blue'
+            prefixIcon={<IconGithubLogo />}
+            size='large'
+            shape='circle'
+            type='light'
+        >
+            Semi Design Light Tag
         </Tag>
-        <Tag color="blue" type="ghost">
-            ghost tag
+        <Tag
+            color='cyan'
+            size='large'
+            shape='circle'
+            suffixIcon={<IconSemiLogo />}
+            type='light'
+        >
+            D2C: figma to code in one click</Tag>
+        <Tag
+            color='light-blue'
+            prefixIcon={<IconGithubLogo />}
+            size='large'
+            shape='circle'
+            type='ghost'
+        >
+            Semi Design Ghost Tag
         </Tag>
-        <Tag color="blue" type="solid">
-            solid tag
+        <Tag
+            color='cyan'
+            size='large'
+            shape='circle'
+            type='ghost'
+            suffixIcon={<IconSemiLogo />}
+        >
+            D2C: figma to code in one click</Tag>
+        <Tag
+            color='light-blue'
+            prefixIcon={<IconGithubLogo />}
+            size='large'
+            shape='circle'
+            type='solid'
+        >
+            Semi Design Solid Tag
         </Tag>
+        <Tag
+            color='cyan'
+            size='large'
+            shape='circle'
+            type='solid'
+            suffixIcon={<IconSemiLogo />}
+        >
+            D2C: figma to code in one click</Tag>
     </Space>
 );
 ```
@@ -158,7 +199,7 @@ You can use `visible` property to control whether the tag is visible.
 
 ```jsx live=true
 import React, { useState } from 'react';
-import { Tag, Button } from '@douyinfe/semi-ui';
+import { Tag, Button, RadioGroup, Radio } from '@douyinfe/semi-ui';
 
 () => {
     const [visible, setVisible] = useState(false);
@@ -167,13 +208,17 @@ import { Tag, Button } from '@douyinfe/semi-ui';
     };
     return (
         <div>
-            <Button onClick={toggleVisible}>{visible ? 'Hide Tag' : 'Show Tag'}</Button>
+            <RadioGroup type='button' defaultValue={0} onChange={e => toggleVisible(e.target.value)}>
+                <Radio value={1}>Show</Radio>
+                <Radio value={0}>Hide</Radio>
+            </RadioGroup>
             <div style={{ marginTop: 10 }}>
-                <Tag visible={visible}>Invisible tag </Tag>
+                <Tag visible={visible} size='large' color='light-blue'>Invisible tag </Tag>
             </div>
         </div>
     );
 };
+
 ```
 
 ### TagGroup
@@ -244,58 +289,49 @@ import { TagGroup } from '@douyinfe/semi-ui';
 If the tags in the TagGroup can be deleted, the user needs to process the `tagList` passed to the TagGroup in `onTagClose`
 
 ```jsx live=true
-import React from 'react';
+import React, { useState } from 'react';
 import { TagGroup } from '@douyinfe/semi-ui';
 
-class TagGroupCloseableDemo extends React.Component {
-    constructor(props){
-        super(props);
-        this.state = {
-            tagList: [
-                { tagKey: '1', color: 'white', children: '抖音', closable: true, },
-                { tagKey: '2', color: 'white', children: '火山小视频', closable: true, },
-                { tagKey: '3', color: 'white', children: '剪映', closable: true, },
-                { tagKey: '4', color: 'white', children: '皮皮虾', closable: true, },
-                { tagKey: '5', color: 'white', children: '懂车帝', closable: true, },
-            ]
-        };
-        this.tagListClick = this.tagListClick.bind(this);
-    }
-
-    tagListClick(value, e, tagKey){
-        const newTagList = [...this.state.tagList];
+() => {
+    const defaultList = [
+        { tagKey: '1', color: 'light-blue', children: 'Douyin', closable: true, },
+        { tagKey: '3', color: 'amber', children: 'Jianying', closable: true, },
+        { tagKey: '3', color: 'violet', children: 'Faceu', closable: true, },
+        { tagKey: '4', color: 'white', children: 'Lark', closable: true, },
+    ];
+
+    const [tagList, setTagList] = useState(defaultList);
+
+    const tagListClick = (value, e, tagKey) => {
+        const newTagList = [...tagList];
         const closeTagIndex = newTagList.findIndex(t => t.tagKey === tagKey);
         newTagList.splice(closeTagIndex, 1);
-        this.setState({
-            tagList: newTagList,
-        });
-    }
-
-    render() {
-        return (
-            <div style={ {
-                backgroundColor: 'var(--semi-color-fill-0)',
-                height: 35,
-                width: 300,
-                display: 'flex',
-                alignItems: 'center',
-                padding: '0 10px',
-                marginBottom: 30,
-            }}>
-                <TagGroup
-                    maxTagCount={3}
-                    style={ {
-                        display: 'flex',
-                        alignItems: 'center',
-                        width: 350,
-                    }}
-                    tagList={this.state.tagList}
-                    size='large'
-                    onTagClose={this.tagListClick}
-                />
-            </div>
-        );
-    }
+        setTagList(newTagList);
+    };
+
+    return (
+        <div style={ {
+            backgroundColor: 'var(--semi-color-fill-0)',
+            height: 35,
+            width: 300,
+            display: 'flex',
+            alignItems: 'center',
+            padding: '0 10px',
+            marginBottom: 30,
+        }}>
+            <TagGroup
+                maxTagCount={3}
+                style={ {
+                    display: 'flex',
+                    alignItems: 'center',
+                    width: 350,
+                }}
+                tagList={tagList}
+                size='large'
+                onTagClose={tagListClick}
+            />
+        </div>
+    );
 }
 ```
 
@@ -310,6 +346,8 @@ class TagGroupCloseableDemo extends React.Component {
 | className | Class name | string |  |  |
 | closable | Toggle whether the tag can be closed | boolean | false |  |
 | color | Color of tags, one of `amber`、 `blue`、 `cyan`、 `green`、 `grey`、 `indigo`、 `light-blue`、 `light-green`、 `lime`、 `orange`、 `pink`、 `purple`、 `red`、 `teal`、 `violet`、 `yellow`、 `white` | string | `grey` |  |
+| prefixIcon | prefix icon | ReactNode | | 2.44.0 |
+| suffixIcon | suffix icon | ReactNode | | 2.44.0 |
 | shape | Shape of tag, one of `square`、 `circle` | string | `square` | 2.20.0 |
 | size | Size, one of `small`, `large` | string | `small` |  |
 | style | Inline style | object |  |  |

+ 140 - 72
content/show/tag/index.md

@@ -36,7 +36,6 @@ import { Tag, Space } from '@douyinfe/semi-ui';
     </div>
 );
 ```
-
 ### 尺寸
 
 默认定义了两种尺寸:大、小(默认)。
@@ -47,8 +46,8 @@ import { Tag, Space } from '@douyinfe/semi-ui';
 
 () => (
     <Space>
-        <Tag size="small"> small tag </Tag>
-        <Tag size="large"> large tag </Tag>
+        <Tag size="small" color='light-blue'> small tag </Tag>
+        <Tag size="large" color='cyan'> large tag </Tag>
     </Space>
 );
 ```
@@ -63,12 +62,42 @@ import { Tag, Space } from '@douyinfe/semi-ui';
 
 () => (
     <Space>
-        <Tag size="small" shape='circle'> small circle tag </Tag>
-        <Tag size="large" shape='circle'> large circle tag </Tag>
+        <Tag size="small" shape='circle' color='amber'> small circle tag </Tag>
+        <Tag size="large" shape='circle' color='violet'> large circle tag </Tag>
     </Space>
 );
 ```
 
+### 配置图标
+v2.44 后支持通过配置 prefixIcon、suffixIcon, 可以在 children 内容前后添加 Icon 图标 
+
+```jsx live=true
+import React from 'react';
+import { Tag, Space } from '@douyinfe/semi-ui';
+import { IconGithubLogo, IconSemiLogo } from '@douyinfe/semi-icons';
+
+() => (
+    <Space>
+        <Tag
+            color='light-blue'
+            prefixIcon={<IconGithubLogo />}
+            size='large'
+            shape='circle'
+        >
+            Semi Design
+        </Tag>
+        <Tag
+            color='cyan'
+            size='large'
+            shape='circle'
+            suffixIcon={<IconSemiLogo />}
+        >
+            D2C: figma to code in one click</Tag>
+    </Space>
+);
+
+```
+
 ### 颜色
 
 标签支持默认色板的 16 种颜色和白色,包括:`amber`、 `blue`、 `cyan`、 `green`、 `grey`、 `indigo`、 `light-blue`、 `light-green`、 `lime`、 `orange`、 `pink`、 `purple`、 `red`、 `teal`、 `violet`、 `yellow`、 `white`,也可以通过 style 来自定义颜色样式。
@@ -83,7 +112,7 @@ import { Tag, Space } from '@douyinfe/semi-ui';
             ['amber', 'blue', 'cyan', 'green', 'grey', 'indigo',  
                 'light-blue', 'light-green', 'lime', 'orange', 'pink',  
                 'purple', 'red', 'teal', 'violet', 'yellow', 'white'
-            ].map(item => (<Tag color={item} key={item}> {item} tag </Tag>))
+            ].map(item => (<Tag color={item} key={item}> {item} </Tag>))
         }
     </Space>
 );
@@ -91,26 +120,66 @@ import { Tag, Space } from '@douyinfe/semi-ui';
 
 ### 样式类型
 
-标签支持三种样式类型,包括浅色底色 `light`,白色底色 `ghost`,深色底色 `solid`;默认值为 `light`。
+标签支持三种样式类型,包括浅色底色 `light`,白色底色 `ghost`,深色底色 `solid`;默认值为 `light`。通过 type 配置
 
 ```jsx live=true
 import React from 'react';
 import { Tag, Space } from '@douyinfe/semi-ui';
+import { IconGithubLogo, IconSemiLogo } from '@douyinfe/semi-icons';
 
 () => (
-    <Space>
-        <Tag color="blue" type="light">
-            {' '}
-            light tag{' '}
+    <Space wrap>
+        <Tag
+            color='light-blue'
+            prefixIcon={<IconGithubLogo />}
+            size='large'
+            shape='circle'
+            type='light'
+        >
+            Semi Design Light Tag
         </Tag>
-        <Tag color="blue" type="ghost">
-            {' '}
-            ghost tag{' '}
+        <Tag
+            color='cyan'
+            size='large'
+            shape='circle'
+            suffixIcon={<IconSemiLogo />}
+            type='light'
+        >
+            D2C: figma to code in one click</Tag>
+        <Tag
+            color='light-blue'
+            prefixIcon={<IconGithubLogo />}
+            size='large'
+            shape='circle'
+            type='ghost'
+        >
+            Semi Design Ghost Tag
         </Tag>
-        <Tag color="blue" type="solid">
-            {' '}
-            solid tag{' '}
+        <Tag
+            color='cyan'
+            size='large'
+            shape='circle'
+            type='ghost'
+            suffixIcon={<IconSemiLogo />}
+        >
+            D2C: figma to code in one click</Tag>
+        <Tag
+            color='light-blue'
+            prefixIcon={<IconGithubLogo />}
+            size='large'
+            shape='circle'
+            type='solid'
+        >
+            Semi Design Solid Tag
         </Tag>
+        <Tag
+            color='cyan'
+            size='large'
+            shape='circle'
+            type='solid'
+            suffixIcon={<IconSemiLogo />}
+        >
+            D2C: figma to code in one click</Tag>
     </Space>
 );
 ```
@@ -154,7 +223,7 @@ function Demo() {
 
 ```jsx live=true
 import React, { useState } from 'react';
-import { Tag, Button } from '@douyinfe/semi-ui';
+import { Tag, Button, RadioGroup, Radio } from '@douyinfe/semi-ui';
 
 () => {
     const [visible, setVisible] = useState(false);
@@ -163,13 +232,17 @@ import { Tag, Button } from '@douyinfe/semi-ui';
     };
     return (
         <div>
-            <Button onClick={toggleVisible}>{visible ? 'Hide Tag': 'Show Tag'}</Button>
+            <RadioGroup type='button' defaultValue={0} onChange={e => toggleVisible(e.target.value)}>
+                <Radio value={1}>Show</Radio>
+                <Radio value={0}>Hide</Radio>
+            </RadioGroup>
             <div style={{ marginTop: 10 }}>
-                <Tag visible={visible}>Invisible tag </Tag>
+                <Tag visible={visible} size='large' color='light-blue'>Invisible tag </Tag>
             </div>
         </div>
     );
 };
+
 ```
 
 ### TagGroup 使用
@@ -183,9 +256,9 @@ import { TagGroup } from '@douyinfe/semi-ui';
 
 () => {
     const tagList = [
-        { color: 'white', children: '抖音' },
-        { color: 'white', children: '火山' },
-        { color: 'white', children: '剪映' },
+        { color: 'light-blue', children: '抖音' },
+        { color: 'cyan', children: '火山' },
+        { color: 'violet', children: '剪映' },
         { color: 'white', children: '醒图' },
     ];
     const src = 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/dy.png';
@@ -232,59 +305,52 @@ import { TagGroup } from '@douyinfe/semi-ui';
 如果 TagGroup 中的标签可删除,用户需要在 `onTagClose` 中处理传递给 TagGroup 的 `tagList`。
 
 ```jsx live=true
-import React from 'react';
+import React, { useState } from 'react';
 import { TagGroup } from '@douyinfe/semi-ui';
 
-class TagGroupCloseableDemo extends React.Component {
-    constructor(props){
-        super(props);
-        this.state = {
-            tagList: [
-                { tagKey: '1', color: 'white', children: '抖音', closable: true, },
-                { tagKey: '2', color: 'white', children: '火山小视频', closable: true, },
-                { tagKey: '3', color: 'white', children: '剪映', closable: true, },
-                { tagKey: '4', color: 'white', children: '轻颜相机', closable: true, },
-                { tagKey: '5', color: 'white', children: '懂车帝', closable: true, },
-            ]
-        };
-        this.tagListClick = this.tagListClick.bind(this);
-    }
-
-    tagListClick(value, e, tagKey){
-        const newTagList = [...this.state.tagList];
+() => {
+    const defaultList = [
+        { tagKey: '1', color: 'light-blue', children: '抖音', closable: true, },
+        { tagKey: '3', color: 'cyan', children: '剪映', closable: true, },
+        { tagKey: '2', color: 'violet', children: '醒图', closable: true, },
+        { tagKey: '4', color: 'teal', children: '轻颜相机', closable: true, },
+        { tagKey: '5', color: 'white', children: '飞书', closable: true, },
+    ];
+
+    const [tagList, setTagList] = useState(defaultList);
+
+    const tagListClick = (value, e, tagKey) => {
+        const newTagList = [...tagList];
         const closeTagIndex = newTagList.findIndex(t => t.tagKey === tagKey);
         newTagList.splice(closeTagIndex, 1);
-        this.setState({
-            tagList: newTagList,
-        });
-    }
-
-    render() {
-        return (
-            <div style={ {
-                backgroundColor: 'var(--semi-color-fill-0)',
-                height: 35,
-                width: 300,
-                display: 'flex',
-                alignItems: 'center',
-                padding: '0 10px',
-                marginBottom: 30,
-            }}>
-                <TagGroup
-                    maxTagCount={3}
-                    style={ {
-                        display: 'flex',
-                        alignItems: 'center',
-                        width: 350,
-                    }}
-                    tagList={this.state.tagList}
-                    size='large'
-                    onTagClose={this.tagListClick}
-                />
-            </div>
-        );
-    }
-}
+        setTagList(newTagList);
+    };
+
+    return (
+        <div style={ {
+            backgroundColor: 'var(--semi-color-fill-0)',
+            height: 35,
+            width: 300,
+            display: 'flex',
+            alignItems: 'center',
+            padding: '0 10px',
+            marginBottom: 30,
+        }}>
+            <TagGroup
+                maxTagCount={3}
+                showPopover
+                style={ {
+                    display: 'flex',
+                    alignItems: 'center',
+                    width: 350,
+                }}
+                tagList={tagList}
+                size='large'
+                onTagClose={tagListClick}
+            />
+        </div>
+    );
+};
 ```
 
 ## API参考
@@ -298,6 +364,8 @@ class TagGroupCloseableDemo extends React.Component {
 | className | 类名 | string |     | |
 | closable | 标签是否可以关闭 | boolean  |  false   | |
 | color  | 标签的颜色,可选 `amber`、 `blue`、 `cyan`、 `green`、 `grey`、 `indigo`、 `light-blue`、 `light-green`、 `lime`、 `orange`、 `pink`、 `purple`、 `red`、 `teal`、 `violet`、 `yellow`、 `white` | string  | `grey`| |
+| prefixIcon | 前缀图标 | ReactNode | | 2.44.0 |
+| suffixIcon | 后缀图标 | ReactNode | | 2.44.0 |
 | shape | 标签的形状,可选 `square`、 `circle` | string | `square` | 2.20.0 |
 | size | 标签的尺寸,可选 `small`、 `large` | string | `small` | |
 | style | 样式 | CSSProperties |     | |

+ 10 - 0
packages/semi-foundation/tag/tag.scss

@@ -51,6 +51,16 @@ $types: "ghost", "solid", "light";
         display: none;
     }
 
+    &-prefix-icon {
+        display: flex;
+        padding-right: $spacing-tag_prefix_icon_paddingRight;
+    }
+
+    &-suffix-icon {
+        display: flex;
+        padding-left: $spacing-tag_suffix_icon_paddingLeft;
+    }
+
     &-content {
         flex: 1;
 

+ 3 - 0
packages/semi-foundation/tag/variables.scss

@@ -25,6 +25,9 @@ $radius-tag_circle: var(--semi-border-radius-full); // 胶囊标签圆角大小
 $spacing-tag_small-paddingY: 2px; // 小尺寸标签垂直方向内边距
 $spacing-tag_small-paddingX: $spacing-tight; // 小尺寸标签水平方向内边距
 
+$spacing-tag_prefix_icon_paddingRight: $spacing-extra-tight; // 后缀图标右侧边距 
+$spacing-tag_suffix_icon_paddingLeft: $spacing-extra-tight; // 后缀图标左侧边距
+
 $spacing-tag_large-paddingY: 4px; // 大尺寸标签垂直方向内边距
 $spacing-tag_large-paddingX: $spacing-tight; // 大尺寸标签水平方向内边距
 

+ 7 - 1
packages/semi-ui/tag/index.tsx

@@ -38,6 +38,8 @@ export default class Tag extends Component<TagProps, TagState> {
         className: '',
         shape: 'square',
         avatarShape: 'square',
+        prefixIcon: null,
+        suffixIcon: null
     };
 
     static propTypes = {
@@ -50,6 +52,8 @@ export default class Tag extends Component<TagProps, TagState> {
         visible: PropTypes.bool,
         onClose: PropTypes.func,
         onClick: PropTypes.func,
+        prefixIcon: PropTypes.node,
+        suffixIcon: PropTypes.node,
         style: PropTypes.object,
         className: PropTypes.string,
         avatarSrc: PropTypes.string,
@@ -122,7 +126,7 @@ export default class Tag extends Component<TagProps, TagState> {
     }
 
     render() {
-        const { tagKey, children, size, color, closable, visible, onClose, onClick, className, type, shape, avatarSrc, avatarShape, tabIndex, ...attr } = this.props;
+        const { tagKey, children, size, color, closable, visible, onClose, onClick, className, type, shape, avatarSrc, avatarShape, tabIndex, prefixIcon, suffixIcon, ...attr } = this.props;
         const { visible: isVisible } = this.state;
         const clickable = onClick !== Tag.defaultProps.onClick || closable;
         // only when the Tag is clickable or closable, the value of tabIndex is allowed to be passed in. 
@@ -160,10 +164,12 @@ export default class Tag extends Component<TagProps, TagState> {
 
         return (
             <div aria-label={this.props['aria-label'] || stringChild ? `${closable ? 'Closable ' : ''}Tag: ${children}` : '' } {...wrapProps}>
+                {prefixIcon ? <div className={`${prefixCls}-prefix-icon`}>{prefixIcon}</div> : null}
                 {avatarSrc ? this.renderAvatar() : null}
                 <div className={contentCls}>
                     {children}
                 </div>
+                {suffixIcon ? <div className={`${prefixCls}-suffix-icon`}>{suffixIcon}</div> : null}
                 {closeIcon}
             </div>
         );

+ 3 - 0
packages/semi-ui/tag/interface.ts

@@ -1,3 +1,4 @@
+import React from 'react';
 import { PopoverProps } from '../popover/index';
 
 export type TagColor =
@@ -33,6 +34,8 @@ export interface TagProps {
     visible?: boolean;
     onClose?: (tagChildren: React.ReactNode, event: React.MouseEvent<HTMLElement>, tagKey: string | number) => void;
     onClick?: React.MouseEventHandler<HTMLDivElement>;
+    prefixIcon?: React.ReactNode;
+    suffixIcon?: React.ReactNode;
     style?: React.CSSProperties;
     className?: string;
     avatarSrc?: string;