@progress/kendo-angular-sortable
Version:
A Sortable Component for Angular
178 lines (177 loc) • 7.9 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 { 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"]
}] } });