UNPKG

@progress/kendo-angular-treeview

Version:
89 lines (88 loc) 5.57 kB
/**----------------------------------------------------------------------------------------- * 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) || []; } }