瀏覽代碼

feat: [Tree] Tree add filterExpandedKeys parameter for onSearch (#1669)

Co-authored-by: pointhalo <[email protected]>
YyumeiZhang 2 年之前
父節點
當前提交
7813bb39aa

+ 63 - 1
content/navigation/tree/index-en-US.md

@@ -991,6 +991,68 @@ import { Tree, Button } from '@douyinfe/semi-ui';
 
 ```
 
+### Controlled Expansion with Search
+When `expandedKeys` is passed in, it is the expanded controlled component, which can be used with `onExpand`. When the expansion is controlled, if you enable `filterTreeNode` and search, the node will not be automatically expanded. At this time, the expansion of the node is completely controlled by `expandedKeys`. You can use the input parameter `filteredExpandedKeys` (version: >= 2.38.0) of `onSearch` to realize the search expansion effect when the expansion is controlled.
+
+```jsx live=true hideInDSM
+import React, { useState } from 'react';
+import { Tree } from '@douyinfe/semi-ui';
+
+() => {
+    const [expandedKeys, setExpandedKeys] = useState([]);
+    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: 'Japan',
+                    value: 'Japan',
+                    key: '0-1',
+                },
+            ],
+        },
+        {
+            label: 'North America',
+            value: 'North America',
+            key: '1',
+        }
+    ];
+    return (
+        <Tree
+            style={{ width: 300 }}
+            treeData={treeData}
+            filterTreeNode
+            expandedKeys={expandedKeys}
+            onExpand={expandedKeys => {
+                setExpandedKeys(expandedKeys);
+            }}
+            onSearch={(inputValue, filteredExpandedKeys) => {
+                setExpandedKeys([...filteredExpandedKeys]);
+            }}
+        />
+    );
+};
+```
+
 ### Controlled Component
 
 You can use `value` along with `onChange` property if you want to use Tree as a controlled component.
@@ -2165,7 +2227,7 @@ import { IconFixedStroked, IconSectionStroked, IconAbsoluteStroked, IconInnerSec
 | onExpand            | Callback function when expand or collapse a node | (expandedKeys: string[], {expanded: boolean, node: TreeNodeData}) => void               | -       | - |
 | onLoad | Callback function when a node is loaded | (loadedKeys: Set< string >, treeNode: TreeNodeData) => void | - | 1.0.0|
 | onContextMenu | Callback function when right click on an item | (e: MouseEvent, node: TreeNodeData) => void | - | 0.35.0 |
-| onSearch            | Callback function when the values for search input changes                | (sunInput: string) => void  | -       | - |
+| onSearch                 | Callback function when search value changes. `filteredExpandedKeys` represents the key of the node expanded due to search or value/defaultValue, which can be used when expandedKeys is controlled<br/> **filteredExpandedKeys is supported in 2.38.0**      | function(input: string, filteredExpandedKeys: string[])  
 | onSelect            | Callback function when selected, return the key property of data          | (selectedKey:string, selected: bool, selectedNode: TreeNodeData) => void | -       | - |
 
 ### TreeNodeData

+ 63 - 1
content/navigation/tree/index.md

@@ -1039,6 +1039,68 @@ import { Tree, Button } from '@douyinfe/semi-ui';
 
 ```
 
+### 开启搜索的展开受控
+传入 `expandedKeys` 时即为展开受控组件,可以配合 `onExpand` 使用。当展开受控时,如果开启 `filterTreeNode` 并进行搜索是不会再自动展开节点的,此时,节点的展开完全由 `expandedKeys` 来控制。你可以利用 `onSearch` 的入参 `filteredExpandedKeys`(version: >= 2.38.0) 来实现展开受控时的搜索展开效果。
+
+```jsx live=true hideInDSM
+import React, { useState } from 'react';
+import { Tree } from '@douyinfe/semi-ui';
+
+() => {
+    const [expandedKeys, setExpandedKeys] = useState([]);
+    const treeData = [
+        {
+            label: '亚洲',
+            value: 'Asia',
+            key: '0',
+            children: [
+                {
+                    label: '中国',
+                    value: 'China',
+                    key: '0-0',
+                    children: [
+                        {
+                            label: '北京',
+                            value: 'Beijing',
+                            key: '0-0-0',
+                        },
+                        {
+                            label: '上海',
+                            value: 'Shanghai',
+                            key: '0-0-1',
+                        },
+                    ],
+                },
+                {
+                    label: '日本',
+                    value: 'Japan',
+                    key: '0-1',
+                },
+            ],
+        },
+        {
+            label: '北美洲',
+            value: 'North America',
+            key: '1',
+        }
+    ];
+    return (
+        <Tree
+            style={{ width: 300 }}
+            treeData={treeData}
+            filterTreeNode
+            expandedKeys={expandedKeys}
+            onExpand={expandedKeys => {
+                setExpandedKeys(expandedKeys);
+            }}
+            onSearch={(inputValue, filteredExpandedKeys) => {
+                setExpandedKeys([...filteredExpandedKeys]);
+            }}
+        />
+    );
+};
+```
+
 ### 受控
 
 传入 `value` 时即为受控组件,可以配合 `onChange` 使用。
