@progress/kendo-angular-utils
Version:
Kendo UI Angular utils component
235 lines (234 loc) • 9.39 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 { ChangeDetectorRef, Directive, ElementRef, EventEmitter, Input, NgZone, Output } from '@angular/core';
import { validatePackage } from '@progress/kendo-licensing';
import { packageMetadata } from '../package-metadata';
import { DragStateService } from './drag-state.service';
import { DropTargetEvent } from './events/drop-target-event';
import { isPresent, intersect } from './util';
import * as i0 from "@angular/core";
import * as i1 from "./drag-state.service";
/**
* Represents the [Kendo UI DropTargetContainer directive for Angular]({% slug api_utils_droptargetcontainerdirective %}).
* Used to configure multiple elements as drop targets.
*
* @example
* ```ts-no-run
* <div kendoDropTargetContainer dropTargetFilter=".my-droptarget">
* <div class="my-droptarget">foo</div>
* </div>
* ```
*/
export class DropTargetContainerDirective {
service;
element;
ngZone;
cdr;
/**
* Specifies a selector for elements within a container which will be configured as drop targets
* ([see example]({% slug drop_target_container %})). The possible values include any
* DOM `selector`.
*/
set dropTargetFilter(value) {
this._dropTargetFilter = value;
if (!this.dropDisabled) {
this.initializeDropTargets();
}
}
get dropTargetFilter() {
return this._dropTargetFilter;
}
/**
* Specifies whether the Drop Targets within the container will emit the corresponding events upon interaction with a Drag Target.
*/
set dropDisabled(value) {
this._dropDisabled = value;
if (value) {
this.clearPreviousTargets();
}
else {
this.initializeDropTargets();
}
}
get dropDisabled() {
return this._dropDisabled;
}
/**
* Fires when a DragTarget element enters the DropTarget.
*/
onDragEnter = new EventEmitter();
/**
* Fires when a DragTarget element is being dragged over the DropTarget.
*/
onDragOver = new EventEmitter();
/**
* Fires when a DragTarget element leaves the DropTarget.
*/
onDragLeave = new EventEmitter();
/**
* Fires when a DragTarget element is dropped over the DropTarget.
*/
onDrop = new EventEmitter();
/**
* Used for notifying the DropTargetContainer that its content has changed.
*/
notify() {
this.cdr.detectChanges();
this.initializeDropTargets();
}
constructor(service, element, ngZone, cdr) {
this.service = service;
this.element = element;
this.ngZone = ngZone;
this.cdr = cdr;
validatePackage(packageMetadata);
}
currentDropTargetElement = null;
previousDropTargets = [];
_dropTargetFilter = null;
_dropDisabled = false;
get nativeElement() {
return this.element.nativeElement;
}
ngAfterViewInit() {
!this.dropDisabled && this.initializeDropTargets();
}
get allDropTargets() {
if (isPresent(this.dropTargetFilter) && this.dropTargetFilter !== '') {
return Array.from(this.nativeElement.querySelectorAll(this.dropTargetFilter));
}
}
/**
* @hidden
*/
handleDragEnter(event) {
if (!this.service.dragTargetPresent || this.service.dropTargetPresent) {
return;
}
const currDragTargetElement = this.service.dragTarget.hint || this.service.dragTarget.element;
const currDropTargetElem = intersect(currDragTargetElement, this.allDropTargets);
const currDropTarget = this.service.dropTargets.find(dt => dt.element === currDropTargetElem);
if (!isPresent(currDropTargetElem) || !isPresent(currDropTarget)) {
return;
}
this.currentDropTargetElement = currDropTargetElem;
this.service.dropTarget = currDropTarget;
this.service.dropIndex = this.getDropIndex();
this.emitZoneAwareEvent('onDragEnter', event);
}
/**
* @hidden
*/
handleDragLeave(event) {
if (!this.service.dragTargetPresent || !this.service.dropTargetPresent) {
return;
}
this.emitZoneAwareEvent('onDragLeave', event);
this.currentDropTargetElement = null;
this.service.dropTarget = null;
this.service.dropIndex = null;
}
/**
* @hidden
*/
handleDragOver(event) {
if (!this.service.dragTargetPresent || !this.service.dropTargetPresent) {
return;
}
this.emitZoneAwareEvent('onDragOver', event);
}
/**
* @hidden
*/
handleDrop(event) {
if (!this.service.dragTargetPresent || !this.service.dropTargetPresent) {
return;
}
this.emitZoneAwareEvent('onDrop', event);
this.currentDropTargetElement = null;
this.service.dropTarget = null;
this.service.dropIndex = null;
}
initializeDropTargets() {
if (!isPresent(this.allDropTargets)) {
if (this.previousDropTargets.length > 0) {
this.clearPreviousTargets();
}
return;
}
this.allDropTargets.forEach(dropTargetEl => {
const isDropTargetInitialized = this.service.dropTargets.find(dt => dt.element === dropTargetEl);
if (!isDropTargetInitialized) {
this.service.dropTargets.push({
element: dropTargetEl,
onDragEnter: this.handleDragEnter.bind(this),
onDragLeave: this.handleDragLeave.bind(this),
onDragOver: this.handleDragOver.bind(this),
onDrop: this.handleDrop.bind(this)
});
}
});
if (this.previousDropTargets.length > 0) {
const dropTargetsToRemove = this.previousDropTargets.filter(dt => !this.allDropTargets.includes(dt));
dropTargetsToRemove.forEach(dropTarget => {
const idx = this.service.dropTargets.findIndex(serviceDropTarget => serviceDropTarget.element === dropTarget);
if (idx > -1) {
this.service.dropTargets.splice(idx, 1);
}
});
}
this.previousDropTargets = this.allDropTargets;
}
emitZoneAwareEvent(event, normalizedEvent) {
const eventProps = {
dragTarget: this.service.dragTarget?.element,
dropTarget: this.currentDropTargetElement,
dragData: this.service.dragData,
dragEvent: normalizedEvent,
dropTargetIndex: this.service.dropIndex
};
if (isPresent(this.service.dragTarget?.hint)) {
eventProps.hintElement = this.service.dragTarget.hint;
}
const eventArgs = new DropTargetEvent(eventProps);
this.ngZone.run(() => {
this[event].emit(eventArgs);
});
}
getDropIndex() {
return this.allDropTargets.indexOf(this.currentDropTargetElement);
}
clearPreviousTargets() {
this.previousDropTargets.forEach(dropTarget => {
const idx = this.service.dropTargets.findIndex(serviceDropTarget => serviceDropTarget.element === dropTarget);
if (idx > -1) {
this.service.dropTargets.splice(idx, 1);
}
});
this.previousDropTargets = [];
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DropTargetContainerDirective, deps: [{ token: i1.DragStateService }, { token: i0.ElementRef }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: DropTargetContainerDirective, isStandalone: true, selector: "[kendoDropTargetContainer]", inputs: { dropTargetFilter: "dropTargetFilter", dropDisabled: "dropDisabled" }, outputs: { onDragEnter: "onDragEnter", onDragOver: "onDragOver", onDragLeave: "onDragLeave", onDrop: "onDrop" }, exportAs: ["kendoDropTargetContainer"], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DropTargetContainerDirective, decorators: [{
type: Directive,
args: [{
selector: '[kendoDropTargetContainer]',
exportAs: 'kendoDropTargetContainer',
standalone: true
}]
}], ctorParameters: function () { return [{ type: i1.DragStateService }, { type: i0.ElementRef }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { dropTargetFilter: [{
type: Input
}], dropDisabled: [{
type: Input
}], onDragEnter: [{
type: Output
}], onDragOver: [{
type: Output
}], onDragLeave: [{
type: Output
}], onDrop: [{
type: Output
}] } });