New to KendoReact? Start a free 30-day trial
Dragging and Dropping
The drag-and-drop functionality enables the user to move the TreeView items by dragging and dropping them within a single tree or across multiple trees.
Basics
To implement dragging and dropping in the TreeView:
- Set the
draggable
property totrue
. - Handle the dispatched
drag
events.
To facilitate the event handling, the TreeView provides the following utilities:
- The
moveTreeViewItem
function—A helper function which moves a TreeView item in an immutable way. - The
TreeViewDragAnalyzer
class—An API for analyzing thedrag
events of the TreeView. - The
TreeViewDragClue
component—A component which renders a clue when an item is dragged.
Moving Items within a Single Tree
The following example demonstrates how to:
- Implement the drag-and-drop functionality within a single TreeView by using all drag-and-drop utilities.
- Update the
expanded
andselected
item fields by using theprocessTreeViewItems
function.
Change Theme
Theme
Loading ...
Moving Items across Multiple Trees
The following example demonstrates how to:
- Implement the drag-and-drop functionality across two TreeViews by using all drag-and-drop utilities.
- Directly update the
expanded
andselected
item fields.
jsx
class App extends React.Component {
treeView1Guid;
treeView2Guid;
dragClue;
dragOverCnt = 0;
isDragDrop = false;
state = { tree, tree2 };
render() {
return (
<div>
<TreeView
data={this.state.tree}
draggable={true}
onItemDragOver={this.onItemDragOver}
onItemDragEnd={this.onItemDragEnd}
ref={(treeView) => (this.treeView1Guid = treeView && treeView.guid)}
expandIcons={true}
onExpandChange={this.onExpandChange}
onItemClick={this.onItemClick}
/>
<TreeView
data={this.state.tree2}
draggable={true}
onItemDragOver={this.onItemDragOver}
onItemDragEnd={this.onItemDragEnd}
ref={(treeView) => (this.treeView2Guid = treeView && treeView.guid)}
expandIcons={true}
onExpandChange={this.onExpandChange}
onItemClick={this.onItemClick}
/>
<TreeViewDragClue ref={(dragClue) => (this.dragClue = dragClue)} />
</div>
);
}
onItemDragOver = (event) => {
this.dragOverCnt++;
this.dragClue.show(event.pageY + 10, event.pageX, event.item.text, this.getClueClassName(event));
};
onItemDragEnd = (event) => {
this.isDragDrop = this.dragOverCnt > 0;
this.dragOverCnt = 0;
this.dragClue.hide();
const eventAnalyzer = new TreeViewDragAnalyzer(event).init();
if (eventAnalyzer.isDropAllowed) {
const { sourceData, targetData } = moveTreeViewItem(
event.itemHierarchicalIndex,
this.resolveData(event.target.guid),
eventAnalyzer.getDropOperation(),
eventAnalyzer.destinationMeta.itemHierarchicalIndex,
this.resolveData(eventAnalyzer.destinationMeta.treeViewGuid)
);
this.setState({
[this.resolveDataKey(event.target.guid)]: sourceData,
[this.resolveDataKey(eventAnalyzer.destinationMeta.treeViewGuid)]: targetData
});
}
};
onItemClick = (event) => {
if (!this.isDragDrop) {
event.item.selected = !event.item.selected;
this.forceUpdate();
}
};
onExpandChange = (event) => {
event.item.expanded = !event.item.expanded;
this.forceUpdate();
};
getClueClassName(event) {
const eventAnalyzer = new TreeViewDragAnalyzer(event).init();
const { itemHierarchicalIndex: itemIndex, treeViewGuid } = eventAnalyzer.destinationMeta;
if (eventAnalyzer.isDropAllowed) {
switch (eventAnalyzer.getDropOperation()) {
case 'child':
return 'k-i-plus';
case 'before':
return itemIndex === '0' || itemIndex.endsWith(`${SEPARATOR}0`)
? 'k-i-insert-up'
: 'k-i-insert-middle';
case 'after':
const siblings = getSiblings(itemIndex, this.resolveData(treeViewGuid));
const lastIndex = Number(itemIndex.split(SEPARATOR).pop());
return lastIndex < siblings.length - 1 ? 'k-i-insert-middle' : 'k-i-insert-down';
default:
break;
}
}
return 'k-i-cancel';
}
resolveData(treeViewGuid) {
return treeViewGuid === this.treeView1Guid ? this.state.tree : this.state.tree2;
}
resolveDataKey(treeViewGuid) {
return treeViewGuid === this.treeView1Guid ? 'tree' : 'tree2';
}
}
function getSiblings(itemIndex, data) {
let result = data;
const indices = itemIndex.split(SEPARATOR).map((index) => Number(index));
for (let i = 0; i < indices.length - 1; i++) {
result = result[indices[i]].items;
}
return result;
}
const SEPARATOR = '_';
const tree = [
{
text: 'Furniture',
expanded: true,
items: [{ text: 'Tables & Chairs' }, { text: 'Sofas' }, { text: 'Occasional Furniture' }]
},
{
text: 'Decor',
expanded: true,
items: [{ text: 'Bed Linen' }, { text: 'Curtains & Blinds' }, { text: 'Carpets' }]
}
];
const tree2 = [
{
text: 'Storage',
expanded: true,
items: [{ text: 'Wall Shelving' }, { text: 'Floor Shelving' }, { text: 'Kids Storage' }]
},
{
text: 'Lights',
expanded: true,
items: [{ text: 'Ceiling' }, { text: 'Table' }, { text: 'Floor' }]
}
];
ReactDOM.render(<App />, document.querySelector('my-app'));