@@ -2157,7 +2219,7 @@ import { IconFixedStroked, IconSectionStroked, IconAbsoluteStroked, IconInnerSec
 | onExpand | 展示节点时调用 | (expandedKeys: string[], {expanded: boolean, node: TreeNodeData}) => void | - | - |
 | onLoad | 节点加载完毕时触发的回调 | (loadedKeys: Set<string/>, treeNode: TreeNodeData) => void |- |  1.0.0|
 | onContextMenu | 右键点击的回调 | (e: MouseEvent, node: TreeNodeData) => void | - | 0.35.0 |
-| onSearch | 文本框值变化时回调 | (sunInput: string) => void | - | - |
+| onSearch | 文本框值变化时回调, 入参 filteredExpandedKeys 表示因为搜索或 value / defaultValue 而展开的节点的 key, <br/>可以配合 expandedKeys 受控时使用。filteredExpandedKeys 在 2.38.0 中新增 | (sunInput: string, filteredExpandedKeys: string[]) => void | - | - |
 | onSelect | 被选中时调用,返回值为当前事件选项的key值 | (selectedKey:string, selected: bool, selectedNode: TreeNodeData) => void | - | - |
 
 ### TreeNodeData

+ 5 - 4
packages/semi-foundation/tree/foundation.ts

@@ -217,7 +217,7 @@ export interface BasicTreeProps {
     onExpand?: (expandedKeys: string[], expandedOtherProps: BasicExpandedOtherProps) => void;
     onLoad?: (loadedKeys?: Set<string>, treeNode?: BasicTreeNodeData) => void;
     onContextMenu?: (e: any, node: BasicTreeNodeData) => void;
-    onSearch?: (sunInput: string) => void;
+    onSearch?: (sunInput: string, filteredExpandedKeys: string[]) => void;
     onSelect?: (selectedKeys: string, selected: boolean, selectedNode: BasicTreeNodeData) => void;
     preventScroll?: boolean;
     renderDraggingNode?: (nodeInstance: HTMLElement, node: BasicTreeNodeData) => HTMLElement;
@@ -303,7 +303,7 @@ export interface TreeAdapter extends DefaultAdapter<BasicTreeProps, BasicTreeInn
     notifyExpand: (expandedKeys: Set<string>, { expanded, node }: BasicExpandedOtherProps) => void;
     notifySelect: (selectKey: string, bool: boolean, node: BasicTreeNodeData) => void;
     notifyChange: (value: BasicValue) => void;
-    notifySearch: (input: string) => void;
+    notifySearch: (input: string, filteredExpandedKeys: string[]) => void;
     notifyRightClick: (e: any, node: BasicTreeNodeData) => void;
     notifyDoubleClick: (e: any, node: BasicTreeNodeData) => void;
     cacheFlattenNodes: (bool: boolean) => void;
@@ -483,13 +483,14 @@ export default class TreeFoundation extends BaseFoundation<TreeAdapter, BasicTre
                 showFilteredOnly && filteredShownKeys
             );
         }
-        this._adapter.notifySearch(sugInput);
+        const newFilteredExpandedKeys = new Set(expandedOptsKeys);
+        this._adapter.notifySearch(sugInput, Array.from(newFilteredExpandedKeys));
         this._adapter.updateState({
             expandedKeys,
             flattenNodes,
             motionKeys: new Set([]),
             filteredKeys: new Set(filteredOptsKeys),
-            filteredExpandedKeys: new Set(expandedOptsKeys),
+            filteredExpandedKeys: newFilteredExpandedKeys,
             filteredShownKeys,
         });
     }

