UNPKG

@progress/kendo-angular-utils

Version:

Kendo UI Angular utils component

235 lines (234 loc) 9.39 kB
/**----------------------------------------------------------------------------------------- * 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 }] } });