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.
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.
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')
);