+ 73 - 1
packages/semi-ui/tree/_story/tree.stories.jsx

@@ -3,10 +3,11 @@ import { cloneDeep, difference, isEqual } from 'lodash';
 import { IconEdit, IconMapPin, IconMore } from '@douyinfe/semi-icons';
 import Tree from '../index';
 import AutoSizer from '../autoSizer';
-import { Button, ButtonGroup, Input, Popover, Toast, Space, Select, Switch } from '../../index';
+import { Button, ButtonGroup, Input, Popover, Toast, Space, Select, Switch, Typography} from '../../index';
 import BigTree from './BigData';
 import testData from './data';
 const TreeNode = Tree.TreeNode;
+const { Title } = Typography;
 
 export default {
   title: 'Tree'
@@ -2622,3 +2623,74 @@ export const virtualizeTree = () => <DemoV />;
 virtualizeTree.story = {
   name: 'virtualize tree',
 };
+
+export const SearchableAndExpandedKeys = () => {
+  const [expandedKeys1, setExpandedKeys1] = useState([]);
+  const [expandedKeys2, setExpandedKeys2] = useState([]);
+  const [expandedKeys3, setExpandedKeys3] = useState([]);
+  const [expandedKeys4, setExpandedKeys4] = useState([]);
+  return (
+      <>
+          <Title heading={6}>expandedKeys 受控</Title>
+          <Tree
+              style={{ width: 300, marginBottom: 30 }}
+              dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
+              treeData={treeData1}
+              expandedKeys={expandedKeys1}
+              defaultValue='beijing'
+              onExpand={v => {
+                  console.log('onExpand value: ', v);
+                  setExpandedKeys1(v);
+              }}
+          />
+          <Title heading={6}>expandedKeys 受控 + 开启搜索</Title>
+          <Tree
+              style={{ width: 300, marginBottom: 30 }}
+              dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
+              treeData={treeData1}
+              filterTreeNode
+              defaultValue='beijing'
+              expandedKeys={expandedKeys2}
+              onExpand={v => {
+                  console.log('onExpand value: ', v);
+                  setExpandedKeys2(v);
+              }}
+          />
+          <Title heading={6}>expandedKeys 受控 + 开启搜索 + 搜索时更新 expandedKeys</Title>
+          <Tree
+              style={{ width: 300, marginBottom: 30 }}
+              dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
+              treeData={treeData1}
+              filterTreeNode
+              expandedKeys={expandedKeys3}
+              defaultValue='beijing'
+              onExpand={v => {
+                  console.log('onExpand value: ', v);
+                  setExpandedKeys3(v)
+              }}
+              onSearch={(input, filterExpandedKeys) => {
+                  console.log('onExpand filterExpandedKeys: ', filterExpandedKeys);
+                  setExpandedKeys3(filterExpandedKeys);
+              }}
+          />
+          <Title heading={6}>expandedKeys 受控 + 开启搜索 + showFilteredOnly + 搜索时更新 expandedKeys</Title>
+          <Tree
+              style={{ width: 300, marginBottom: 30 }}
+              dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
+              treeData={treeData1}
+              filterTreeNode
+              showFilteredOnly
+              expandedKeys={expandedKeys4}
+              defaultValue='beijing'
+              onExpand={v => {
+                  console.log('onExpand value: ', v);
+                  setExpandedKeys4(v)
+              }}
+              onSearch={(input, filterExpandedKeys) => {
+                  console.log('onExpand filterExpandedKeys: ', filterExpandedKeys);
+                  setExpandedKeys4(filterExpandedKeys);
+              }}
+          />
+      </>
+  )
+}

+ 2 - 2
packages/semi-ui/tree/index.tsx

@@ -489,8 +489,8 @@ class Tree extends BaseComponent<TreeProps, TreeState> {
             notifyChange: value => {
                 this.props.onChange && this.props.onChange(value);
             },
-            notifySearch: input => {
-                this.props.onSearch && this.props.onSearch(input);
+            notifySearch: (input: string, filteredExpandedKeys: string[]) => {
+                this.props.onSearch && this.props.onSearch(input, filteredExpandedKeys);
             },
             notifyRightClick: (e, node) => {
                 this.props.onContextMenu && this.props.onContextMenu(e, node);