Browse Source

feat: [TagGroup] TagGroup adds onTagClose method (#1053)

* feat: [TagGroup] TagGroup adds onTagClose method

* docs: optimize example
YyumeiZhang 3 năm trước cách đây
mục cha
commit
e56165e81e

+ 60 - 1
content/show/tag/index-en-US.md

@@ -243,6 +243,64 @@ 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 { 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 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>
+        );
+    }
+}
+```
+
 ## API Reference
 
 ### Tag
@@ -260,7 +318,7 @@ import { TagGroup } from '@douyinfe/semi-ui';
 | visible | Toggle the visibility of the tag | boolean | true |  |
 | tagKey  | The key required by React, as the unique identifier of each tag, does not allow repetition | string | number | |
 | onClick | Callback function when clicking the tag | (e: MouseEvent) => void | - |  |
-| onClose | Callback function when the tag is closed | (tagChildren: ReactNode, e: MouseEvent) => void | - |  |
+| onClose | Callback function when the tag is closed | (tagChildren: ReactNode, e: MouseEvent, tagKey: string \| number ) => void | - | e is available in v1.18, tagKey is available in v2.18.0 |
 
 ### TagGroup
 
@@ -274,6 +332,7 @@ import { TagGroup } from '@douyinfe/semi-ui';
 | size | Size, one of `small`, `large` | string | `small` |  |
 | style | Inline style | CSSProperties |  |  |
 | tagList | Label Group data | (TagProps)[] |  |  |
+| onTagClose | The callback function when deleting the Tag in the TagGroup | (tagChildren: ReactNode, e: MouseEvent, tagKey: string \| number ) => void | - |  2.18.0 |
 
 ## Accessibility
 

+ 60 - 1
content/show/tag/index.md

@@ -204,6 +204,64 @@ import { TagGroup } from '@douyinfe/semi-ui';
 
 ```
 
+如果 TagGroup 中的标签可删除,用户需要在 `onTagClose` 中处理传递给 TagGroup 的 `tagList`。
+
+```jsx live=true
+import React 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 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>
+        );
+    }
+}
+```
+
 ## API参考
 
 ### Tag
@@ -221,7 +279,7 @@ import { TagGroup } from '@douyinfe/semi-ui';
 | visible | 标签是否可见 | boolean | true    | |
 | tagKey  | React 需要的 key,作为每个标签的唯一标识,不允许重复 | string | number | |
 | onClick | 单击标签时的回调函数 | (e: MouseEvent) => void | 无   | |
-| onClose | 关闭标签时的回调函数 | (tagChildren: ReactNode, e: MouseEvent) => void | 无    | e于v1.18版本提供 |
+| onClose | 关闭标签时的回调函数 | (tagChildren: ReactNode, e: MouseEvent, tagKey: string \| number ) => void | 无    | e  v1.18 版本提供, tagKey 于 v2.18.0 提供 |
 
 ### TagGroup
 
@@ -235,6 +293,7 @@ import { TagGroup } from '@douyinfe/semi-ui';
 | size | 标签的尺寸,可选 `small`、 `large` | string | `small` | |
 | style | 样式 | CSSProperties |    | |
 | tagList | 标签组  | (TagProps)[] |     | |
+| onTagClose | 删除TagGroup中的Tag时候的回调函数 | (tagChildren: ReactNode, e: MouseEvent, tagKey: string \| number ) => void | - |  2.18.0 |
 
 ## Accessibility
 

+ 50 - 40
packages/semi-ui/tag/_story/tag.stories.js

@@ -1,5 +1,5 @@
 /* argus-disable unPkgSensitiveInfo */
-import React from 'react';
+import React, { useCallback, useState } from 'react';
 import withPropsCombinations from 'react-storybook-addon-props-combinations';
 import { BASE_CLASS_PREFIX } from '../../../semi-foundation/base/constants';
 
@@ -233,45 +233,55 @@ AvatarTagGroup.story = {
   name: 'avatar tagGroup',
 };
 
-const TagGroupCloseableDemo = () => {
-  const src = 'https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/avatarDemo.jpeg';
-  const tagList2 = [
-      { tagKey: '1', color: 'white', children:'Douyin', avatarSrc:src,closable:true},
-      { tagKey: '2', color: 'white', children:'Hotsoon', avatarSrc:src,closable:true},
-      { tagKey: '3', color: 'white', children:'Capcut', avatarSrc:src,closable:true},
-      { tagKey: '4', color: 'black', children:'bytedance', avatarSrc:src,closable:true},
-      { tagKey: '5', color: 'white', children:'vvvvv', avatarSrc:src,closable:true},
-      { tagKey: '6', color: 'white', children:'Pipixia', avatarSrc:src,closable:true},
-  ];
-  const divStyle = {
-      backgroundColor: 'var(--semi-color-fill-0)',
-      height: 35,
-      width: 300,
-      display: 'flex',
-      alignItems: 'center',
-      padding: '0 10px',
-      marginBottom: 30,
-  };
-  const tagGroupStyle = {
-      display: 'flex',
-      alignItems: 'center',
-      width: 350,
-  };
-  return (
-      <>
-          <div style={divStyle}>
-              <TagGroup
-                  maxTagCount={3}
-                  style={tagGroupStyle}
-                  tagList={tagList2}
-                  size='large'
-                  avatarShape='circle'
-                  showPopover
-              />
-          </div>
-      </>
-  );
-};
+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,},
+            ]
+        };
+        this.tagListClick = this.tagListClick.bind(this);
+    }
+
+    tagListClick(value, e, tagKey){
+        const newTagList = [...this.state.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>
+        );
+    }
+}
 
 export const TagGroupCloseable = () => <TagGroupCloseableDemo />;
 

