Search code examples
javascriptreactjsantd

Having a tree structure on both sides of the Transfer component


I am using antd's Transfer component. Using the examples given in the documentation, I am able to create a tree transfer box that looks similar to: enter image description here

Is there a way I could have a tree structure also on the right side? Currently, as I select 0-1-0 under 0-1, it appears flat on the right side.

The code as also given in the Sandbox example, is given below:

import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Transfer, Tree } from 'antd';

const { TreeNode } = Tree;

// Customize Table Transfer
const isChecked = (selectedKeys, eventKey) => {
    return selectedKeys.indexOf(eventKey) !== -1;
};

const generateTree = (treeNodes = [], checkedKeys = []) => {
    return treeNodes.map(({ children, ...props }) => (
        <TreeNode {...props} disabled={checkedKeys.includes(props.key)}>
        {generateTree(children, checkedKeys)}
        </TreeNode>
    ));
};

const TreeTransfer = ({ dataSource, targetKeys, ...restProps }) => {
    const transferDataSource = [];
    function flatten(list = []) {
        list.forEach(item => {
        transferDataSource.push(item);
        flatten(item.children);
        });
    }
    flatten(dataSource);

    return (
        <Transfer
        {...restProps}
        targetKeys={targetKeys}
        dataSource={transferDataSource}
        className="tree-transfer"
        render={item => item.title}
        showSelectAll={false}
        >
        {({ direction, onItemSelect, selectedKeys }) => {
            if (direction === 'left') {
            const checkedKeys = [...selectedKeys, ...targetKeys];
            return (
                <Tree
                blockNode
                checkable
                checkStrictly
                defaultExpandAll
                checkedKeys={checkedKeys}
                onCheck={(
                    _,
                    {
                    node: {
                        props: { eventKey },
                    },
                    },
                ) => {
                    onItemSelect(eventKey, !isChecked(checkedKeys, eventKey));
                }}
                onSelect={(
                    _,
                    {
                    node: {
                        props: { eventKey },
                    },
                    },
                ) => {
                    onItemSelect(eventKey, !isChecked(checkedKeys, eventKey));
                }}
                >
                {generateTree(dataSource, targetKeys)}
                </Tree>
            );
            }
        }}
        </Transfer>
    );
};

const treeData = [
    { key: '0-0', title: '0-0' },
    {
        key: '0-1',
        title: '0-1',
        children: [{ key: '0-1-0', title: '0-1-0' }, { key: '0-1-1', title: '0-1-1' }],
    },
    { key: '0-2', title: '0-3' },
];

class App extends React.Component {
    state = {
        targetKeys: [],
    };

    onChange = targetKeys => {
        console.log('Target Keys:', targetKeys);
        this.setState({ targetKeys });
    };

    render() {
        const { targetKeys } = this.state;
        return (
        <div>
            <TreeTransfer dataSource={treeData} targetKeys={targetKeys} onChange={this.onChange} />
        </div>
        );
    }
}

ReactDOM.render(<App />, document.getElementById('container'));

The way I want to transfer is depicted in the images below.

  • I want to be able to transfer the children
  • When a child is transferred the left table should have the remaining children under it parent and the right table should have the transferred children under it parent name.

enter image description here

enter image description here


Solution

  • Check out this example where:

    • TransferTree is an example of checking nodes and showing the tree path.
    • AntdTreeTransfer an example for Transfer component with tree path on transfer.

    enter image description here

    export const renderTreeNodes = data =>
      data.map(item =>
        item.children ? (
          <Tree.TreeNode title={item.title} key={item.key} dataRef={item}>
            {renderTreeNodes(item.children)}
          </Tree.TreeNode>
        ) : (
          <Tree.TreeNode {...item} dataRef={item} />
        )
      );
    
    export const filterTree = (keys, halfKeys, rootNode) =>
      rootNode
        ? rootNode
            .filter(node => keys.includes(node.key) || halfKeys.includes(node.key))
            .map(nodeRoot => ({
              ...nodeRoot,
              children: filterTree(keys, halfKeys, nodeRoot.children)
            }))
        : [];
    
    export default function TreeTransfer() {
      const [checkedNodes, setCheckedNodes] = useState([]);
    
      const onCheck = (selectedKeys, info) => {
        const filteredTree = filterTree(selectedKeys, info.halfCheckedKeys, data);
        setCheckedNodes(filteredTree);
      };
    
      return (
        <FlexBox>
          <Row type="flex" gutter={20}>
            <Col>
              <Card>
                <Tree checkable defaultExpandAll onCheck={onCheck}>
                  {renderTreeNodes(data)}
                </Tree>
              </Card>
            </Col>
            <Col>
              <Card>
                <Tree checkable defaultExpandAll>
                  {renderTreeNodes(checkedNodes)}
                </Tree>
              </Card>
            </Col>
          </Row>
        </FlexBox>
      );
    }
    
    export default function AntdTreeTransfer() {
      const [leftCheckedKeys, setLeftCheckedKeys] = useState([]);
      const [checkedNodes, setCheckedNodes] = useState([]);
      const [targetNodes, setTargetNodes] = useState([]);
    
      return (
        <FlexBox>
          <Transfer
            operations={['', 'Clear']}
            onChange={(_, direction) => {
              setLeftCheckedKeys([]);
    
              direction === 'right'
                ? setTargetNodes(checkedNodes)
                : setTargetNodes([]);
            }}
            style={{ width: '50vh' }}
          >
            {({ direction, onItemSelect, selectedKeys }) =>
              direction === 'left' ? (
                <Tree
                  showLine
                  blockNode
                  checkable
                  defaultExpandAll
                  checkedKeys={leftCheckedKeys}
                  onCheck={(selectedKeys, info) => {
                    setLeftCheckedKeys(selectedKeys);
                    const filteredTree = filterTree(
                      selectedKeys,
                      info.halfCheckedKeys,
                      data
                    );
                    setCheckedNodes(filteredTree);
    
                    const eventKey = info.node.props.eventKey;
                    onItemSelect(eventKey, selectedKeys.includes(eventKey));
                  }}
                >
                  {renderTreeNodes(data)}
                </Tree>
              ) : (
                <Tree
                  autoExpandParent
                  blockNode
                  checkable
                  onCheck={(selectedKeys, info) => {
                    const eventKey = info.node.props.eventKey;
                    onItemSelect(eventKey, selectedKeys.includes(eventKey));
                  }}
                >
                  {renderTreeNodes(targetNodes)}
                </Tree>
              )
            }
          </Transfer>
        </FlexBox>
      );
    }
    

    Edit Q-56680158-TreeTransfer