ソースを参照

feat: [TagInput] Add the onClose parameter to the renderTagItem of TagInput to support deleting tags (#1239)

Co-authored-by: pointhalo <[email protected]>
YyumeiZhang 3 年 前
コミット
8e50c364ef

+ 7 - 6
content/input/taginput/index-en-US.md

@@ -75,7 +75,6 @@ import { TagInput } from '@douyinfe/semi-ui';
 );
 ```
 
-
 ### Disabled
 
 ```jsx live=true
@@ -355,11 +354,12 @@ class TagInputDemo extends React.Component {
 
 ### Custom TagInput rendering
 
-You can use `renderTagItem` to customize tag rendering.
+You can use `renderTagItem` to customize tag rendering. `renderTagItem(value: string, index: number, onClose: function ) => React.ReactNode` The third parameter `onClose` is available since version 2.23.0.
 
 ```jsx live=true
 import React from 'react';
 import { TagInput, Avatar } from '@douyinfe/semi-ui';
+import { IconClose } from '@douyinfe/semi-ui-icons';
 
 class CustomRender extends React.Component {
     constructor(props) {
@@ -376,7 +376,7 @@ class CustomRender extends React.Component {
         this.mapList = new Map(this.list.map( item => [item.name, item]));
     }
 
-    renderTagItem(value, index) {
+    renderTagItem(value, index, onClose) {
         const data = this.mapList.get(value);
         return (
             <div 
@@ -391,6 +391,7 @@ class CustomRender extends React.Component {
                 <span style={{ marginLeft: 8 }}>
                     {`${value}@semi.com`}
                 </span>
+                <IconClose onClick={onClose} />
             </div>
         );
     }
@@ -401,12 +402,12 @@ class CustomRender extends React.Component {
             <TagInput 
                 value={value} 
                 onChange={value=>this.setState({ value })}
-                renderTagItem={(value, index)=>this.renderTagItem(value, index)}
+                renderTagItem={(value, index, onClose) => this.renderTagItem(value, index, onClose)}
             />
         );
     }
 }
-``` 
+```
 
 ### Drag to sort
 
@@ -449,7 +450,7 @@ import { TagInput } from '@douyinfe/semi-ui';
 |placeholder   |Content to be appear by default                  |string                                                           | -         |1.19.0|
 |prefix        |Prefix                                           |ReactNode                                                        |-          |1.19.0|
 | preventScroll | Indicates whether the browser should scroll the document to display the newly focused element, acting on the focus method inside the component, excluding the component passed in by the user | boolean |  |  |
-|renderTagItem |Customize the rendering of items                 |(value: string, index: number) => React.ReactNode                | -        |1.19.0|
+|renderTagItem |Customize the rendering of items, The parameter onClose is available in version 2.23.0  |<ApiType detail='(value: string, index: number, onClose: function) => React.ReactNode'>(params) => React.ReactNode</ApiType> | -        |1.19.0|
 |separator     |Customize the separator                          |string\|string[]                                                 |,          |1.19.0,string[] is supported since 1.29.0|
 |showClear     |Whether to show the clear button                 |boolean                                                          |false      |1.19.0|
 |size          |Size, one of `small`、`large`、`default`          |string                                                           |`default` |1.19.0|

+ 7 - 5
content/input/taginput/index.md

@@ -355,11 +355,12 @@ class TagInputDemo extends React.Component {
 
 ### 自定义标签渲染
 
-可以使用 `renderTagItem` 自定义标签渲染。
+可以使用 `renderTagItem` 自定义标签渲染。 `renderTagItem(value: string, index: number, onClose: function ) => React.ReactNode` 第三个参数 `onClose` 于 2.23.0 版本开始提供。
 
 ```jsx live=true
 import React from 'react';
 import { TagInput, Avatar } from '@douyinfe/semi-ui';
+import { IconClose } from '@douyinfe/semi-ui-icons';
 
 class CustomRender extends React.Component {
     constructor(props) {
@@ -376,7 +377,7 @@ class CustomRender extends React.Component {
         this.mapList = new Map(this.list.map( item => [item.name, item]));
     }
 
-    renderTagItem(value, index) {
+    renderTagItem(value, index, onClose) {
         const data = this.mapList.get(value);
         return (
             <div 
@@ -391,6 +392,7 @@ class CustomRender extends React.Component {
                 <span style={{ marginLeft: 8 }}>
                     {`${value}@semi.com`}
                 </span>
+                <IconClose onClick={onClose} />
             </div>
         );
     }
@@ -401,12 +403,12 @@ class CustomRender extends React.Component {
             <TagInput 
                 value={value} 
                 onChange={value => this.setState({ value })}
-                renderTagItem={(value, index) => this.renderTagItem(value, index)}
+                renderTagItem={(value, index, onClose) => this.renderTagItem(value, index, onClose)}
             />
         );
     }
 }
-``` 
+```
 
 ### 拖拽排序
 
@@ -449,7 +451,7 @@ import { TagInput } from '@douyinfe/semi-ui';
 |placeholder  |占位默认值                                         |string                         | -         |1.19.0|
 |prefix       |前缀标签                                           |ReactNode                      |-          |1.19.0|
 | preventScroll | 指示浏览器是否应滚动文档以显示新聚焦的元素,作用于组件内的 focus 方法 | boolean |  |  |
-|renderTagItem|自定义标签渲染                                      |(value: string, index: number) => React.ReactNode | -        |1.19.0|
+|renderTagItem|自定义标签渲染, 参数 onClose 于版本2.23.0版本提供     | <ApiType detail='(value: string, index: number, onClose: function) => React.ReactNode'>(params) => React.ReactNode</ApiType> | -  |1.19.0|
 |separator    |设置批量输入时的分隔符                               |string\|string[]                         |,    |1.19.0, string[]是从1.29.0开始支持|
 |showClear    |是否支持一键删除所有标签和输入内容                     |boolean                        |false      |1.19.0|
 |size         |设置输入框尺寸,可选: `small`、`large`、`default`     |string                          |`default` |1.19.0|

+ 9 - 0
cypress/integration/tagInput.spec.js

@@ -0,0 +1,9 @@
+describe('tag', () => {
+    it('tagInput with renderTagItem', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=taginput--render-tag-item&args=&viewMode=story');
+
+        // focus and esc
+        cy.get('.semi-icon-close').click();
+        cy.get('.semi-icon-close').should('not.exist');
+    });
+});

+ 26 - 11
packages/semi-ui/tagInput/_story/tagInput.stories.jsx

@@ -1,7 +1,7 @@
-import React from 'react';
-import { Toast, Icon, Button, Avatar, Form } from '@douyinfe/semi-ui/';
+import React, { useState } from 'react';
+import { Toast, Icon, Button, Avatar, Form, Switch } from '@douyinfe/semi-ui/';
 import TagInput from '../index';
-import { IconGift, IconVigoLogo } from '@douyinfe/semi-icons';
+import { IconGift, IconVigoLogo, IconClose } from '@douyinfe/semi-icons';
 const style = {
   width: 400,
   marginTop: 10,
@@ -344,6 +344,7 @@ class CustomRender extends React.Component {
   constructor() {
     super();
     this.state = {
+      draggable: false,
       list: [
         {
           name: 'semi',
@@ -355,7 +356,7 @@ class CustomRender extends React.Component {
     };
   }
 
-  renderTagItem(node, index) {
+  renderTagItem(node, index, onClose) {
     return (
       <div
         key={index}
@@ -376,6 +377,7 @@ class CustomRender extends React.Component {
         >
           {node.email}
         </span>
+        <IconClose onClick={onClose} />
       </div>
     );
   }
@@ -393,15 +395,28 @@ class CustomRender extends React.Component {
     });
   }
 
+  onSwitchChange(value) {
+    this.setState({
+      draggable: value
+    });
+  }
+
   render() {
-    const { list } = this.state;
+    const { list, draggable } = this.state;
     return (
-      <TagInput
-        style={style}
-        value={list}
-        onChange={value => this.handleChange(value)}
-        renderTagItem={(node, index) => this.renderTagItem(node, index)}
-      />
+      <>
+        <div style={{ display: 'flex', alignItems: 'center', marginBottom: 20 }}>
+          <span>是否可拖拽:</span>
+          <Switch checked={draggable} onChange={(value) => this.onSwitchChange(value)} />
+        </div>
+        <TagInput
+          draggable={draggable}
+          style={style}
+          value={list}
+          onChange={value => this.handleChange(value)}
+          renderTagItem={(node, index, onClose) => this.renderTagItem(node, index, onClose)}
+        />
+      </>
     );
   }
 }

+ 7 - 6
packages/semi-ui/tagInput/index.tsx

@@ -68,7 +68,7 @@ export interface TagInputProps {
     insetLabel?: React.ReactNode;
     insetLabelId?: string;
     prefix?: React.ReactNode;
-    renderTagItem?: (value: string, index: number) => React.ReactNode;
+    renderTagItem?: (value: string, index: number, onClose: () => void) => React.ReactNode;
     separator?: string | string[] | null;
     showClear?: boolean;
     size?: Size;
@@ -416,11 +416,14 @@ class TagInput extends BaseComponent<TagInputProps, TagInputState> {
         const DragHandle = SortableHandle(() => <IconHandle className={`${prefixCls}-drag-handler`}></IconHandle>);
         return tagsArray.map((value, index) => {
             const elementKey = showIconHandler ? value : `${index}${value}`;
+            const onClose = () => {
+                !disabled && this.handleTagClose(index);
+            };
             if (isFunction(renderTagItem)) {
                 return showIconHandler? (<div className={itemWrapperCls} key={elementKey}>
                     <DragHandle />
-                    {renderTagItem(value, index)}
-                </div>) : renderTagItem(value, index);
+                    {renderTagItem(value, index, onClose)}
+                </div>) : renderTagItem(value, index, onClose);
             } else {
                 return (
                     <Tag
@@ -428,9 +431,7 @@ class TagInput extends BaseComponent<TagInputProps, TagInputState> {
                         color="white"
                         size={size === 'small' ? 'small' : 'large'}
                         type="light"
-                        onClose={() => {
-                            !disabled && this.handleTagClose(index);
-                        }}
+                        onClose={onClose}
                         closable={!disabled}
                         key={elementKey}
                         visible