UNPKG

@progress/kendo-angular-sortable

Version:

A Sortable Component for Angular

178 lines (177 loc) 7.9 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import { Directive, Input } from '@angular/core'; import { SortableComponent } from './sortable.component'; import { SortableService } from './sortable.service'; import { DataAddEvent, DataMoveEvent, DataRemoveEvent } from './data-events'; import { filter, take } from 'rxjs/operators'; import * as i0 from "@angular/core"; import * as i1 from "./sortable.component"; import * as i2 from "./sortable.service"; /** * A Directive which handles the most common scenarios such reordering and moving items between Sortables, thus minimizng boilerplate code. * This is achieved by subscribing to the Sortable's events and handling them using the API methods it provides. */ export class SortableBindingDirective { sortable; sortableService; removeHiddenSubscription; dragOverSubscription; navigateSubscription; lastTarget; /** * Sets a data-bound array that is used as a data source for the Sortable. * * <demo metaUrl="sortable/overview/" height="430"></demo> * */ set data(data) { this.sortable.data = data; } constructor(sortable, sortableService) { this.sortable = sortable; this.sortableService = sortableService; this.sortableService = sortableService; } nextEnabledIndex(index, sortable) { for (let i = index; i <= sortable.data.length; i++) { if (sortable.itemEnabled(i)) { return i; } } } addItem(event, sortable) { let index = event.index; const dataItem = this.sortableService.getSource().data[event.oldIndex]; const addEvent = new DataAddEvent({ index: index, dataItem: dataItem }); sortable.dataAdd.emit(addEvent); if (!addEvent.isDefaultPrevented()) { if (index === sortable.itemWrappers.length - 1 && !sortable.noDataContainer) { index++; } sortable.addDataItem(dataItem, index); } return !addEvent.isDefaultPrevented(); } removeItem(event, sortable) { const originDraggable = this.sortableService.originDraggable; const removeEvent = new DataRemoveEvent({ index: event.oldIndex, dataItem: sortable.data[event.oldIndex] }); sortable.dataRemove.emit(removeEvent); if (!removeEvent.isDefaultPrevented()) { if (originDraggable && originDraggable.parent === sortable) { sortable.hideItem(event.oldIndex, true); } else { sortable.removeDataItem(event.oldIndex); } } return !removeEvent.isDefaultPrevented(); } moveItem(event, sortable) { if (event.index === event.oldIndex) { return false; } const moveEvent = new DataMoveEvent({ dataItem: sortable.data[event.oldIndex], index: event.index, oldIndex: event.oldIndex }); sortable.dataMove.emit(moveEvent); if (!moveEvent.isDefaultPrevented()) { sortable.moveItem(event.oldIndex, event.index); } return !moveEvent.isDefaultPrevented(); } /** * Removes the Draggable item from which the drag started. * @hidden */ removeOriginDraggable() { if (this.removeHiddenSubscription) { this.removeHiddenSubscription.unsubscribe(); } this.removeHiddenSubscription = this.sortableService.onReleaseSubject.pipe(take(1), filter(_ => this.sortableService.originDraggable !== null && this.sortableService.originDraggable.hidden)).subscribe((e) => { const originDraggable = this.sortableService.originDraggable; const newSource = this.sortableService.getSource(); if (originDraggable.parent !== this.sortableService.target) { const isTargetDraggable = e.target ? (e.target.isDraggable || e.target.isDraggableChild) : false; if (isTargetDraggable || originDraggable.parent !== newSource) { if (originDraggable.parent !== this.sortableService.target) { originDraggable.parent.removeDataItem(originDraggable.index); } } this.sortableService.originDraggable = null; } }); } onDragOver(event) { const source = this.sortableService.getSource(); const target = this.sortableService.target; const targetDraggables = target.draggables.toArray(); if (event.isDefaultPrevented()) { return; } event.preventDefault(); if (target === source) { // Skip moveItem if target is the draggable after which item was just added // Ensures item added to the end stays there until further user action if (targetDraggables[event.index] !== this.lastTarget) { this.moveItem(event, target); } this.lastTarget = undefined; } else { if (!target.itemEnabled(event.index)) { event.index = this.nextEnabledIndex(event.index, target); } //Add to target and remove from source if (this.addItem(event, target) && this.removeItem(event, source)) { this.lastTarget = target.draggables.toArray()[event.index]; this.removeOriginDraggable(); target.activeIndex = event.index; source.activeIndex = -1; } } } onNavigate(event) { if (event.ctrlKey) { const moveEvent = new DataMoveEvent({ dataItem: this.sortable.data[event.oldIndex], index: event.index, oldIndex: event.oldIndex }); this.sortable.dataMove.emit(moveEvent); if (!moveEvent.isDefaultPrevented()) { this.sortable.moveItem(event.oldIndex, event.index); } } else { this.sortable.activeIndex = event.index; } } ngOnInit() { this.dragOverSubscription = this.sortable.dragOver.subscribe(this.onDragOver.bind(this)); this.navigateSubscription = this.sortable.navigate.subscribe(this.onNavigate.bind(this)); } ngOnDestroy() { this.dragOverSubscription.unsubscribe(); this.navigateSubscription.unsubscribe(); if (this.removeHiddenSubscription) { this.removeHiddenSubscription.unsubscribe(); } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SortableBindingDirective, deps: [{ token: i1.SortableComponent }, { token: i2.SortableService }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: SortableBindingDirective, isStandalone: true, selector: "[kendoSortableBinding]", inputs: { data: ["kendoSortableBinding", "data"] }, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SortableBindingDirective, decorators: [{ type: Directive, args: [{ selector: '[kendoSortableBinding]', standalone: true }] }], ctorParameters: function () { return [{ type: i1.SortableComponent }, { type: i2.SortableService }]; }, propDecorators: { data: [{ type: Input, args: ["kendoSortableBinding"] }] } });