@progress/kendo-angular-treeview
Version:
Kendo UI TreeView for Angular
89 lines (88 loc) • 5.57 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
import { getter, setter } from '@progress/kendo-common';
import { DropPosition } from '../models';
import { collapseEmptyParent, expandDropTarget, updateMovedItemIndex } from '../drag-and-drop-utils';
import { decrementPageSize, incrementPageSize, copyPageSize } from '../../load-more/load-more-utils';
import { isPresent, getDataItem, buildTreeIndex } from '../../utils';
/**
* @hidden
*/
export class HierarchyEditingService {
hierarchyBinding;
movedItemNewIndex;
constructor(hierarchyBinding) {
this.hierarchyBinding = hierarchyBinding;
}
add({ sourceItem, destinationItem, dropPosition, sourceTree, destinationTree }) {
// shallow clone the item as not to mistake it for its 'older' version when the remove handler kicks in to splice the item at its old position
const clonedSourceDataItem = Object.assign({}, getDataItem(sourceItem));
if (dropPosition === DropPosition.Over) {
// expand the item that was dropped into
expandDropTarget(destinationItem, destinationTree);
const destinationChildren = this.childrenFor(getDataItem(destinationItem));
// add the moved node just before the load more button if load more is enabled
const targetIndex = isPresent(destinationTree.loadMoreService) ?
Math.min(destinationTree.loadMoreService.getGroupSize(getDataItem(destinationItem)), destinationChildren.length) : // the page size might be greater than the actual children array length
destinationChildren.length;
destinationChildren.splice(targetIndex, 0, clonedSourceDataItem);
setter(this.hierarchyBinding.childrenField)(getDataItem(destinationItem), destinationChildren);
this.movedItemNewIndex = buildTreeIndex(destinationItem.item.index, targetIndex);
}
else {
const destinationParentNodes = this.getParentNodes(destinationItem, destinationTree);
const shiftIndex = dropPosition === DropPosition.After ? 1 : 0;
const targetIndex = destinationParentNodes.indexOf(getDataItem(destinationItem)) + shiftIndex;
destinationParentNodes.splice(targetIndex, 0, clonedSourceDataItem);
const parentIndex = destinationItem.parent ? destinationItem.parent.item.index : null;
this.movedItemNewIndex = buildTreeIndex(parentIndex, targetIndex);
}
// increment the parent page size => an item is moved into it
const updatedParent = dropPosition === DropPosition.Over ? getDataItem(destinationItem) : getDataItem(destinationItem.parent);
incrementPageSize(destinationTree, updatedParent);
// the page sizes are stored by data-item reference => copy the old item ref page size to the new item reference
copyPageSize(destinationTree, getDataItem(sourceItem), clonedSourceDataItem);
// the source tree nodes are reloaded on `removeItem` - reload the destination tree nodes if the soruce and the destination tree are different
if (sourceTree !== destinationTree && !destinationTree.loadOnDemand) {
destinationTree.preloadChildNodes();
}
// if the source and destination trees are the same, focusing the moved item here will not have the desired effect
// as the `remove` handler has not yet kicked-in to remove the item from its old position
if (sourceTree !== destinationTree) {
// ensure the focus target is rendered and registered
destinationTree.changeDetectorRef.detectChanges();
destinationTree.focus(this.movedItemNewIndex);
}
}
remove({ sourceItem, sourceTree, destinationTree }) {
const sourceParentNodes = this.getParentNodes(sourceItem, sourceTree);
const sourceItemIndex = sourceParentNodes.indexOf(getDataItem(sourceItem));
sourceParentNodes.splice(sourceItemIndex, 1);
// emit collapse for the parent node if its last child node was spliced
collapseEmptyParent(sourceItem.parent, sourceParentNodes, sourceTree);
// decrement source item parent page size => an item has been removed from it
decrementPageSize(sourceTree, getDataItem(sourceItem.parent));
// reload the treeview nodes
if (!sourceTree.loadOnDemand) {
sourceTree.preloadChildNodes();
}
// if the source and destination trees are different we want to focus only the moved item in the destination tree
if (sourceTree === destinationTree) {
// ensure the focus target is rendered and registered
destinationTree.changeDetectorRef.detectChanges();
// after the source item is removed from its original position, the candidate index might have to be corrected
const index = updateMovedItemIndex(this.movedItemNewIndex, sourceItem.item.index);
destinationTree.focus(index);
}
}
getParentNodes(node, treeView) {
return isPresent(node.parent) ?
this.childrenFor(getDataItem(node.parent)) :
treeView.nodes;
}
childrenFor(dataItem) {
return getter(this.hierarchyBinding.childrenField)(dataItem) || [];
}
}