ngx-explorer-dnd
Version:
## _Drag & Drop in Angular like in a desktop explorer!_
1,111 lines (1,101 loc) • 50.5 kB
JavaScript
import { DOCUMENT } from '@angular/common';
import * as i0 from '@angular/core';
import { Injectable, InjectionToken, EventEmitter, Directive, Inject, Output, Input, Optional, SkipSelf, HostListener, NgModule } from '@angular/core';
import { Subject, Subscription } from 'rxjs';
/**
* Set styles to a HTMLElement.
* @param dest HTMLElements style property.
* @param source The styles as JS object.
* @param importantProperties Name of important style properties. This properties will be set to `!important`.
*/
function extendStyles(dest, source, importantProperties) {
for (let key in source) {
if (source.hasOwnProperty(key)) {
const value = source[key];
if (value) {
dest.setProperty(key, value, (importantProperties === null || importantProperties === void 0 ? void 0 : importantProperties.has(key)) ? 'important' : '');
}
else {
dest.removeProperty(key);
}
}
}
return dest;
}
// With help from cdkDrag: https://github.com/angular/components/blob/72547a41d4230cea0c6a5448e85bd60cfc26bd35/src/cdk/drag-drop/drag-utils.ts
/** Clamps a number between zero and a maximum. */
function clamp(value, max) {
return Math.max(0, Math.min(max, value));
}
var DragRowPosition;
(function (DragRowPosition) {
DragRowPosition[DragRowPosition["SMALLER"] = 0] = "SMALLER";
DragRowPosition[DragRowPosition["GREATER"] = 1] = "GREATER";
DragRowPosition[DragRowPosition["SAME"] = 2] = "SAME";
DragRowPosition[DragRowPosition["UNDEFINED"] = 3] = "UNDEFINED";
})(DragRowPosition || (DragRowPosition = {}));
/**
* Moves an item one index in an array to another.
* @param array Array in which to move the item.
* @param fromIndex Starting index of the item.
* @param toIndex Index to which the item should be moved.
*/
function moveItemInArray(array, fromIndex, toIndex) {
const from = clamp(fromIndex, array.length - 1);
const to = clamp(toIndex, array.length - 1);
console.log(from, to);
if (from === to) {
return;
}
const target = array[from];
const delta = to < from ? -1 : 1;
for (let i = from; i !== to; i += delta) {
array[i] = array[i + delta];
}
array[to] = target;
}
class DndService {
constructor() {
this.dragElementSubject = new Subject();
}
getDragElementSubject() {
return this.dragElementSubject.asObservable();
}
setCurrentDragElement(element) {
this.dragElementSubject.next(element);
}
}
DndService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: DndService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
DndService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: DndService, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: DndService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: function () { return []; } });
const EXPLORER_DND_CONTAINER = new InjectionToken('EXPLORER_DND_CONTAINER');
class NgxExplorerContainerDirective {
//#endregion
constructor(_ngZone, _document, dndService) {
this._ngZone = _ngZone;
this._document = _document;
this.dndService = dndService;
/** The registered elements as array from type `NgxExplorerItemDirective`. */
this._dragElements = [];
/** If drag startet. */
this.dragStarted = false;
/** Check if the current element can be moved with the intern `isMinRangeMoved` method. */
this.canMove = false;
/** A unsorted list of all registered components */
this.unsortedItems = [];
/** A sorted list of all registered components (sorted by position in the DOM) */
this.sortedItems = [];
/** Event emitted when the drag progress was started. */
this.dragInProgress = new EventEmitter();
/** Event emitted when the files are dropped with `mouseup`. */
this.drop = new EventEmitter();
/** Event emitted when a target under the mouse will change */
this.targetChange = new EventEmitter();
/** Cancel move back animation after `mouseup` event */
this.cancelAnimation = false;
/** Can the file/folder components are sorted */
this.sortingEnabled = false;
this.onMouseMove = (event) => {
var _a, _b;
if (this.dragStarted) {
if (this.isMinRangeMoved(event.x, event.y)) {
this.canMove = true;
const htmlElement = (_a = this._currentDragElement) === null || _a === void 0 ? void 0 : _a.htmlElement;
if (htmlElement)
this.toggleVisibility(htmlElement, true);
}
if (!this.canMove)
return;
if (this.sortingEnabled) {
this.setCurrentPositionOfDraggedElement((_b = this._currentDragElement) === null || _b === void 0 ? void 0 : _b.htmlElement, { x: event.x, y: event.y });
}
this.setPreviewElementPosition(event.x, event.y);
}
event.stopPropagation();
event.preventDefault();
};
this.onMouseUp = (event) => {
var _a;
this.dragStarted = false;
this.canMove = false;
let indexData;
if (this.previewElement) {
if (this._currentDragElement && this.sortingEnabled) {
indexData = this.getCurrentIndexPosition(event.x, event.y, (_a = this._currentDragElement) === null || _a === void 0 ? void 0 : _a.htmlElement);
this.positionDifference = { left: indexData === null || indexData === void 0 ? void 0 : indexData.x, top: indexData === null || indexData === void 0 ? void 0 : indexData.y };
}
this.previewElement.style.transition = '0.2s ease-in-out';
let posX = (+this.previewElement.style.left.replace('px', '').trim() -
(this.positionDifference
? this.positionDifference.left
: this.position.left)) *
-1;
let posY = (+this.previewElement.style.top.replace('px', '').trim() -
(this.positionDifference
? this.positionDifference.top
: this.position.top)) *
-1;
this.previewElement.style.transform =
'translate(' + posX + 'px, ' + posY + 'px)';
event.preventDefault();
event.stopPropagation();
setTimeout(() => {
var _a, _b, _c, _d;
this.drop.emit({
item: (_a = this._currentDragElement) === null || _a === void 0 ? void 0 : _a.data,
target: (_b = this._currentTargetElement) === null || _b === void 0 ? void 0 : _b.data,
optionalDragData: this.dragData,
oldIndex: indexData ? indexData.oldIndex : null,
newIndex: indexData ? indexData.newIndex : null,
});
// Clear positionDifference
this.positionDifference = null;
if (this.previewElement)
this._document.body.removeChild(this.previewElement);
this.previewElement = null;
const htmlElement = (_c = this._currentDragElement) === null || _c === void 0 ? void 0 : _c.htmlElement;
if (htmlElement) {
this.toggleVisibility(htmlElement, false);
}
this.resetPositions((_d = this._currentDragElement) === null || _d === void 0 ? void 0 : _d.htmlElement);
this.dndService.setCurrentDragElement(null);
this._currentDragElement = null;
this._currentTargetElement = null;
}, this.cancelAnimation ? 0 : 201);
this.dragInProgress.emit(false);
}
};
}
//#region Helper
/** Deep clone the current element for a preview element. */
deepCloneNode(node, badge) {
const clone = node.cloneNode(true);
if (badge) {
const badgeElement = document.createElement('div');
badgeElement.innerHTML = badge;
extendStyles(badgeElement.style, {
margin: '0',
position: 'absolute',
top: '0',
right: '0',
'text-align': 'center',
'border-style': 'solid',
'border-radius': '50%',
'border-width': '0',
'background-color': 'red',
width: '20px',
height: '20px',
color: 'white',
});
clone.appendChild(badgeElement);
}
const descendantsWithId = clone.querySelectorAll('[id]');
// Remove the `id` to avoid having multiple elements with the same id on the page.
clone.removeAttribute('id');
for (let i = 0; i < descendantsWithId.length; i++) {
descendantsWithId[i].removeAttribute('id');
}
return clone;
}
/** Hide/show the dragged component */
toggleVisibility(element, visible) {
extendStyles(element.style, {
opacity: visible ? '0' : '',
// position: visible ? 'fixed' : '',
// left: visible ? '0' : '',
// top: visible ? '-500em' : '',
});
}
/** Creates a list with all registered components and their positions */
createUnsortedList() {
const unsortedItems = [];
// All registered components
for (let data of this._dragElements) {
const clientRect = data.htmlElement.getBoundingClientRect();
const posData = {
x: clientRect.x,
y: clientRect.y,
width: clientRect.width,
height: clientRect.height,
};
unsortedItems.push({ htmlElement: data.htmlElement, posData });
}
return unsortedItems;
}
/** Gets the registered items in the list, sorted by their position in the DOM. */
getSortedItems() {
return Array.from(this.createUnsortedList()).sort((a, b) => {
const documentPosition = a.htmlElement.compareDocumentPosition(b.htmlElement);
// `compareDocumentPosition` returns a bitmask so we have to use a bitwise operator.
// https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition
// tslint:disable-next-line:no-bitwise
return documentPosition & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
});
}
/** Check if the element has a translate3d style and returns the "real" values of x, y, width and height. */
getCurrentPositionInclusiveTranslate3d(element) {
const rect = element.getBoundingClientRect();
let translateX = 0, translateY = 0;
if (element.style.transform) {
translateX =
+element.style.transform
.replace(/translate3d|px|\(|\)/gi, '')
.split(',')[0] || 0;
translateY =
+element.style.transform
.replace(/translate3d|px|\(|\)/gi, '')
.split(',')[1] || 0;
}
return {
startX: rect.x - translateX,
startY: rect.y - translateY,
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
halfWidthX: rect.x + rect.width / 2,
halfHeightY: rect.y + rect.height / 2,
};
}
/** Checks goes the new position of the element outside of the parent and set new row position */
needToMoveUpOrDown(parentRect, indexOfElement, addDirection) {
let transX = '0px', transY = '0px';
let posChanged = false;
const currentElement = this.sortedItems[indexOfElement];
if (currentElement.posData.x + addDirection < parentRect.x &&
indexOfElement > 0) {
transX =
this.sortedItems[indexOfElement - 1].posData.x -
this.sortedItems[indexOfElement].posData.x +
'px';
transY =
this.sortedItems[indexOfElement - 1].posData.y -
this.sortedItems[indexOfElement].posData.y +
'px';
posChanged = true;
}
else if (currentElement.posData.x + currentElement.posData.width + addDirection >
parentRect.x + parentRect.width) {
transX =
this.sortedItems[indexOfElement + 1].posData.x -
this.sortedItems[indexOfElement].posData.x +
'px';
transY =
this.sortedItems[indexOfElement + 1].posData.y -
this.sortedItems[indexOfElement].posData.y +
'px';
posChanged = true;
}
return posChanged ? { transX, transY } : false;
}
/** Set the position of the dragged element */
setCurrentPositionOfDraggedElement(draggedElement, mousePosition) {
var _a;
if (!draggedElement)
return;
const parentRect = (_a = draggedElement.parentElement) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
if (!parentRect)
return;
// Intern helpers ------------------------------------------
/** If the cursor moving in same row like the dragged element */
const isInSameLine = () => {
const el = draggedElement.getBoundingClientRect();
if (mousePosition.y >= el.y && mousePosition.y <= el.y + el.height)
return {
sameRow: true,
dragRowPosition: DragRowPosition.SAME,
sameLineAsMouse: DragRowPosition.UNDEFINED,
};
else if (mousePosition.y < el.y)
return {
sameRow: false,
dragRowPosition: DragRowPosition.SMALLER,
sameLineAsMouse: DragRowPosition.UNDEFINED,
};
else
return {
sameRow: false,
dragRowPosition: DragRowPosition.GREATER,
sameLineAsMouse: DragRowPosition.UNDEFINED,
};
};
/** If the element is in the same line as the dragged element */
const amIAtTheSameRow = (y, height) => {
const el = draggedElement.getBoundingClientRect();
let mouseRowPosition = DragRowPosition.UNDEFINED;
if (height) {
if (mousePosition.y > y + height)
mouseRowPosition = DragRowPosition.SMALLER;
else if (mousePosition.y < y)
mouseRowPosition = DragRowPosition.GREATER;
else if (mousePosition.y >= y && mousePosition.y <= y + height)
mouseRowPosition = DragRowPosition.SAME;
}
if (y === el.y) {
return {
sameRow: true,
dragRowPosition: DragRowPosition.SAME,
sameLineAsMouse: mouseRowPosition,
};
}
else if (y > el.y) {
return {
sameRow: false,
dragRowPosition: DragRowPosition.GREATER,
sameLineAsMouse: mouseRowPosition,
};
}
else {
return {
sameRow: false,
dragRowPosition: DragRowPosition.SMALLER,
sameLineAsMouse: mouseRowPosition,
};
}
};
// Mouse is right to new elmement
const isRightToElement = (halfWidth) => {
if (mousePosition.x > halfWidth)
return true;
return false;
};
// Mouse is left to new element
const isLeftToElement = (halfWidth) => {
if (mousePosition.x < halfWidth)
return true;
return false;
};
/** If the start position of the current element before `true`or after `false` the dragged element */
const posIsBeforeDraggedElement = (x, y) => {
const el = draggedElement.getBoundingClientRect();
if (y) {
if (y < el.y)
return true;
if (y > el.y)
return false;
if (x < el.x && y === el.y)
return true;
return false;
}
if (x < el.x)
return true;
return false;
};
// ---------------------------------------------------------
// Only if the mouse is inside the parent
if (mousePosition.x >= parentRect.x &&
mousePosition.x <= parentRect.x + (parentRect === null || parentRect === void 0 ? void 0 : parentRect.width) &&
mousePosition.y >= parentRect.y &&
mousePosition.y <= parentRect.y + (parentRect === null || parentRect === void 0 ? void 0 : parentRect.height)) {
const draggedRect = draggedElement.getBoundingClientRect();
this.positionDifference = Object.assign({}, this.position);
let index = 0; // Index of the current unsorted item
for (let element of this.sortedItems) {
if (element.htmlElement !== draggedElement) {
const rect = this.getCurrentPositionInclusiveTranslate3d(element.htmlElement);
// if (
// element.htmlElement.getAttribute('ng-reflect-file-name') &&
// element.htmlElement.getAttribute('ng-reflect-file-name') ===
// 'File 6'
// ) {
//console.log(rect.x, rect.startX, rect.startY);
// console.log(
// mousePosition.x,
// rect.halfWidthX,
// rect.startX,
// mousePosition.y,
// rect.startY,
// rect.startY + rect.height
// );
// }
let translateXValue = '0px', translateYValue = '0px';
// Part: in same line
// Are the current elements all in the same line as the dragged element, handle it here
if (isInSameLine().sameRow &&
amIAtTheSameRow(rect.y).sameRow &&
isLeftToElement(rect.halfWidthX) &&
posIsBeforeDraggedElement(rect.startX)) {
translateXValue = draggedRect.width + 'px';
}
else if (isInSameLine().sameRow &&
amIAtTheSameRow(rect.y).sameRow &&
isRightToElement(rect.halfWidthX) &&
!posIsBeforeDraggedElement(rect.startX)) {
translateXValue = -draggedRect.width + 'px';
}
// Part: in other line
// Are the current elements are greater or smaller than the dragged element, handle it here
if (!isInSameLine().sameRow) {
// Check all elements their position are smaller then the current
if (isInSameLine().dragRowPosition == DragRowPosition.GREATER) {
// Mouse goes under the dragged element; all elements they are greater as dragged element, but smaller/same as mouse will be handled here
if ((amIAtTheSameRow(rect.y).dragRowPosition ===
DragRowPosition.GREATER ||
amIAtTheSameRow(rect.y).dragRowPosition ===
DragRowPosition.SAME) &&
(amIAtTheSameRow(rect.y, rect.height).sameLineAsMouse ===
DragRowPosition.SAME ||
amIAtTheSameRow(rect.y, rect.height).sameLineAsMouse ===
DragRowPosition.SMALLER) &&
!posIsBeforeDraggedElement(rect.startX, rect.startY)) {
if (isRightToElement(rect.halfWidthX) ||
amIAtTheSameRow(rect.y).dragRowPosition ==
DragRowPosition.SAME ||
(amIAtTheSameRow(rect.y).dragRowPosition ==
DragRowPosition.GREATER &&
amIAtTheSameRow(rect.y, rect.height).sameLineAsMouse !==
DragRowPosition.SAME)) {
translateXValue = -draggedRect.width + 'px';
}
}
}
else if (isInSameLine().dragRowPosition == DragRowPosition.SMALLER) {
if ((amIAtTheSameRow(rect.y).dragRowPosition ===
DragRowPosition.SMALLER ||
amIAtTheSameRow(rect.y).dragRowPosition ===
DragRowPosition.SAME) &&
(amIAtTheSameRow(rect.y, rect.height).sameLineAsMouse ===
DragRowPosition.SAME ||
amIAtTheSameRow(rect.y, rect.height).sameLineAsMouse ===
DragRowPosition.GREATER) &&
posIsBeforeDraggedElement(rect.startX, rect.startY)) {
if (isLeftToElement(rect.halfWidthX) ||
amIAtTheSameRow(rect.y).dragRowPosition ==
DragRowPosition.SAME ||
(amIAtTheSameRow(rect.y).dragRowPosition ==
DragRowPosition.SMALLER &&
amIAtTheSameRow(rect.y, rect.height).sameLineAsMouse !==
DragRowPosition.SAME)) {
translateXValue = draggedRect.width + 'px';
}
}
}
}
// Check goes any element outside the parent. So change it's row position
const checkNeedToMoveUpOrDown = this.needToMoveUpOrDown(parentRect, index, +translateXValue.replace('px', ''));
if (checkNeedToMoveUpOrDown) {
translateXValue = (checkNeedToMoveUpOrDown).transX;
translateYValue = (checkNeedToMoveUpOrDown).transY;
}
extendStyles(element.htmlElement.style, {
transform: `translate3d(${translateXValue},${translateYValue},0)`,
});
}
index++;
}
}
}
/** Removes the transform3d positions from all elements */
resetPositions(draggedElement) {
if (!draggedElement)
return;
for (let element of this.unsortedItems) {
if (element.htmlElement !== draggedElement) {
extendStyles(element.htmlElement.style, {
transform: '',
});
}
}
}
/** Set the position of the preview element */
setPreviewElementPosition(posX, posY) {
if (this.previewElement) {
if (this.previewElement.style.display === 'none') {
this.previewElement.style.display = 'unset';
}
this.previewElement.style.left =
posX - this.previewElement.clientWidth / 2 + 'px';
this.previewElement.style.top =
posY - this.previewElement.clientHeight / 2 + 'px';
}
}
/** Dragging start if the pressed mouse moved the min range. */
isMinRangeMoved(posX, posY) {
// Standard is 8px
if (this.previewElement) {
if (this.startPosition.left - 7 > posX ||
this.startPosition.left + 7 < posX ||
this.startPosition.top - 7 > posY ||
this.startPosition.top + 7 < posY) {
return true;
}
}
return false;
}
/** Check if element is inside rect. */
checkIfElementInsideRect(rect, selectionBorder) {
if (((selectionBorder.x >= rect.x &&
selectionBorder.x <= rect.x + rect.width) ||
(selectionBorder.x <= rect.x &&
selectionBorder.x + selectionBorder.width >= rect.x)) &&
((selectionBorder.y >= rect.y &&
selectionBorder.y <= rect.y + rect.height) ||
(selectionBorder.y <= rect.y &&
selectionBorder.y + selectionBorder.height >= rect.y))) {
return true;
}
else {
return false;
}
}
/** Get all elements of type `NgxExplorerItemDirective` from an event. */
getTargetHandle(event) {
return this._dragElements.find((handle) => {
return (event.target &&
(event.target === handle.htmlElement ||
handle.htmlElement.contains(event.target)));
});
}
/** Get all elements of type `NgxExplorerItemDirective` from a HTMLElement. */
getTargetHandleFromHTMLElement(element) {
var _a;
return (_a = this._dragElements.find((handle) => {
return (element === handle.htmlElement || element.contains(element));
})) === null || _a === void 0 ? void 0 : _a.htmlElement;
}
/** Get the current index of the dragged element in the unsorted list */
getCurrentIndexPosition(lastMouseX, lastMouseY, draggedElement) {
var _a;
const parentRect = (_a = draggedElement.parentElement) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
let currentPosition = undefined;
if (parentRect) {
let newIndex = 0, oldIndex = 0;
for (let elIndex = 0; elIndex < this.sortedItems.length; elIndex++) {
if (this.sortedItems[elIndex].htmlElement === draggedElement) {
oldIndex = elIndex;
break;
}
}
for (let el of this.sortedItems) {
if (el.htmlElement !== draggedElement) {
// Mouse has position of Object
if (lastMouseX >= el.posData.x &&
lastMouseX <= el.posData.x + el.posData.width &&
lastMouseY >= el.posData.y &&
lastMouseY <= el.posData.y + el.posData.height) {
currentPosition = {
x: el.posData.x,
y: el.posData.y,
oldIndex,
newIndex,
};
break;
}
}
newIndex++;
}
}
return currentPosition;
}
/** Returns a list of all elements of type `NgxExplorerItemDirective` they are inside a specific rect. */
getElementsInsideSelectionDiv(x, y, width, height) {
const result = [];
this._dragElements.forEach((data) => {
const rect = data.htmlElement.getBoundingClientRect();
if (this.checkIfElementInsideRect(rect, {
x,
y,
width,
height,
})) {
result.push(data);
}
});
return result;
}
/** Initialize all event listener. */
initAll() {
// Use arrow functions to use "this" in onMouseDown (as example) or use .....bind(this)
// We can use @HostListener instead.
this._document.addEventListener('mousedown', this.onMouseDown.bind(this));
this._document.addEventListener('mousemove', this.onMouseMove);
this._document.addEventListener('mouseup', this.onMouseUp);
return this;
}
/** Remove all event listener. */
clearAll() {
this._document.removeEventListener('mousedown', this.onMouseDown);
this._document.removeEventListener('mousemove', this.onMouseMove);
this._document.removeEventListener('mouseup', this.onMouseUp);
}
ngOnInit() {
this.initAll();
}
ngOnDestroy() {
this.clearAll();
}
onMouseDown(event) {
var _a;
if (event.button !== 0) {
event.preventDefault();
return;
}
// List of the current registered elements
if (this.sortingEnabled) {
this.unsortedItems = this.createUnsortedList();
this.sortedItems = this.getSortedItems();
}
this.dragStarted = true;
this._currentDragElement = this.getTargetHandle(event) || null;
const htmlElement = (_a = this._currentDragElement) === null || _a === void 0 ? void 0 : _a.htmlElement;
if (htmlElement) {
this.previewElement = this.deepCloneNode(htmlElement, this.badge);
this.position = {
left: htmlElement.getBoundingClientRect().left,
top: htmlElement.getBoundingClientRect().top,
};
this.startPosition = {
left: event.x,
top: event.y,
};
extendStyles(this.previewElement.style, {
'pointer-events': 'none',
margin: '0',
position: 'fixed',
top: '0',
left: '0',
'z-index': '1000',
display: 'none',
}, new Set(['position']));
this._document.body.appendChild(this.previewElement);
this.dndService.setCurrentDragElement(htmlElement);
event.stopPropagation();
event.preventDefault();
this.dragInProgress.emit(true);
}
}
//#region Public Voids
/** `ngx-explorer-item` will call this method to register itself. */
addDragElement(element) {
this._dragElements.push(element);
}
/** `ngx-explorer-target` will call this method to register itself as target on mouseenter */
setCurrentTarget(element) {
this._currentTargetElement = element;
this.targetChange.emit({ target: element === null || element === void 0 ? void 0 : element.getHostComponent() });
}
}
NgxExplorerContainerDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: NgxExplorerContainerDirective, deps: [{ token: i0.NgZone }, { token: DOCUMENT }, { token: DndService }], target: i0.ɵɵFactoryTarget.Directive });
NgxExplorerContainerDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: NgxExplorerContainerDirective, selector: "[ngxExplorerDndContainer]", inputs: { dragData: "dragData", badge: "badge", cancelAnimation: "cancelAnimation", sortingEnabled: "sortingEnabled" }, outputs: { dragInProgress: "dragInProgress", drop: "drop", targetChange: "targetChange" }, providers: [
{
provide: EXPLORER_DND_CONTAINER,
useExisting: NgxExplorerContainerDirective,
},
], ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: NgxExplorerContainerDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngxExplorerDndContainer]',
providers: [
{
provide: EXPLORER_DND_CONTAINER,
useExisting: NgxExplorerContainerDirective,
},
],
}]
}], ctorParameters: function () {
return [{ type: i0.NgZone }, { type: Document, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }, { type: DndService }];
}, propDecorators: { dragInProgress: [{
type: Output
}], drop: [{
type: Output
}], targetChange: [{
type: Output
}], dragData: [{
type: Input
}], badge: [{
type: Input
}], cancelAnimation: [{
type: Input
}], sortingEnabled: [{
type: Input
}] } });
/** Enum of the type of the FileFolder class. */
var FileFolderType;
(function (FileFolderType) {
FileFolderType[FileFolderType["File"] = 0] = "File";
FileFolderType[FileFolderType["Folder"] = 1] = "Folder";
})(FileFolderType || (FileFolderType = {}));
/** This class can be extended to a component. */
class FileFolder {
constructor() {
this.selected = false;
this.type = FileFolderType.File;
}
}
class NgxExplorerTargetDirective {
constructor(element, renderer, dndService, _parentDrag, host) {
this.element = element;
this.renderer = renderer;
this.dndService = dndService;
this._parentDrag = _parentDrag;
this.host = host;
this.dragElementSubscription = Subscription.EMPTY;
}
ngOnInit() {
if (this._parentDrag) {
this.dragElementSubscription = this.dndService
.getDragElementSubject()
.subscribe((element) => {
this.currentDragElement = element;
});
}
}
ngOnDestroy() {
this.dragElementSubscription.unsubscribe();
}
onMouseEnter(ev) {
var _a, _b;
if (!this.currentDragElement) {
ev.preventDefault();
return;
}
const htmlElement = (_a = this._parentDrag) === null || _a === void 0 ? void 0 : _a.getTargetHandleFromHTMLElement(this.currentDragElement);
if (htmlElement) {
this.renderer.addClass(this.element.nativeElement, 'dnd-target-highlight');
}
(_b = this._parentDrag) === null || _b === void 0 ? void 0 : _b.setCurrentTarget(this);
}
onMouseLeave(ev) {
var _a;
this.renderer.removeClass(this.element.nativeElement, 'dnd-target-highlight');
(_a = this._parentDrag) === null || _a === void 0 ? void 0 : _a.setCurrentTarget(null);
}
/** Return the host component. */
getHostComponent() {
return this.host;
}
}
NgxExplorerTargetDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: NgxExplorerTargetDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: DndService }, { token: EXPLORER_DND_CONTAINER, optional: true, skipSelf: true }, { token: FileFolder, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
NgxExplorerTargetDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: NgxExplorerTargetDirective, selector: "[ngxExplorerDndTarget]", inputs: { data: ["dndTargetData", "data"] }, host: { listeners: { "mouseenter": "onMouseEnter($event)", "mouseleave": "onMouseLeave($event)" } }, providers: [
{
provide: EXPLORER_DND_CONTAINER,
useExisting: NgxExplorerTargetDirective,
},
], ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: NgxExplorerTargetDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngxExplorerDndTarget]',
providers: [
{
provide: EXPLORER_DND_CONTAINER,
useExisting: NgxExplorerTargetDirective,
},
],
}]
}], ctorParameters: function () {
return [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: DndService }, { type: NgxExplorerContainerDirective, decorators: [{
type: Optional
}, {
type: SkipSelf
}, {
type: Inject,
args: [EXPLORER_DND_CONTAINER]
}] }, { type: FileFolder, decorators: [{
type: Optional
}] }];
}, propDecorators: { data: [{
type: Input,
args: ['dndTargetData']
}], onMouseEnter: [{
type: HostListener,
args: ['mouseenter', ['$event']]
}], onMouseLeave: [{
type: HostListener,
args: ['mouseleave', ['$event']]
}] } });
class NgxExplorerItemDirective {
constructor(elementRef, _parentDrag, host) {
this.elementRef = elementRef;
this._parentDrag = _parentDrag;
this.host = host;
this.htmlElement = this.elementRef.nativeElement;
if (this._parentDrag) {
this._parentDrag.addDragElement(this);
}
}
/** Return the host component. */
getHostComponent() {
return this.host;
}
}
NgxExplorerItemDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: NgxExplorerItemDirective, deps: [{ token: i0.ElementRef }, { token: EXPLORER_DND_CONTAINER, optional: true, skipSelf: true }, { token: FileFolder, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
NgxExplorerItemDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: NgxExplorerItemDirective, selector: "[ngxExplorerDndElement]", inputs: { data: ["dndElementData", "data"] }, providers: [
{
provide: EXPLORER_DND_CONTAINER,
useExisting: NgxExplorerItemDirective,
},
], ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: NgxExplorerItemDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngxExplorerDndElement]',
providers: [
{
provide: EXPLORER_DND_CONTAINER,
useExisting: NgxExplorerItemDirective,
},
],
}]
}], ctorParameters: function () {
return [{ type: i0.ElementRef }, { type: NgxExplorerContainerDirective, decorators: [{
type: Optional
}, {
type: SkipSelf
}, {
type: Inject,
args: [EXPLORER_DND_CONTAINER]
}] }, { type: FileFolder, decorators: [{
type: Optional
}] }];
}, propDecorators: { data: [{
type: Input,
args: ['dndElementData']
}] } });
class NgxDragSelectionDirective {
//#endregion
constructor(_document, _parentDrag) {
this._document = _document;
this._parentDrag = _parentDrag;
/** The current x position of the selection div. */
this.startX = 0;
/** The current y position of the selection div. */
this.startY = 0;
/**
* The initial x position of the selection div.
* Will be used to handle negative x position.
*/
this.initialX = 0;
/**
* The initial y position of the selection div.
* Will be used to handle negative y position.
*/
this.initialY = 0;
/**
* Will be used to set current selected elements to check has
* the current selected elements changed to before.
*/
this.selectedElements = new Set();
/** The current x position from the fired event. */
this.currentX = 0;
/** the current y position from the fired event. */
this.currentY = 0;
/** The current calculated width of the selection div. */
this.currentWidth = 0;
/** The current calculated height of the selection div. */
this.currentHeight = 0;
/** Will be true if selection in progress. */
this.selectingInProgress = false;
/** Sets whether ngx-drag-selection can be used. */
this.selectionAllowed = true;
/** Event emitted when the selected elements are changed. */
this.selectedElementsChange = new EventEmitter();
}
//#region Helper
/** Checks if a registered element of type `ngx-explorer-item` inside the selection div rect. */
checkIfElementInsideRect(rect, selectionBorder) {
if (((selectionBorder.x >= rect.x &&
selectionBorder.x <= rect.x + rect.width) ||
(selectionBorder.x <= rect.x &&
selectionBorder.x + selectionBorder.width >= rect.x)) &&
((selectionBorder.y >= rect.y &&
selectionBorder.y <= rect.y + rect.height) ||
(selectionBorder.y <= rect.y &&
selectionBorder.y + selectionBorder.height >= rect.y))) {
return true;
}
else {
return false;
}
}
/** Checks whether the selected elements have changed. */
checkIfSelectedElementsChanged(items) {
let changed = false;
for (let item of items) {
if (!this.selectedElements.has(item)) {
// data has changed...
changed = true;
}
}
if (!changed) {
if (this.selectedElements.size !== items.length)
changed = true;
}
if (changed)
// Changed? So "reset" selectedElements
this.selectedElements = new Set(items);
return changed;
}
/** Creates the selection div into document.body. */
createSelectionDiv() {
if (this.selectionDivElement) {
this.selectionDiv = this.selectionDivElement;
extendStyles(this.selectionDiv.style, {
display: 'none',
position: 'fixed',
top: '0',
left: '0',
width: '0px',
height: '0px',
});
}
else {
this.selectionDiv = document.createElement('div');
extendStyles(this.selectionDiv.style, {
display: 'none',
position: 'fixed',
opacity: '0.5',
top: '0',
left: '0',
'border-style': 'solid',
'border-radius': '6px',
'border-width': '0',
'border-color': '#7a7afa',
'background-color': '#add8e6',
width: '0px',
height: '0px',
});
}
this._document.body.appendChild(this.selectionDiv);
}
/** On `mousemove` this will set the current position and width of the selection div. */
setSelectionDivPosition(mouseX, mouseY) {
this.currentX = mouseX;
this.currentY = mouseY;
this.currentWidth = this.currentX - this.startX;
this.currentHeight = this.currentY - this.startY;
if (this.initialX >= this.currentX) {
this.currentWidth = this.initialX - this.currentX;
this.startX = this.currentX;
}
if (this.initialY >= this.currentY) {
this.currentHeight = this.initialY - this.currentY;
this.startY = this.currentY;
}
// div is not visible on start
if (this.selectionDiv.style.display === 'none') {
this.selectionDiv.style.display = 'unset';
}
extendStyles(this.selectionDiv.style, {
left: this.startX + 'px',
top: this.startY + 'px',
width: this.currentWidth + 'px',
height: this.currentHeight + 'px',
});
}
//#region HostListener
onMouseDown(ev) {
if (!this.selectionAllowed) {
ev.preventDefault();
return;
}
if (!this.selectingInProgress) {
this.startX = ev.x;
this.startY = ev.y;
this.initialX = this.startX;
this.initialY = this.startY;
this.selectingInProgress = true;
this.createSelectionDiv();
this.setSelectionDivPosition(this.startX, this.startY);
}
}
onMouseMove(ev) {
if (!this.selectingInProgress || !this.selectionAllowed) {
ev.preventDefault();
return;
}
this.setSelectionDivPosition(ev.clientX, ev.clientY);
if (this._parentDrag) {
const data = this._parentDrag.getElementsInsideSelectionDiv(this.startX, this.startY, this.currentWidth, this.currentHeight);
const result = [];
if (data.length > 0) {
for (let _data of data) {
const fileFolderComponent = _data.getHostComponent();
if (fileFolderComponent) {
result.push(fileFolderComponent);
}
}
}
if (this.checkIfSelectedElementsChanged(result.map((data) => {
return data.__ngContext__;
}))) {
this.selectedElementsChange.emit({
count: result.length,
data: result,
});
}
}
}
onMouseUp(ev) {
if (this.selectingInProgress) {
this.selectingInProgress = false;
this._document.body.removeChild(this.selectionDiv);
}
}
}
NgxDragSelectionDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: NgxDragSelectionDirective, deps: [{ token: DOCUMENT }, { token: EXPLORER_DND_CONTAINER }], target: i0.ɵɵFactoryTarget.Directive });
NgxDragSelectionDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: NgxDragSelectionDirective, selector: "[ngxDragSelection]", inputs: { selectionAllowed: "selectionAllowed", selectionDivElement: "selectionDivElement" }, outputs: { selectedElementsChange: "selectedElementsChange" }, host: { listeners: { "document:mousedown": "onMouseDown($event)", "document:mousemove": "onMouseMove($event)", "document:mouseup": "onMouseUp($event)" } }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: NgxDragSelectionDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngxDragSelection]',
}]
}], ctorParameters: function () {
return [{ type: Document, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }, { type: NgxExplorerContainerDirective, decorators: [{
type: Inject,
args: [EXPLORER_DND_CONTAINER]
}] }];
}, propDecorators: { selectionAllowed: [{
type: Input
}], selectionDivElement: [{
type: Input
}], selectedElementsChange: [{
type: Output
}], onMouseDown: [{
type: HostListener,
args: ['document:mousedown', ['$event']]
}], onMouseMove: [{
type: HostListener,
args: ['document:mousemove', ['$event']]
}], onMouseUp: [{
type: HostListener,
args: ['document:mouseup', ['$event']]
}] } });
class NgxExplorerDndModule {
}
NgxExplorerDndModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: NgxExplorerDndModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
NgxExplorerDndModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.0.1", ngImport: i0, type: NgxExplorerDndModule, declarations: [NgxExplorerContainerDirective,
NgxExplorerItemDirective,
NgxExplorerTargetDirective,
NgxDragSelectionDirective], exports: [NgxExplorerItemDirective,
NgxExplorerContainerDirective,
NgxExplorerTargetDirective,
NgxDragSelectionDirective] });
NgxExplorerDndModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: NgxExplorerDndModule });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: NgxExplorerDndModule, decorators: [{
type: NgModule,
args: [{
declarations: [
NgxExplorerContainerDirective,
NgxExplorerItemDirective,
NgxExplorerTargetDirective,
NgxDragSelectionDirective,
],
imports: [],
exports: [
NgxExplorerItemDirective,
NgxExplorerContainerDirective,
NgxExplorerTargetDirective,
NgxDragSelectionDirective,