background

This time in a tree display function, who knows the product is not enough, talk to me:

PD: what? Only unrolled? This how line, we most basic to support editing, support search, if possible can also do a reverse positioning… 😎 😎 😎

YY: 😭😭😭? It’s not on the requirements document either…

PD: Have you ever seen a document get it right all at once? Which PD does not add demand?

YY: That’s how it was said, but that’s not how it was done…

PD: Oh, stop wasting your time and do it!

YY: 🤬 🤬 🤬…

The above story is pure fiction, if any similar, please leave a message in the comment section…

Tree data is fairly common in development. Folders, organizational structures, biological categories, countries, and so on, most of the structure of everything in the world is a tree structure. The use of tree control can be a complete display of the hierarchy, and with the expansion of the selection and other interactive functions.

Demand analysis

  • Edit: Add/modify/delete/move
  • Search function: Name/creator/owner filter
  • Location: TAB reverse location

Project warehouse: github.com/speakice/ed…

Function implementation

There are a number of libraries and components that can do this. Here is just one of them, all of which are Ant Design components:

  • Tree. DirectoryTree directory trees
  • Dropdown right-click menu container
  • Menu Menu contents
  • Tabs Indicates the right Tab page
  • Input the Search Search box
  • Switch Switch the association status
  • Shortid generates unique ids
import { Tree, Dropdown, Menu, Tabs, Input, Switch } from 'antd';
import shortid from 'shortid';
Copy the code

Recursive method

The most important prerequisite for manipulating tree row data is to have a handy recursive method:

/** * Action returns the modified item if it needs to be modified, and does not return */ if it does not
export const deepTree = (tree = [], action = () => {}) = > {
  return tree.map((item) = > {
    constnewItem = action({ ... item }) || item;if (newItem.children) {
      newItem.children = deepTree(newItem.children, action);
    }
    return newItem;
  });
};
Copy the code

Right-click menu

Right-click on title and write Dropdown to the data source of the tree component:

    <DirectoryTree
          style={{ width: 280 }}
          draggable
          onDrop={onDrop}
          defaultExpandAll
          onRightClick={({ node }) = > setRightClickKey(node.key)}
          onSelect={onSelect}
          selectedKeys={rightConnect ? [activeTabKey] : selectedKeys}
          onExpand={onExpand}
          treeData={[
            ...deepTree(treeData, (item) = > {
              return {
                ...item,
                titleWord: item.title,
                title: (
                  <Dropdown
                    trigger="contextMenu"
                    visible={rightClickKey= = =item.key}
                    onVisibleChange={()= > setRightClickKey()}
                    overlayStyle={{ width: 80 }}
                    overlay={menu(item)}
                  >
                    <div
                      style={
                        searchWord && item.title.includes(searchWord)? {color: 'red'}:{}} >
                      {item.title}
                    </div>
                  </Dropdown>),}; })]} / >Copy the code

A few things to add about the right-click menu:

  • The Dropdown trigger property needs to be contextMenu.
  • The Dropdown display position is relative to the title, you need to set the width of the outer container to fill the remaining space:
.ant-tree-node-content-wrapper {
  display: flex;
}
.ant-tree-title {
  flex: 1;
}
Copy the code
  • The Dropdown display is determined by right-clicking the record key;
  • The Dropdown menu needs to pass the current item;
  const menu = (node) = > (
    <Menu
      onClick={({ key.domEvent}) = >{ domEvent.stopPropagation(); console.log('menuClick', node, key); Switch (key) {case 'add': switch (key) {case 'add': setTreeData( deepTree(treeData, (item) => { if (item.children && item.key === node.key) { return { ... item, children: [ ...item.children, { title: 'new add', key: shortid.generate(), isLeaf: true, }, ], }; }})); break; case 'delete': const outer = treeData.find((item) => item.key === node.key); if (outer) { setTreeData(treeData.filter((item) => item.key ! == node.key)); return; } setTreeData( deepTree(treeData, (item) => { if (item.children) { return { ... item, children: item.children.filter( ({ key }) => key ! == node.key ), }; } return item; })); break; case 'edit': setTreeData( deepTree(treeData, (item) => { if (item.key === node.key) { console.log('editle', { ... item, title: 'new edit', }); return { ... item, title: 'new edit', }; } return item; })); break; }}} ><Menu.Item key="add">new</Menu.Item>
      <Menu.Item key="delete" danger>delete</Menu.Item>
      <Menu.Item key="edit">The editor</Menu.Item>
    </Menu>
  );
Copy the code

Add/Modify/delete functions

By default, the function can only be added to the folder, judging by the key value, here is a relatively simple process, only do the core function demonstration, see the code in the previous section;In formal projects, popup editing is generally required or input boxes are embedded in the title of the tree component. Variables can be used to record the item being edited, and finally save it and insert it into the tree data recursively:

The delete function makes a judgment. If the outermost layer is deleted, the filter is directly used.⚠ ️Otherwise,Deletion is filtered through childrenWe have to pay special attention here.

The search function

The search function is prompted by the titile color turning red:

In terms of implementation, it only does the search after the click, without real-time search prompt or search term differentiation. Here, it can be achieved by intercepting the string, as you can seeThe official instance.Note that this default turns on the parent’s property, autoExpandParentOtherwise it might take some work to recurse up.

Another requirement is to filter the data source, which can be implemented by simply modifying the official instance;

Tab reverse positioning

Click the Tree component item and add Tab to the right or activate Tab. This can be regarded as forward positioning. That is, when the right Tab page is switched, the left Tree component selects the corresponding item. The core code is to specify selectedKeys. Comparatively speaking, it is not difficult to open the relevant parent node by defaultautoExpandParentThis property, that’s it.

Drag and drop to move

Drag and drop is supported by the Tree component itself, and the official drag-and-drop instance has been provided. I only modified the official instance slightly, and I will not go into details here:

The end of the

The difficulty of searching and reverse locating is actually opening the associated folder, but the official example uses the autoExpandParent property to make it a lot easier.

It’s getting late. That’s all for today.