UNPKG

ngx-explorer-dnd

Version:

## _Drag & Drop in Angular like in a desktop explorer!_

1,111 lines (1,101 loc) 50.5 kB
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,