@progress/kendo-angular-utils
Version:
Kendo UI Angular utils component
1,366 lines (1,353 loc) • 95 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 * as i0 from '@angular/core';
import { Directive, HostBinding, Input, Injectable, Component, isDevMode, EventEmitter, Output, ContentChildren, NgModule, InjectionToken, Optional, Inject } from '@angular/core';
import { validatePackage } from '@progress/kendo-licensing';
import { dispatchDragAndDrop, getScrollableParent, autoScroll } from '@progress/kendo-draggable-common';
import { PreventableEvent, contains, isDocumentAvailable, parseCSSClassNames, isPresent as isPresent$1, areObjectsEqual } from '@progress/kendo-angular-common';
import { NgTemplateOutlet } from '@angular/common';
import { Subject, BehaviorSubject, Subscription, fromEvent } from 'rxjs';
import { map, tap, filter } from 'rxjs/operators';
/**
* Represents the Kendo UI DragHandle directive for Angular.
* It is used to specify a concrete element within a drag target as a handle for dragging, instead the drag target itself.
*/
class DragHandleDirective {
element;
touchActionStyle = 'none';
/**
* Specifies the cursor style of the drag handle. Accepts same values as the [CSS `cursor` property](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#values).
*
* @default 'move'
*/
cursorStyle = 'move';
constructor(element) {
this.element = element;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DragHandleDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: DragHandleDirective, isStandalone: true, selector: "[kendoDragHandle]", inputs: { cursorStyle: "cursorStyle" }, host: { properties: { "style.touch-action": "this.touchActionStyle", "style.cursor": "this.cursorStyle" } }, exportAs: ["kendoDragHandle"], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DragHandleDirective, decorators: [{
type: Directive,
args: [{
selector: '[kendoDragHandle]',
exportAs: 'kendoDragHandle',
standalone: true
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { touchActionStyle: [{
type: HostBinding,
args: ['style.touch-action']
}], cursorStyle: [{
type: HostBinding,
args: ['style.cursor']
}, {
type: Input
}] } });
/**
* @hidden
*/
const packageMetadata = {
name: '@progress/kendo-angular-utils',
productName: 'Kendo UI for Angular',
productCode: 'KENDOUIANGULAR',
productCodes: ['KENDOUIANGULAR'],
publishDate: 1749540010,
version: '19.1.1',
licensingDocsUrl: 'https://www.telerik.com/kendo-angular-ui/my-license/'
};
function isDocumentNode(container) {
return container.nodeType === 9;
}
/**
* @hidden
*/
const getAction = (event, draggable) => {
return {
event: event,
payload: draggable
};
};
/**
* @hidden
*/
const dragTargetTransition = 'transform .3s ease-in-out';
/**
* @hidden
*/
const isPresent = (value) => value !== null && value !== undefined;
/**
* @hidden
*/
function closestBySelector(element, selector) {
if (element.closest) {
return element.closest(selector);
}
const matches = Element.prototype.matches ?
(el, sel) => el.matches(sel)
: (el, sel) => el.msMatchesSelector(sel);
let node = element;
while (node && !isDocumentNode(node)) {
if (matches(node, selector)) {
return node;
}
node = node.parentNode;
}
}
/**
* @hidden
*/
const intersect = (element, candidates) => {
let max = 0;
let result = null;
candidates.forEach((candidate) => {
if (candidate && element) {
const ration = getRatio(element, candidate);
if (ration > max) {
max = ration;
result = candidate;
}
}
});
return result;
};
const getRatio = (element, target) => {
const elementRect = element.getBoundingClientRect();
const targetRect = target.getBoundingClientRect();
const top = Math.max(targetRect.top, elementRect.top);
const left = Math.max(targetRect.left, elementRect.left);
const right = Math.min(targetRect.left + targetRect.width, elementRect.left + elementRect.width);
const bottom = Math.min(targetRect.top + targetRect.height, elementRect.top + elementRect.height);
const width = right - left;
const height = bottom - top;
if (left < right && top < bottom) {
const targetArea = targetRect.width * targetRect.height;
const entryArea = elementRect.width * elementRect.height;
const intersectionArea = width * height;
const intersectionRatio = intersectionArea / (targetArea + entryArea - intersectionArea);
return Number(intersectionRatio.toFixed(4));
}
return 0;
};
/**
* @hidden
*/
const setElementStyles = (renderer, elem, styles) => {
const props = Object.keys(styles);
props.forEach(p => {
renderer.setStyle(elem, p, styles[p]);
});
};
/**
* @hidden
*/
const noop = () => { };
/**
* @hidden
*/
class DragStateService {
constructor() {
this.setCallbacks();
}
dragTarget = null;
dropTarget = null;
dragTargets = [];
dropTargets = [];
pressed = false;
ignoreMouse = false;
autoScroll = true;
isScrolling = false;
scrollableParent = null;
autoScrollDirection = { horizontal: true, vertical: true };
initialClientOffset = { x: 0, y: 0 };
clientOffset = { x: 0, y: 0 };
initialScrollOffset = { x: 0, y: 0 };
scrollOffset = { x: 0, y: 0 };
offset = { x: 0, y: 0 };
pageOffset = { x: 0, y: 0 };
velocity = { x: 0, y: 0 };
dragTargetDirective;
state;
dragIndex = null;
dropIndex = null;
dragData;
dragTargetId;
callbacks = {};
scrollInterval = null;
handleDragAndDrop(action) {
this.updateState();
dispatchDragAndDrop(this.state, action, this.callbacks);
}
setPressed(pressed) {
this.pressed = pressed;
}
setScrolling(isScrolling) {
this.isScrolling = isScrolling;
if (isScrolling) {
const scrollableParent = getScrollableParent(document.elementFromPoint(this.clientOffset.x, this.clientOffset.y));
window.clearInterval(this.scrollInterval);
this.scrollInterval = window.setInterval(() => {
autoScroll(scrollableParent, { x: this.velocity.x, y: this.velocity.y });
}, 50);
}
else {
if (this.scrollInterval) {
window.clearInterval(this.scrollInterval);
this.scrollInterval = null;
}
}
}
setVelocity(velocity) {
this.velocity = velocity;
}
setOffset(offset) {
this.offset = offset;
}
setClientOffset(clientOffset) {
this.clientOffset = clientOffset;
}
setPageOffset(pageOffset) {
this.pageOffset = pageOffset;
}
setInitialClientOffset(initialClientOffset) {
this.initialClientOffset = initialClientOffset;
}
setScrollOffset(scrollOffset) {
this.scrollOffset = scrollOffset;
}
setInitialScrollOffset(initialScrollOffset) {
this.initialScrollOffset = initialScrollOffset;
}
get dragTargetPresent() {
return isPresent(this.dragTarget?.element);
}
get dropTargetPresent() {
return isPresent(this.dropTarget?.element);
}
updateState() {
this.state = {
drag: this.dragTarget,
drop: this.dropTarget,
drags: this.dragTargets,
drops: this.dropTargets,
pressed: this.pressed,
ignoreMouse: this.ignoreMouse,
autoScroll: this.autoScroll,
isScrolling: this.isScrolling,
scrollableParent: this.scrollableParent,
autoScrollDirection: this.autoScrollDirection,
initialClientOffset: this.initialClientOffset,
clientOffset: this.clientOffset,
initialScrollOffset: this.initialScrollOffset,
scrollOffset: this.scrollOffset,
offset: this.offset,
pageOffset: this.pageOffset,
velocity: this.velocity
};
}
setCallbacks() {
this.callbacks = {
onVelocityChange: this.setVelocity.bind(this),
onOffsetChange: this.setOffset.bind(this),
onClientOffsetChange: this.setClientOffset.bind(this),
onPageOffsetChange: this.setPageOffset.bind(this),
onInitialClientOffsetChange: this.setInitialClientOffset.bind(this),
onScrollOffsetChange: this.setScrollOffset.bind(this),
onInitialScrollOffsetChange: this.setInitialScrollOffset.bind(this),
onIsPressedChange: this.setPressed.bind(this),
onIsScrollingChange: this.setScrolling.bind(this)
};
}
ngOnDestroy() {
if (this.scrollInterval) {
window.clearInterval(this.scrollInterval);
this.scrollInterval = null;
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DragStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DragStateService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DragStateService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: function () { return []; } });
/**
* @hidden
*/
class HintComponent {
element;
template;
directive;
targetIndex;
contextData;
customContext;
pointerEvents = 'none';
constructor(element) {
this.element = element;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HintComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: HintComponent, isStandalone: true, selector: "kendo-draghint", inputs: { template: "template", directive: "directive", targetIndex: "targetIndex", contextData: "contextData", customContext: "customContext" }, host: { properties: { "style.pointer-events": "this.pointerEvents" } }, ngImport: i0, template: `
<ng-container
[ngTemplateOutlet]="template"
[ngTemplateOutletContext]="customContext || { $implicit: this.directive, index: this.targetIndex, data: this.contextData }">
</ng-container>
`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HintComponent, decorators: [{
type: Component,
args: [{
selector: 'kendo-draghint',
template: `
<ng-container
[ngTemplateOutlet]="template"
[ngTemplateOutletContext]="customContext || { $implicit: this.directive, index: this.targetIndex, data: this.contextData }">
</ng-container>
`,
standalone: true,
imports: [NgTemplateOutlet]
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { template: [{
type: Input
}], directive: [{
type: Input
}], targetIndex: [{
type: Input
}], contextData: [{
type: Input
}], customContext: [{
type: Input
}], pointerEvents: [{
type: HostBinding,
args: ['style.pointer-events']
}] } });
/**
* Arguments for the press event of the DragTarget and DragTargetContainer.
*/
class DragTargetPressEvent {
/**
* The normalized drag event.
*/
dragEvent;
/**
* The DOM element that is being dragged.
*/
dragTarget;
/**
* The identifier passed to the `dragTargetId` input property of the `DragTarget` or `DragTargetContainer` directive.
*/
dragTargetId;
/**
* The index of the current drag target in the collection of drag targets (applicable for the `DragTargetContainer` directive).
*/
dragTargetIndex;
/**
* Left for backward compatibility for the DragTarget deprecated events.
* @hidden
*/
get normalizedEvent() {
return this.dragEvent;
}
/**
* Left for backward compatibility for the DragTarget deprecated events.
* @hidden
*/
get hostElement() {
return this.dragTarget;
}
/**
* @hidden
*/
constructor(args) {
Object.assign(this, args);
}
}
/**
* Arguments for the dragReady event of the DragTarget and DragTargetContainer.
*/
class DragTargetDragReadyEvent {
/**
* The normalized drag event.
*/
dragEvent;
/**
* The DOM element that is being dragged.
*/
dragTarget;
/**
* The identifier passed to the `dragTargetId` input property of the `DragTarget` or `DragTargetContainer` directive.
*/
dragTargetId;
/**
* The index of the current drag target in the collection of drag targets (applicable for the `DragTargetContainer` directive).
*/
dragTargetIndex;
/**
* @hidden
*/
constructor(args) {
Object.assign(this, args);
}
}
/**
* Arguments for the dragStart event of the DragTarget and DragTargetContainer.
*/
class DragTargetDragStartEvent extends PreventableEvent {
/**
* The normalized drag event.
*/
dragEvent;
/**
* The DOM element that is being dragged.
*/
dragTarget;
/**
* The identifier passed to the `dragTargetId` input property of the `DragTarget` or `DragTargetContainer` directive.
*/
dragTargetId;
/**
* The index of the current drag target in the collection of drag targets (applicable for the `DragTargetContainer` directive).
*/
dragTargetIndex;
/**
* Left for backward compatibility for the DragTarget deprecated events.
* @hidden
*/
get normalizedEvent() {
return this.dragEvent;
}
/**
* Left for backward compatibility for the DragTarget deprecated events.
* @hidden
*/
get hostElement() {
return this.dragTarget;
}
/**
* @hidden
*/
constructor(args) {
super();
Object.assign(this, args);
}
}
/**
* Arguments for the dragEnd event of the DragTarget and DragTargetContainer.
*/
class DragTargetDragEndEvent {
/**
* The normalized drag event.
*/
dragEvent;
/**
* The DOM element that is being dragged.
*/
dragTarget;
/**
* The identifier passed to the `dragTargetId` input property of the `DragTarget` or `DragTargetContainer` directive.
*/
dragTargetId;
/**
* The index of the current drag target in the collection of drag targets (applicable for the `DragTargetContainer` directive).
*/
dragTargetIndex;
/**
* Left for backward compatibility for the DragTarget deprecated events.
* @hidden
*/
get normalizedEvent() {
return this.dragEvent;
}
/**
* Left for backward compatibility for the DragTarget deprecated events.
* @hidden
*/
get hostElement() {
return this.dragTarget;
}
/**
* @hidden
*/
constructor(args) {
Object.assign(this, args);
}
}
/**
* Arguments for the drag event of the DragTarget and DragTargetContainer.
*/
class DragTargetDragEvent extends PreventableEvent {
/**
* The normalized drag event.
*/
dragEvent;
/**
* The DOM element that is being dragged.
*/
dragTarget;
/**
* The hint of the DragTarget.
*/
hintElement;
/**
* The identifier passed to the `dragTargetId` input property of the `DragTarget` or `DragTargetContainer` directive.
*/
dragTargetId;
/**
* The index of the current drag target in the collection of drag targets (applicable for the `DragTargetContainer` directive).
*/
dragTargetIndex;
/**
* Left for backward compatibility for the DragTarget deprecated events.
* @hidden
*/
get normalizedEvent() {
return this.dragEvent;
}
/**
* Left for backward compatibility for the DragTarget deprecated events.
* @hidden
*/
get hostElement() {
return this.dragTarget;
}
/**
* @hidden
*/
constructor(args) {
super();
Object.assign(this, args);
}
}
/**
* Arguments for the release event of the DragTarget and DragTargetContainer.
*/
class DragTargetReleaseEvent {
/**
* The normalized drag event.
*/
dragEvent;
/**
* The DOM element that is being dragged.
*/
dragTarget;
/**
* The identifier passed to the `dragTargetId` input property of the `DragTarget` or `DragTargetContainer` directive.
*/
dragTargetId;
/**
* The index of the current drag target in the collection of drag targets (applicable for the `DragTargetContainer` directive).
*/
dragTargetIndex;
/**
* Left for backward compatibility for the DragTarget deprecated events.
* @hidden
*/
get normalizedEvent() {
return this.dragEvent;
}
/**
* Left for backward compatibility for the DragTarget deprecated events.
* @hidden
*/
get hostElement() {
return this.dragTarget;
}
/**
* @hidden
*/
constructor(args) {
Object.assign(this, args);
}
}
let isDragStartPrevented$1 = false;
let isDragPrevented$1 = false;
/**
* Represents the [Kendo UI DragTargetContainer directive for Angular]({% slug api_utils_dragtargetcontainerdirective %}).
* Used to configure multiple elements as draggable.
*
* @example
* ```ts-no-run
* <ul kendoDragTargetContainer dragTargetFilter=".my-draggable">
* <li class="my-draggable">foo</li>
* </ul>
* ```
*/
class DragTargetContainerDirective {
wrapper;
ngZone;
renderer;
service;
viewContainer;
cdr;
/**
* Defines whether a hint will be used for dragging. By default, the hint is a copy of the current drag target. ([see example]({% slug drag_hint %})).
*
* @default false
*/
hint = false;
/**
* Specifies a selector for elements within a container which will be configured as draggable
* ([see example]({% slug drag_target_container %})). The possible values include any
* DOM `selector`.
*/
set dragTargetFilter(value) {
this._dragTargetFilter = value;
if (!this.dragDisabled) {
this.initializeDragTargets();
}
}
get dragTargetFilter() {
return this._dragTargetFilter;
}
/**
* Specifies a selector for elements within each DragTarget which will be configured as drag handles.
*/
dragHandle;
/**
* Defines the delay in milliseconds after which the drag will begin ([see example](slug:drag_target_container#toc-events)).
*
* @default 0
*/
dragDelay = 0;
/**
* The number of pixels the pointer moves in any direction before the dragging starts ([see example]({% slug minimum_distance %})).
*
* @default 0
*/
threshold = 0;
/**
* Defines a unique identifier for each drag target.
* It exposes the current DragTarget HTML element and its index in the collection of drag targets as arguments.
*/
set dragTargetId(fn) {
if (isDevMode && typeof fn !== 'function') {
throw new Error(`dragTargetId must be a function, but received ${JSON.stringify(fn)}.`);
}
this._dragTargetId = fn;
}
get dragTargetId() {
return this._dragTargetId;
}
/**
* Defines a callback function which returns custom data passed to the DropTarget events.
* It exposes the current DragTarget HTML element, its `dragTargetId` and its index in the collection of drag targets as arguments.
*/
set dragData(fn) {
if (isDevMode && typeof fn !== 'function') {
throw new Error(`dragData must be a function, but received ${JSON.stringify(fn)}.`);
}
this._dragData = fn;
}
get dragData() {
return this._dragData;
}
/**
* If set to true, the dragging of DragTargets within the container will be disabled.
*
* @default false
*/
set dragDisabled(value) {
this._dragDisabled = value;
if (value) {
this.clearPreviousTargets();
this.removeListeners();
if (isPresent(this.hintElem)) {
this.destroyHint();
}
}
else {
if (isPresent(this.wrapper) || isPresent(this.currentDragTarget)) {
this.subscribe();
}
this.initializeDragTargets();
}
}
get dragDisabled() {
return this._dragDisabled;
}
/**
* Specifies whether the default dragging behavior will be performed or the developer will manually handle the drag action.
*
* @default 'auto'
*/
mode = 'auto';
/**
* Specifies the cursor style of the drag targets. Accepts same values as the [CSS `cursor` property](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#values).
*
* @default 'move'
*/
cursorStyle = 'move';
/**
* @hidden
*/
hintContext;
/**
* Fires when a DragTarget's `dragDelay` has passed and the user is able to drag the element.
*/
onDragReady = new EventEmitter();
/**
* Fires when the user presses a DragTarget element.
*/
onPress = new EventEmitter();
/**
* Fires when the dragging of a DragTarget element begins.
*/
onDragStart = new EventEmitter();
/**
* Fires while the user drags a DragTarget element.
*/
onDrag = new EventEmitter();
/**
* Fires when the user releases a DragTarget element after being pressed.
*/
onRelease = new EventEmitter();
/**
* Fires when the dragging of a DragTarget ends and the element is released.
*/
onDragEnd = new EventEmitter();
/**
* Used for notifying the DragTargetContainer that its content has changed.
*/
notify() {
this.cdr.detectChanges();
this.initializeDragTargets();
}
currentDragTarget = null;
dragTimeout = null;
pressed = false;
dragStarted = false;
hintComponent = null;
defaultHint = null;
currentDragTargetElement = null;
scrollableParent = null;
previousDragTargets = [];
initialPosition = { x: 0, y: 0 };
position = { x: 0, y: 0 };
positionsMap = new Map();
_dragTargetFilter = null;
_dragDisabled = false;
_dragData = () => null;
_dragTargetId = () => null;
prevUserSelect;
get allDragTargets() {
return this.queryHost(this.dragTargetFilter);
}
get dragHandles() {
return this.isHandleSelectorValid ? this.queryHost(this.dragHandle) : null;
}
get hintTemplate() {
return isPresent(this.hint) && typeof this.hint === 'object' ? this.hint.hintTemplate : null;
}
constructor(wrapper, ngZone, renderer, service, viewContainer, cdr) {
this.wrapper = wrapper;
this.ngZone = ngZone;
this.renderer = renderer;
this.service = service;
this.viewContainer = viewContainer;
this.cdr = cdr;
validatePackage(packageMetadata);
}
ngAfterViewInit() {
const isTargetPresent = isPresent(this.wrapper) || isPresent(this.currentDragTarget);
if (!this.dragDisabled && isTargetPresent) {
this.subscribe();
}
!this.dragDisabled && this.initializeDragTargets();
}
ngOnDestroy() {
this.removeListeners();
}
onPointerDown(event) {
const filterElement = closestBySelector(event.target, this.isHandleSelectorValid ? this.dragHandle : this.dragTargetFilter);
if (this.dragTargetFilter === '' || !isPresent(filterElement)) {
return;
}
if (isPresent(this.dragHandles) && !this.isDragHandle(event.target)) {
return;
}
const action = getAction(event, this.currentDragTarget);
this.service.handleDragAndDrop(action);
this.subscribe();
}
onTouchStart(event) {
const filterElement = closestBySelector(event.target, this.isHandleSelectorValid ? this.dragHandle : this.dragTargetFilter);
if (this.dragTargetFilter === '' || !isPresent(filterElement)) {
return;
}
if (isPresent(this.dragHandles) && !this.isDragHandle(event.target)) {
return;
}
event.preventDefault();
const action = getAction(event, this.currentDragTarget);
this.service.handleDragAndDrop(action);
this.subscribe();
}
onPointerMove(event) {
const action = getAction(event, this.currentDragTarget);
this.service.handleDragAndDrop(action);
}
onTouchMove(event) {
event.preventDefault();
const action = getAction(event, this.currentDragTarget);
this.service.handleDragAndDrop(action);
}
onPointerUp(event) {
const action = getAction(event, this.currentDragTarget);
this.service.handleDragAndDrop(action);
this.subscribe();
}
onContextMenu(event) {
event.preventDefault();
const action = getAction(event, this.currentDragTarget);
this.service.handleDragAndDrop(action);
this.subscribe();
}
handlePress(event) {
if (this.dragDelay > 0) {
this.dragTimeout = window.setTimeout(() => {
this.pressed = true;
this.emitZoneAwareEvent('onDragReady', event);
}, this.dragDelay);
}
else {
this.pressed = true;
}
const eventTarget = event.originalEvent.target;
this.currentDragTargetElement = closestBySelector(eventTarget, this.dragTargetFilter);
this.currentDragTarget.element = this.currentDragTargetElement;
this.service.dragIndex = this.getDragIndex();
this.scrollableParent = this.hintTemplate ? document.body : this.currentDragTargetElement ? getScrollableParent(this.currentDragTargetElement) : null;
this.prevUserSelect = this.currentDragTargetElement.style.userSelect;
this.renderer.setStyle(this.currentDragTargetElement, 'user-select', 'none');
this.emitZoneAwareEvent('onPress', event);
}
handleDragStart(event) {
if (!this.pressed) {
if (this.dragTimeout) {
window.clearTimeout(this.dragTimeout);
this.dragTimeout = null;
}
return;
}
isDragStartPrevented$1 = this.emitZoneAwareEvent('onDragStart', event).isDefaultPrevented();
if (isDragStartPrevented$1) {
return;
}
this.position = this.positionsMap.has(this.currentDragTargetElement) ? this.positionsMap.get(this.currentDragTargetElement) : { x: 0, y: 0 };
if (this.hint) {
this.createHint();
if (this.mode === 'auto') {
this.renderer.setStyle(this.currentDragTargetElement, 'opacity', '0.7');
}
}
else {
this.initialPosition = { x: event.clientX - this.position.x, y: event.clientY - this.position.y };
}
this.dragStarted = this.threshold === 0;
this.service.dragTarget = this.currentDragTarget;
const targetIdArgs = { dragTarget: this.currentDragTargetElement, dragTargetIndex: this.service.dragIndex };
this.service.dragTargetId = this.dragTargetId(targetIdArgs);
const targetDataArgs = Object.assign({ dragTargetId: this.service.dragTargetId }, targetIdArgs);
this.service.dragData = this.dragData(targetDataArgs);
}
handleDrag(event) {
if (!this.pressed || isDragStartPrevented$1) {
return;
}
const elem = this.hint ? this.hintElem : this.currentDragTargetElement;
this.position = this.calculatePosition(elem, event);
const thresholdNotReached = Math.abs(this.position.x) < this.threshold && Math.abs(this.position.y) < this.threshold;
if (!this.dragStarted && thresholdNotReached) {
return;
}
if (!this.dragStarted && this.threshold > 0) {
this.dragStarted = true;
}
isDragPrevented$1 = this.emitZoneAwareEvent('onDrag', event).isDefaultPrevented();
if (isDragPrevented$1) {
return;
}
if (this.mode === 'auto') {
this.performDrag();
}
else {
this.dragStarted = true;
}
}
handleRelease(event) {
if (this.dragStarted) {
this.positionsMap.set(this.currentDragTargetElement, this.position);
}
if (this.dragTimeout) {
clearTimeout(this.dragTimeout);
this.dragTimeout = null;
}
this.pressed = false;
this.prevUserSelect ? this.renderer.setStyle(this.currentDragTargetElement, 'user-select', this.prevUserSelect) :
this.renderer.removeStyle(this.currentDragTargetElement, 'user-select');
this.prevUserSelect = null;
this.emitZoneAwareEvent('onRelease', event);
}
handleDragEnd(event) {
if (!this.dragStarted) {
return;
}
if (this.mode === 'auto') {
const isDroppedOverParentTarget = isPresent(this.service.dropTarget) && !contains(this.service.dropTarget?.element, this.service.dragTarget?.element, true);
const elem = this.hint ? this.hintElem : this.currentDragTargetElement;
if (isDroppedOverParentTarget || this.service.dropTargets.length > 0 && isPresent(elem)) {
this.renderer.removeStyle(elem, 'transform');
setElementStyles(this.renderer, elem, {
transition: dragTargetTransition
});
this.positionsMap.delete(this.currentDragTargetElement);
}
}
if (this.hint && isPresent(this.hintElem)) {
this.destroyHint();
if (this.mode === 'auto') {
this.renderer.removeStyle(this.currentDragTargetElement, 'opacity');
}
}
this.service.dragTarget = null;
this.service.dragIndex = null;
this.currentDragTarget.element = null;
this.emitZoneAwareEvent('onDragEnd', event);
if (isDragStartPrevented$1 || isDragPrevented$1) {
return;
}
this.dragStarted = false;
}
get nativeElement() {
return this.wrapper.nativeElement;
}
get hintElem() {
return this.hintTemplate && isPresent(this.hintComponent) ? this.hintComponent.instance.element.nativeElement : this.defaultHint;
}
removeListeners() {
if (isPresent(this.scrollableParent)) {
this.scrollableParent.removeEventListener('scroll', this.onPointerMove);
}
const element = this.nativeElement;
if (!isDocumentAvailable()) {
return;
}
document.removeEventListener('pointermove', this.onPointerMove);
document.removeEventListener('pointerup', this.onPointerUp, true);
document.removeEventListener('pointercancel', this.onPointerUp);
document.removeEventListener('contextmenu', this.onContextMenu);
window.removeEventListener('touchmove', noop);
element.removeEventListener('touchmove', this.onTouchMove);
element.removeEventListener('touchend', this.onPointerUp);
document.removeEventListener('mousemove', this.onPointerMove);
document.removeEventListener('mouseup', this.onPointerUp);
document.removeEventListener('touchcancel', this.onPointerUp);
element.removeEventListener('pointerdown', this.onPointerDown);
element.removeEventListener('mousedown', this.onPointerDown);
element.removeEventListener('touchstart', this.onTouchStart);
}
get supportPointerEvent() {
return Boolean(typeof window !== 'undefined' && window.PointerEvent);
}
subscribe() {
this.ngZone.runOutsideAngular(() => {
this.removeListeners();
if (!(isDocumentAvailable() && isPresent(this.wrapper))) {
return;
}
this.onPointerMove = this.onPointerMove.bind(this);
this.onPointerUp = this.onPointerUp.bind(this);
this.onTouchMove = this.onTouchMove.bind(this);
this.onContextMenu = this.onContextMenu.bind(this);
this.onPointerDown = this.onPointerDown.bind(this);
this.onTouchStart = this.onTouchStart.bind(this);
const element = this.nativeElement;
if (this.supportPointerEvent) {
if (isPresent(this.scrollableParent)) {
this.scrollableParent.addEventListener('scroll', this.onPointerMove, { passive: true });
}
element.addEventListener('pointerdown', this.onPointerDown, { passive: true });
if (this.pressed) {
document.addEventListener('pointermove', this.onPointerMove);
document.addEventListener('pointerup', this.onPointerUp, true);
document.addEventListener('contextmenu', this.onContextMenu);
document.addEventListener('pointercancel', this.onPointerUp, { passive: true });
}
}
else {
window.addEventListener('touchmove', noop, { capture: false, passive: false });
element.addEventListener('mousedown', this.onPointerDown, { passive: true });
element.addEventListener('touchstart', this.onTouchStart, { passive: true });
if (this.pressed) {
document.addEventListener('mousemove', this.onPointerMove, { passive: true });
document.addEventListener('mouseup', this.onPointerUp, { passive: true });
element.addEventListener('touchmove', this.onTouchMove, { passive: true });
element.addEventListener('touchend', this.onPointerUp, { passive: true });
}
}
});
}
emitZoneAwareEvent(event, normalizedEvent) {
const targetIdArgs = { dragTarget: this.currentDragTargetElement, dragTargetIndex: this.service.dragIndex };
const eventProps = {
dragTarget: this.currentDragTargetElement,
dragEvent: normalizedEvent,
dragTargetIndex: this.service.dragIndex,
dragTargetId: this.dragTargetId(targetIdArgs)
};
if (this.hint && isPresent(this.hintElem)) {
eventProps.hintElement = this.hintElem;
}
let eventArgs;
switch (event) {
case 'onDragReady':
eventArgs = new DragTargetDragReadyEvent(eventProps);
break;
case 'onPress':
eventArgs = new DragTargetPressEvent(eventProps);
break;
case 'onDragStart':
eventArgs = new DragTargetDragStartEvent(eventProps);
break;
case 'onDrag':
eventArgs = new DragTargetDragEvent(eventProps);
break;
case 'onRelease':
eventArgs = new DragTargetReleaseEvent(eventProps);
break;
case 'onDragEnd':
eventArgs = new DragTargetDragEndEvent(eventProps);
break;
default:
break;
}
this.ngZone.run(() => {
this[event].emit(eventArgs);
});
return eventArgs;
}
createHint() {
if (!(isDocumentAvailable() && isPresent(this.wrapper))) {
return;
}
if (isPresent(this.hint) && typeof this.hint === 'object') {
if (isPresent(this.hint.hintTemplate)) {
this.createCustomHint();
}
else {
this.createDefaultHint();
}
}
else {
this.createDefaultHint();
}
this.currentDragTarget.hint = this.hintElem;
if (typeof this.hint === 'object' && isPresent(this.hint.appendTo)) {
this.hint.appendTo.element.nativeElement.appendChild(this.hintElem);
}
else {
document.body.appendChild(this.hintElem);
}
}
createDefaultHint() {
this.defaultHint = this.currentDragTargetElement.cloneNode(true);
if (typeof this.hint === 'object') {
if (isPresent(this.hint.hintClass)) {
const hintClasses = parseCSSClassNames(this.hint.hintClass);
hintClasses.forEach(className => this.renderer.addClass(this.defaultHint, className));
}
}
}
createCustomHint() {
if (isPresent(this.hint.appendTo)) {
this.hintComponent = this.hint.appendTo.createComponent(HintComponent);
}
else {
this.hintComponent = this.viewContainer.createComponent(HintComponent);
}
this.hintComponent.instance.template = this.hintTemplate;
this.hintComponent.instance.directive = this;
this.hintComponent.instance.targetIndex = this.service.dragIndex;
const targetDataArgs = { dragTarget: this.currentDragTargetElement, dragTargetId: this.service.dragTargetId, dragTargetIndex: this.service.dragIndex };
this.hintComponent.instance.contextData = this.dragData(targetDataArgs);
this.hintComponent.instance.customContext = this.hintContext;
this.hintComponent.changeDetectorRef.detectChanges();
}
destroyHint() {
if (isPresent(this.hintTemplate)) {
this.hintComponent.destroy();
this.hintComponent.changeDetectorRef.detectChanges();
this.hintComponent = null;
}
else {
document.body.removeChild(this.defaultHint);
this.defaultHint = null;
}
this.currentDragTarget.hint = null;
}
getDragIndex() {
return this.allDragTargets.indexOf(this.currentDragTargetElement);
}
initializeDragTargets() {
if (!isPresent(this.allDragTargets)) {
if (this.previousDragTargets.length > 0) {
this.clearPreviousTargets();
}
return;
}
this.allDragTargets.forEach(dragTargetEl => {
const isDragTargetInitialized = this.service.dragTargets.find(dt => dt.element === dragTargetEl);
if (!isDragTargetInitialized) {
this.service.dragTargets.push({
element: dragTargetEl,
hint: null,
onPress: this.handlePress.bind(this),
onRelease: this.handleRelease.bind(this),
onDragStart: this.handleDragStart.bind(this),
onDrag: this.handleDrag.bind(this),
onDragEnd: this.handleDragEnd.bind(this)
});
}
});
if (this.previousDragTargets.length > 0) {
const dragTargetsToRemove = this.previousDragTargets.filter(dt => !this.allDragTargets.includes(dt));
dragTargetsToRemove.forEach(dragTarget => {
const idx = this.service.dragTargets.findIndex(serviceDragTarget => serviceDragTarget.element === dragTarget);
if (idx > -1) {
this.service.dragTargets.splice(idx, 1);
}
});
}
this.previousDragTargets = this.allDragTargets;
this.currentDragTarget = {
element: null,
hint: null,
onPress: this.handlePress.bind(this),
onRelease: this.handleRelease.bind(this),
onDragStart: this.handleDragStart.bind(this),
onDrag: this.handleDrag.bind(this),
onDragEnd: this.handleDragEnd.bind(this)
};
this.setTargetStyles();
}
isDragHandle(el) {
return this.dragHandles.some(dh => contains(dh, el, true));
}
get isHandleSelectorValid() {
return isPresent(this.dragHandle) && this.dragHandle !== '';
}
setTargetStyles() {
if (!isDocumentAvailable()) {
return;
}
if (isPresent(this.dragHandle) && this.dragHandle !== '') {
if (isPresent(this.dragHandles) && this.dragHandles.length > 0) {
this.dragHandles.forEach(handle => {
this.renderer.setStyle(handle, 'cursor', this.cursorStyle);
this.renderer.setStyle(handle, 'touch-action', 'none');
});
}
}
else {
this.allDragTargets.forEach(target => {
this.renderer.setStyle(target, 'cursor', this.cursorStyle);
this.renderer.setStyle(target, 'touch-action', 'none');
});
}
}
queryHost(selector) {
if (isPresent(selector) && selector !== "") {
return Array.from(this.nativeElement.querySelectorAll(selector));
}
}
clearPreviousTargets() {
this.previousDragTargets.forEach(dragTarget => {
const idx = this.service.dragTargets.findIndex(serviceDragTarget => serviceDragTarget.element === dragTarget);
if (idx > -1) {
this.service.dragTargets.splice(idx, 1);
}
});
this.previousDragTargets = [];
}
performDrag() {
const elem = this.hint ? this.hintElem : this.currentDragTargetElement;
if (elem) {
const styles = this.getStylesPerElement(elem);
setElementStyles(this.renderer, elem, styles);
}
}
calculatePosition(element, event) {
let position = null;
if (!isDocumentAvailable()) {
return { x: 0, y: 0 };
}
if (element === this.hintElem) {
position = { x: event.clientX + window.scrollX, y: event.clientY + window.scrollY };
}
else {
position = { x: event.clientX - this.initialPosition.x + event.scrollX, y: event.clientY - this.initialPosition.y + event.scrollY };
}
return position;
}
getStylesPerElement(element) {
if (element === this.hintElem) {
return {
top: `${this.position.y}px`,
left: `${this.position.x}px`,
transition: 'none',
position: 'absolute',
zIndex: 1999
};
}
else {
const transform = `translate(${this.position.x}px, ${this.position.y}px)`;
return {
transform: transform,
transition: 'none'
};
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DragTargetContainerDirective, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: i0.Renderer2 }, { token: DragStateService }, { token: i0.ViewContainerRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: DragTargetContainerDirective, isStandalone: true, selector: "[kendoDragTargetContainer]", inputs: { hint: "hint", dragTargetFilter: "dragTargetFilter", dragHandle: "dragHandle", dragDelay: "dragDelay", threshold: "threshold", dragTargetId: "dragTargetId", dragData: "dragData", dragDisabled: "dragDisabled", mode: "mode", cursorStyle: "cursorStyle", hintContext: "hintContext" }, outputs: { onDragReady: "onDragReady", onPress: "onPress", onDragStart: "onDragStart", onDrag: "onDrag", onRelease: "onRelease", onDragEnd: "onDragEnd" }, exportAs: ["kendoDragTargetContainer"], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DragTargetContainerDirective, decorators: [{
type: Directive,
args: [{
selector: '[kendoDragTargetContainer]',
exportAs: 'kendoDragTargetContainer',
standalone: true
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: i0.Renderer2 }, { type: DragStateService }, { type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { hint: [{
type: Input
}], dragTargetFilter: [{
type: Input
}], dragHandle: [{
type: Input
}], dragDelay: [{
type: Input
}], threshold: [{
type: Input
}], dragTargetId: [{
type: Input
}], dragData: [{
type: Input
}], dragDisabled: [{
type: Input
}], mode: [{
type: Input
}], cursorStyle: [{
type: Input
}], hintContext: [{
type: Input
}], onDragReady: [{
type: Output
}], onPress: [{
type: Output
}], onDragStart: [{
type: Output
}], onDrag: [{
type: Output
}], onRelease: [{
type: Output
}], onDragEnd: [{
type: Output
}] } });
let isDragStartPrevented = false;
let isDragPrevented = false;
/**
* Represents the Kendo UI DragTarget directive for Angular.
*/
class DragTargetDirective {
element;
renderer;
ngZone;
service;
viewContainer;
get touchActionStyle() {
return this.dragHandles.length > 0 ? null : 'none';
}
/**
* Defines whether a hint will be used for dragging. By default, the hint is a copy of the drag target. ([see example]({% slug drag_hint %})).
*
* @default false
*/
hint = false;
/**
* The number of pixels the pointer moves in any direction before the dragging starts ([see example]({% slug minimum_distance %})). Applicable when `manualDrag` is set to `false`.
*
* @default 0
*/
threshold = 0;
/**
* Defines the automatic container scrolling behavior when close to the edge ([see example]({% slug auto_scroll %})).
*
* @default true
*/
autoScroll = true;
/**
* Defines a unique identifier for the dragTarget.
*/
dragTargetId;
/**
* Defines the delay in milliseconds after which the drag will begin ([see example]({% slug drag_delay %})).
*
* @default 0
*/
dragDelay = 0;
/**
* Restricts the element to be dragged horizontally or vertically only ([see example]({% slug axis_lock %})). Applicable when `mode` is set to `auto`.
*/
restrictByAxis;
/**
* Specifies whether the default dragging behavior will be performed or the developer will manually handle the drag action.
*
* @default 'auto'
*/
mode = 'auto';
/**
* Defines a callback function used for attaching custom data to the dragTarget.
* The data will be available in the events of the respective [`DropTarget`]({% slug api_utils_droptargetdirective %}) or [`DropTargetContainer`]({% slug api_utils_droptargetcontainerdirective %}) directives.
* The current DragTarget HTML element and its `dragTargetId` will be available as arguments.
*/
set dragData(fn) {
if (isDevMode && typeof fn !== 'function') {
throw new Error(`dragData must be a function, but received ${JSON.stringify(fn)}.`);
}
this._dragData = fn;
}
get dragData() {
return this._dragData;
}
/**
* Specifies the cursor style of the drag target. Accepts same values as the [CSS `cursor` property](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#values).
*
* @default 'move'
*/
cursorStyle = 'move';
/**
* Fires when the user presses the DragTarget element.
*/
onPress = new EventEmitter();
/**
* Fires when the dragging of the DragTarget element begins.
*/
onDragStart = new EventEmitter();
/**
* Fires while the user drags the DragTarget element.
*/
onDrag = new EventEmitter();
/**
* Fires when the DragTarget's `dragDelay` has passed and the user is able to drag the element.
*/
onDragReady = new EventEmitter();
/**
* Fires when the user releases the DragTarget element after being pressed.
*/
onRelease = new EventEmitter();
/**
* Fires when the dragging of the DragTarget ends and the element is released.
*/
onDragEnd = new EventEmitter();
drag