Having a tree structure on both sides of the Transfer component

Viewed 2261

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

1 Answers

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

Related