+ 13 - 43
packages/semi-ui/tag/group.tsx

@@ -1,4 +1,4 @@
-import React, { Children, PureComponent } from 'react';
+import React, { PureComponent } from 'react';
 import classNames from 'classnames';
 import PropTypes from 'prop-types';
 import { cssClasses, strings } from '@douyinfe/semi-foundation/tag/constants';
@@ -21,21 +21,20 @@ export interface TagGroupProps<T> {
     popoverProps?: PopoverProps;
     avatarShape?: AvatarShape;
     mode?: string;
+    onTagClose: (tagChildren: React.ReactNode, event: React.MouseEvent<HTMLElement>, tagKey: string | number) => void;
 }
 
-export type TagGroupState<T> = Pick<TagGroupProps<T>, 'tagList'>
-export default class TagGroup<T> extends PureComponent<TagGroupProps<T>, TagGroupState<T> > {
+export default class TagGroup<T> extends PureComponent<TagGroupProps<T>> {
     static defaultProps = {
         style: {},
         className: '',
         size: tagSize[0],
         avatarShape: 'square',
-        onClose: () => undefined,
+        onTagClose: () => undefined,
     };
 
     static propTypes = {
         children: PropTypes.node,
-        tagKey: PropTypes.oneOf([PropTypes.string, PropTypes.number]),
         style: PropTypes.object,
         className: PropTypes.string,
         maxTagCount: PropTypes.number,
@@ -43,36 +42,12 @@ export default class TagGroup<T> extends PureComponent<TagGroupProps<T>, TagGrou
         tagList: PropTypes.array,
         size: PropTypes.oneOf(tagSize),
         mode: PropTypes.string,
-        onClose: PropTypes.func,
+        onTagClose: PropTypes.func,
         showPopover: PropTypes.bool,
         popoverProps: PropTypes.object,
         avatarShape: PropTypes.oneOf(avatarShapeSet),
     };
 
-    constructor(props: TagGroupProps<T>) {
-        super(props);
-        this.state = {
-            tagList: this.props.tagList
-        };
-        this.onClose = this.onClose.bind(this);
-    }
-
-    static getDerivedStateFromProps(nextProps, prevState) {
-        // 自定义 TagGroup 需要根据 props 来 rerender, normal TagGroup 根据 state 进行 rerender
-        if (nextProps.mode !== 'custom') {
-            return {
-                tagList: prevState.tagList
-            };
-        }
-        if (prevState.tagList !== nextProps.tagList) {
-            return {
-                tagList: nextProps.tagList
-            };
-        }
-        return null;
-    }
-
-    
     renderNTag(n: number, restTags: React.ReactNode) {
         const { size, showPopover, popoverProps } = this.props;
         let nTag = (
@@ -107,9 +82,7 @@ export default class TagGroup<T> extends PureComponent<TagGroupProps<T>, TagGrou
     }
 
     renderMergeTags(tags: (Tag | React.ReactNode)[]) {
-        const { maxTagCount, restCount } = this.props;
-        const { tagList } = this.state;
-
+        const { maxTagCount, tagList, restCount } = this.props;
         const n = restCount ? restCount : tagList.length - maxTagCount;
         let renderTags: (Tag | React.ReactNode)[] = tags;
 
@@ -125,13 +98,11 @@ export default class TagGroup<T> extends PureComponent<TagGroupProps<T>, TagGrou
     }
 
     renderAllTags() {
-        const { size, mode, avatarShape } = this.props;
-        const { tagList } = this.state;
+        const { tagList, size, mode, avatarShape, onTagClose } = this.props;
         const renderTags = tagList.map((tag): (Tag | React.ReactNode) => {
             if (mode === 'custom') {
                 return tag as React.ReactNode;
             }
-            
             if (!(tag as TagProps).size) {
                 (tag as TagProps).size = size;
             }
@@ -147,17 +118,16 @@ export default class TagGroup<T> extends PureComponent<TagGroupProps<T>, TagGrou
                     (tag as TagProps).tagKey = Math.random();
                 }
             }
-            return <Tag {...(tag as TagProps)} key={(tag as TagProps).tagKey} onClose={this.onClose} />;
+            return <Tag {...(tag as TagProps)} key={(tag as TagProps).tagKey} onClose={(tagChildren, e, tagKey) => {
+                if ((tag as TagProps).onClose) {
+                    (tag as TagProps).onClose(tagChildren, e, tagKey);
+                }
+                onTagClose && onTagClose(tagChildren, e, tagKey);
+            }} />;
         });
         return renderTags;
     }
 
-    onClose(value, e, tagKey) {
-        const { tagList } = this.state;
-        const newTagList = tagList.filter(tag => (tag as TagProps).tagKey !== tagKey);
-        this.setState({ tagList: newTagList });
-    }
-
     render() {
         const { style, className, maxTagCount, size } = this.props;
 

+ 1 - 1
packages/semi-ui/tag/interface.ts

@@ -21,7 +21,7 @@ export type TagSize = 'default' | 'small' | 'large';
 export type AvatarShape = 'circle' | 'square';
 
 export interface TagProps {
-    children?: React.ReactNode | string | number;
+    children?: React.ReactNode;
     tagKey?: string | number;
     size?: TagSize;
     color?: TagColor;