فهرست منبع

Feat: [TreeSelect] adds showRestTagsPopover and restTagsPopoverProps parameters (#1210)

* feat: [TreeSelect] TreeSelect adds showRestTagsPopover and restTagsPopoverProps to support displaying redundant tags through popover

* fix: Set the expandRestTagsOnClick of TagInput in Cascader/TreeSelect to false to disable the function of clicking to expand redundant tags
YyumeiZhang 3 سال پیش
والد
کامیت
eadc7a15f9

+ 101 - 7
content/input/treeselect/index-en-US.md

@@ -75,7 +75,8 @@ import { TreeSelect } from '@douyinfe/semi-ui';
 
 
 ### Multi-choice
 ### Multi-choice
 
 
-You could use `multiple` to set mode to multi-choice. When all child items are selected, the parent item will be selected. Use `maxTagCount` to set the cap number of tags displayed. Use `leafOnly` (>= v0.32.0) if you prefer to render leaf nodes only and the corresponding params for onChange will also be leaf nodes values.
+You could use `multiple` to set mode to multi-choice. When all child items are selected, the parent item will be selected.  
+Use `leafOnly` (>= v0.32.0) if you prefer to render leaf nodes only and the corresponding params for onChange will also be leaf nodes values.  
 
 
 ```jsx live=true
 ```jsx live=true
 import React from 'react';
 import React from 'react';
@@ -158,18 +159,109 @@ import { TreeSelect } from '@douyinfe/semi-ui';
                 dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
                 dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
                 treeData={treeData}
                 treeData={treeData}
                 multiple
                 multiple
-                maxTagCount={2}
-                placeholder="Display at most two tags"
+                leafOnly
+                placeholder="Display leaf nodes only"
             />
             />
-            <br/>
-            <br/>
+        </div>
+    );
+};
+```
+
+### Limit Tags Number
+
+In the multi-selection scenario, `maxTagCount` can be used to limit the number of tags displayed, and the excess part will be displayed in the form of +N.  
+Use `showRestTagsPopover` (>= v2.22.0) to set whether hover +N displays Popover after exceeding `maxTagCount`, the default is `false`. Also, popovers can be configured in the `restTagsPopoverProps` property.  
+
+```jsx live=true
+import React from 'react';
+import { TreeSelect } from '@douyinfe/semi-ui';
+() => {
+    const treeData = [
+        {
+            label: 'Asia',
+            value: 'Asia',
+            key: '0',
+            children: [
+                {
+                    label: 'China',
+                    value: 'China',
+                    key: '0-0',
+                    children: [
+                        {
+                            label: 'Beijing',
+                            value: 'Beijing',
+                            key: '0-0-0',
+                        },
+                        {
+                            label: 'Shanghai',
+                            value: 'Shanghai',
+                            key: '0-0-1',
+                        },
+                        {
+                            label: 'Chengdu',
+                            value: 'Chengdu',
+                            key: '0-0-2',
+                        },
+                    ],
+                },
+                {
+                    label: 'Japan',
+                    value: 'Japan',
+                    key: '0-1',
+                    children: [
+                        {
+                            label: 'Osaka',
+                            value: 'Osaka',
+                            key: '0-1-0'
+                        }
+                    ]
+                },
+            ],
+        },
+        {
+            label: 'North America',
+            value: 'North America',
+            key: '1',
+            children: [
+                {
+                    label: 'United States',
+                    value: 'United States',
+                    key: '1-0'
+                },
+                {
+                    label: 'Canada',
+                    value: 'Canada',
+                    key: '1-1'
+                }
+            ]
+        }
+    ];
+
+    const textStyle = { margin: '20px 0 10px' };
+
+    return ( 
+        <div>
+            <h4 style={textStyle}>maxTagCount=2:</h4>
             <TreeSelect
             <TreeSelect
+                multiple
+                maxTagCount={2}
                 style={{ width: 300 }}
                 style={{ width: 300 }}
                 dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
                 dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
                 treeData={treeData}
                 treeData={treeData}
+                placeholder="When more than two tabs are selected it will collapse"
+                defaultValue={['Beijing', 'Chengdu', 'Canada']}
+            />
+            <h4 style={textStyle}>maxTagCount=2, showRestTagsPopover:</h4>
+            <TreeSelect
+                showRestTagsPopover={true}
+                restTagsPopoverProps={{ position: 'top' }}
                 multiple
                 multiple
-                leafOnly
-                placeholder="Display leaf nodes only"
+                maxTagCount={2}
+                style={{ width: 300 }}
+                dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
+                treeData={treeData}
+                placeholder="hover +N to view"
+                defaultValue={['Beijing', 'Chengdu', 'Canada']}
             />
             />
         </div>
         </div>
     );
     );
@@ -1325,11 +1417,13 @@ function Demo() {
 | renderFullLabel | Custom option render function, [Detailed Params and Usage](/en-US/navigation/tree#Advanced%20FullRender) | (obj) => ReactNode | 1.7.0 |
 | renderFullLabel | Custom option render function, [Detailed Params and Usage](/en-US/navigation/tree#Advanced%20FullRender) | (obj) => ReactNode | 1.7.0 |
 | renderLabel | Custom label render function | (label:ReactNode, data:TreeNode) => ReactNode | 1.6.0 | 
 | renderLabel | Custom label render function | (label:ReactNode, data:TreeNode) => ReactNode | 1.6.0 | 
 | renderSelectedItem | render selected item | Function | - | 1.26.0 | 
 | renderSelectedItem | render selected item | Function | - | 1.26.0 | 
+| restTagsPopoverProps | The configuration properties of the [Popover](/en-US/show/popover#API%20Reference)     | PopoverProps     | {}        | 2.22.0 |
 | searchAutoFocus        | Whether autofocus for search box           | boolean      | false           | 1.27.0       |
 | searchAutoFocus        | Whether autofocus for search box           | boolean      | false           | 1.27.0       |
 | searchPlaceholder        | Placeholder for search box                                                          | string                                                            | -           | -       |
 | searchPlaceholder        | Placeholder for search box                                                          | string                                                            | -           | -       |
 | searchPosition | Set the position of the search box, one of: `dropdown`、`trigger` | string | `dropdown` | 1.29.0 |
 | searchPosition | Set the position of the search box, one of: `dropdown`、`trigger` | string | `dropdown` | 1.29.0 |
 | showClear | When the value is not empty, whether the trigger displays the clear button | boolean | false |  |
 | showClear | When the value is not empty, whether the trigger displays the clear button | boolean | false |  |
 | showFilteredOnly | Toggle whether to displayed filtered result only in search mode | boolean | false | 0.32.0 |
 | showFilteredOnly | Toggle whether to displayed filtered result only in search mode | boolean | false | 0.32.0 |
+| showRestTagsPopover | When the number of tags exceeds maxTagCount and hover reaches +N, whether to display the remaining content through Popover | boolean | false | 2.22.0 |
 | showSearchClear | Toggle whether to support clear search box | boolean | true | 0.35.0 |
 | showSearchClear | Toggle whether to support clear search box | boolean | true | 0.35.0 |
 | size                     | Size for input box,one of `large`,`small`,`default`                              | string                                                            | `default`   | -       |
 | size                     | Size for input box,one of `large`,`small`,`default`                              | string                                                            | `default`   | -       |
 | style                    | Inline style                                                                        | CSSProperties                                                            | -           | -       |
 | style                    | Inline style                                                                        | CSSProperties                                                            | -           | -       |

+ 101 - 5
content/input/treeselect/index.md

@@ -66,7 +66,8 @@ import { TreeSelect } from '@douyinfe/semi-ui';
 
 
 ### 多选
 ### 多选
 
 
-设置 `multiple`,可以进行多选。多选情况下所有子项都被选择时,自动勾选显示其父项。通过设置 `maxTagCount` 属性,可以设置显示的标签数量上限。通过设置 `leafOnly` (>= v0.32.0) 属性,可以设置只展示叶子节点,同时 onChange 的回调入参也会只有叶子节点的值。
+设置 `multiple`,可以进行多选。多选情况下所有子项都被选择时,自动勾选显示其父项。  
+通过 `leafOnly` (>= v0.32.0) 属性,可以设置只展示叶子节点,同时 onChange 的回调入参也会只有叶子节点的值。  
 
 
 ```jsx live=true
 ```jsx live=true
 import React from 'react';
 import React from 'react';
@@ -148,18 +149,111 @@ import { TreeSelect } from '@douyinfe/semi-ui';
                 dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
                 dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
                 treeData={treeData}
                 treeData={treeData}
                 multiple
                 multiple
-                maxTagCount={2}
-                placeholder="当选中标签超过两个将折叠"
+                leafOnly
+                placeholder="只渲染叶子节点"
             />
             />
             <br/>
             <br/>
             <br/>
             <br/>
+        </div>
+    );
+};
+```
+
+### 限制标签展示数量
+
+在多选的场景中,利用 `maxTagCount` 可以限制展示的标签数量,超出部分将以 +N 的方式展示。  
+使用 `showRestTagsPopover` (>= v2.22.0) 可以设置在超出 `maxTagCount` 后,hover +N 是否显示 Popover,默认为 `false`。并且,还可以在 `restTagsPopoverProps` 属性中配置 Popover。
+
+```jsx live=true
+import React from 'react';
+import { TreeSelect } from '@douyinfe/semi-ui';
+() => {
+    const treeData = [
+        {
+            label: 'Asia',
+            value: 'Asia',
+            key: '0',
+            children: [
+                {
+                    label: 'China',
+                    value: 'China',
+                    key: '0-0',
+                    children: [
+                        {
+                            label: 'Beijing',
+                            value: 'Beijing',
+                            key: '0-0-0',
+                        },
+                        {
+                            label: 'Shanghai',
+                            value: 'Shanghai',
+                            key: '0-0-1',
+                        },
+                        {
+                            label: 'Chengdu',
+                            value: 'Chengdu',
+                            key: '0-0-2',
+                        },
+                    ],
+                },
+                {
+                    label: 'Japan',
+                    value: 'Japan',
+                    key: '0-1',
+                    children: [
+                        {
+                            label: 'Osaka',
+                            value: 'Osaka',
+                            key: '0-1-0'
+                        }
+                    ]
+                },
+            ],
+        },
+        {
+            label: 'North America',
+            value: 'North America',
+            key: '1',
+            children: [
+                {
+                    label: 'United States',
+                    value: 'United States',
+                    key: '1-0'
+                },
+                {
+                    label: 'Canada',
+                    value: 'Canada',
+                    key: '1-1'
+                }
+            ]
+        }
+    ];
+
+    const textStyle = { margin: '20px 0 10px' };
+
+    return ( 
+        <div>
+            <h4 style={textStyle}>maxTagCount=2:</h4>
             <TreeSelect
             <TreeSelect
+                multiple
+                maxTagCount={2}
                 style={{ width: 300 }}
                 style={{ width: 300 }}
                 dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
                 dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
                 treeData={treeData}
                 treeData={treeData}
+                placeholder="当选中标签超过两个将折叠"
+                defaultValue={['Beijing', 'Chengdu', 'Canada']}
+            />
+            <h4 style={textStyle}>maxTagCount=2, showRestTagsPopover:</h4>
+            <TreeSelect
+                showRestTagsPopover={true}
+                restTagsPopoverProps={{ position: 'top' }}
                 multiple
                 multiple
-                leafOnly
-                placeholder="只渲染叶子节点"
+                maxTagCount={2}
+                style={{ width: 300 }}
+                dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
+                treeData={treeData}
+                placeholder="hover +N 查看"
+                defaultValue={['Beijing', 'Chengdu', 'Canada']}
             />
             />
         </div>
         </div>
     );
     );
@@ -1306,11 +1400,13 @@ function Demo() {
 | renderFullLabel | 完全自定义label的渲染函数,[入参及用法详见](/zh-CN/navigation/tree#高级定制) | (obj) => ReactNode | - | 1.7.0 | 
 | renderFullLabel | 完全自定义label的渲染函数,[入参及用法详见](/zh-CN/navigation/tree#高级定制) | (obj) => ReactNode | - | 1.7.0 | 
 | renderLabel | 自定义label的渲染函数,[入参及用法详见](/zh-CN/navigation/tree#自定义节点内容)  | (label:ReactNode, data:TreeNode) => ReactNode | - | 1.6.0 | 
 | renderLabel | 自定义label的渲染函数,[入参及用法详见](/zh-CN/navigation/tree#自定义节点内容)  | (label:ReactNode, data:TreeNode) => ReactNode | - | 1.6.0 | 
 | renderSelectedItem | 自定义渲染已选项 | Function | - | 1.26.0 | 
 | renderSelectedItem | 自定义渲染已选项 | Function | - | 1.26.0 | 
+| restTagsPopoverProps | Popover 的配置属性,可以控制 position、zIndex、trigger 等,具体参考[Popover](/zh-CN/show/popover#API%20%E5%8F%82%E8%80%83) | PopoverProps     | {} | 2.22.0 |
 | searchAutoFocus | 搜索框自动聚焦 | boolean | false | 1.27.0 |
 | searchAutoFocus | 搜索框自动聚焦 | boolean | false | 1.27.0 |
 | searchPlaceholder | 搜索框默认文字 | string | - | - |
 | searchPlaceholder | 搜索框默认文字 | string | - | - |
 | searchPosition | 设置搜索框的位置,可选: `dropdown`、`trigger` | string | `dropdown` | 1.29.0 |
 | searchPosition | 设置搜索框的位置,可选: `dropdown`、`trigger` | string | `dropdown` | 1.29.0 |
 | showClear | 当值不为空时,trigger 是否展示清除按钮  | boolean | false |  |
 | showClear | 当值不为空时,trigger 是否展示清除按钮  | boolean | false |  |
 | showFilteredOnly | 搜索状态下是否只展示过滤后的结果 | boolean | false | 0.32.0 |
 | showFilteredOnly | 搜索状态下是否只展示过滤后的结果 | boolean | false | 0.32.0 |
+| showRestTagsPopover | 当超过 maxTagCount,hover 到 +N 时,是否通过 Popover 显示剩余内容 | boolean | false | 2.22.0 |
 | showSearchClear | 是否显示搜索框的清除按钮 | boolean | true | 0.35.0 |
 | showSearchClear | 是否显示搜索框的清除按钮 | boolean | true | 0.35.0 |
 | size | 选择框大小,可选 `large`,`small`,`default` | string | `default` | - |
 | size | 选择框大小,可选 `large`,`small`,`default` | string | `default` | - |
 | style | 选择框的样式  | CSSProperties | - | - |
 | style | 选择框的样式  | CSSProperties | - | - |

+ 2 - 0
packages/semi-foundation/treeSelect/foundation.ts

@@ -125,6 +125,8 @@ export interface BasicTreeSelectProps extends Pick<BasicTreeProps,
     searchPosition?: string;
     searchPosition?: string;
     stopPropagation?: boolean | string;
     stopPropagation?: boolean | string;
     loadedKeys?: string[];
     loadedKeys?: string[];
+    showRestTagsPopover?: boolean;
+    restTagsPopoverProps?: any;
     loadData?: (data: BasicTreeNodeData) => Promise<void>;
     loadData?: (data: BasicTreeNodeData) => Promise<void>;
     onSelect?: (selectedKeys: string, selected: boolean, selectedNode: BasicTreeNodeData) => void;
     onSelect?: (selectedKeys: string, selected: boolean, selectedNode: BasicTreeNodeData) => void;
     searchRender?: (inputProps: any) => any;
     searchRender?: (inputProps: any) => any;

+ 1 - 0
packages/semi-ui/cascader/index.tsx

@@ -556,6 +556,7 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
                 // TODO Modify logic, not modify type
                 // TODO Modify logic, not modify type
                 onRemove={v => this.handleTagRemove(null, (v as unknown) as (string | number)[])}
                 onRemove={v => this.handleTagRemove(null, (v as unknown) as (string | number)[])}
                 placeholder={placeholder}
                 placeholder={placeholder}
+                expandRestTagsOnClick={false}
             />
             />
         );
         );
     }
     }

+ 17 - 2
packages/semi-ui/treeSelect/index.tsx

@@ -36,7 +36,6 @@ import ConfigContext, { ContextValue } from '../configProvider/context';
 import TagGroup from '../tag/group';
 import TagGroup from '../tag/group';
 import Tag, { TagProps } from '../tag/index';
 import Tag, { TagProps } from '../tag/index';
 import Input, { InputProps } from '../input/index';
 import Input, { InputProps } from '../input/index';
-import Popover from '../popover/index';
 import AutoSizer from '../tree/autoSizer';
 import AutoSizer from '../tree/autoSizer';
 import TreeContext from '../tree/treeContext';
 import TreeContext from '../tree/treeContext';
 import TreeNode from '../tree/treeNode';
 import TreeNode from '../tree/treeNode';
@@ -51,6 +50,7 @@ import { OptionProps, TreeProps, TreeState, FlattenNode, TreeNodeData, TreeNodeP
 import { Motion } from '../_base/base';
 import { Motion } from '../_base/base';
 import { IconChevronDown, IconClear, IconSearch } from '@douyinfe/semi-icons';
 import { IconChevronDown, IconClear, IconSearch } from '@douyinfe/semi-icons';
 import CheckboxGroup from '../checkbox/checkboxGroup';
 import CheckboxGroup from '../checkbox/checkboxGroup';
+import Popover, { PopoverProps } from '../popover/index';
 
 
 export type ExpandAction = false | 'click' | 'doubleClick';
 export type ExpandAction = false | 'click' | 'doubleClick';
 
 
@@ -135,6 +135,7 @@ export interface TreeSelectProps extends Omit<BasicTreeSelectProps, OverrideComm
     zIndex?: number;
     zIndex?: number;
     searchPosition?: string;
     searchPosition?: string;
     stopPropagation?: boolean | string;
     stopPropagation?: boolean | string;
+    restTagsPopoverProps?: PopoverProps;
     searchRender?: boolean | ((inputProps: InputProps) => React.ReactNode);
     searchRender?: boolean | ((inputProps: InputProps) => React.ReactNode);
     onSelect?: (selectedKeys: string, selected: boolean, selectedNode: TreeNodeData) => void;
     onSelect?: (selectedKeys: string, selected: boolean, selectedNode: TreeNodeData) => void;
     renderSelectedItem?: RenderSelectedItem;
     renderSelectedItem?: RenderSelectedItem;
@@ -254,6 +255,8 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
         renderSelectedItem: PropTypes.func,
         renderSelectedItem: PropTypes.func,
         checkRelation: PropTypes.string,
         checkRelation: PropTypes.string,
         'aria-label': PropTypes.string,
         'aria-label': PropTypes.string,
+        showRestTagsPopover: PropTypes.bool,
+        restTagsPopoverProps: PropTypes.object,
     };
     };
 
 
     static defaultProps: Partial<TreeSelectProps> = {
     static defaultProps: Partial<TreeSelectProps> = {
@@ -282,7 +285,9 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
         clickToHide: true,
         clickToHide: true,
         searchAutoFocus: false,
         searchAutoFocus: false,
         checkRelation: 'related',
         checkRelation: 'related',
-        'aria-label': 'TreeSelect'
+        'aria-label': 'TreeSelect',
+        showRestTagsPopover: false,
+        restTagsPopoverProps: {},
     };
     };
     inputRef: React.RefObject<typeof Input>;
     inputRef: React.RefObject<typeof Input>;
     tagInputRef: React.RefObject<TagInput>;
     tagInputRef: React.RefObject<TagInput>;
@@ -817,6 +822,8 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
             maxTagCount,
             maxTagCount,
             searchPosition,
             searchPosition,
             filterTreeNode,
             filterTreeNode,
+            showRestTagsPopover, 
+            restTagsPopoverProps 
         } = this.props;
         } = this.props;
         const isTriggerPositionSearch = filterTreeNode && searchPosition === strings.SEARCH_POSITION_TRIGGER;
         const isTriggerPositionSearch = filterTreeNode && searchPosition === strings.SEARCH_POSITION_TRIGGER;
         // searchPosition = trigger
         // searchPosition = trigger
@@ -840,6 +847,8 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
                 tagList={tagList}
                 tagList={tagList}
                 size="large"
                 size="large"
                 mode="custom"
                 mode="custom"
+                showPopover={showRestTagsPopover}
+                popoverProps={restTagsPopoverProps}
             />
             />
         );
         );
     };
     };
@@ -1085,6 +1094,8 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
             placeholder,
             placeholder,
             maxTagCount,
             maxTagCount,
             checkRelation,
             checkRelation,
+            showRestTagsPopover, 
+            restTagsPopoverProps
         } = this.props;
         } = this.props;
         const {
         const {
             keyEntities,
             keyEntities,
@@ -1108,9 +1119,13 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
                 value={keyList}
                 value={keyList}
                 inputValue={inputValue}
                 inputValue={inputValue}
                 size={size}
                 size={size}
+                showRestTagsPopover={showRestTagsPopover}
+                restTagsPopoverProps={restTagsPopoverProps}
+                // eslint-disable-next-line jsx-a11y/no-autofocus
                 autoFocus={searchAutoFocus}
                 autoFocus={searchAutoFocus}
                 renderTagItem={(itemKey, index) => this.renderTagItem(itemKey, index)}
                 renderTagItem={(itemKey, index) => this.renderTagItem(itemKey, index)}
                 onRemove={itemKey => this.removeTag(itemKey)}
                 onRemove={itemKey => this.removeTag(itemKey)}
+                expandRestTagsOnClick={false}
             />
             />
         );
         );
     };
     };