UNPKG

ngx-explorer-dnd

Version:

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

643 lines 93.2 kB
import { DOCUMENT } from '@angular/common'; import { Directive, EventEmitter, Inject, InjectionToken, Input, Output, } from '@angular/core'; import { extendStyles } from '../core/css-helper'; import { DragRowPosition, } from '../core/utils'; import * as i0 from "@angular/core"; import * as i1 from "../services/dnd.service"; export const EXPLORER_DND_CONTAINER = new InjectionToken('EXPLORER_DND_CONTAINER'); export 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) => { if (this.dragStarted) { if (this.isMinRangeMoved(event.x, event.y)) { this.canMove = true; const htmlElement = this._currentDragElement?.htmlElement; if (htmlElement) this.toggleVisibility(htmlElement, true); } if (!this.canMove) return; if (this.sortingEnabled) { this.setCurrentPositionOfDraggedElement(this._currentDragElement?.htmlElement, { x: event.x, y: event.y }); } this.setPreviewElementPosition(event.x, event.y); } event.stopPropagation(); event.preventDefault(); }; this.onMouseUp = (event) => { this.dragStarted = false; this.canMove = false; let indexData; if (this.previewElement) { if (this._currentDragElement && this.sortingEnabled) { indexData = this.getCurrentIndexPosition(event.x, event.y, this._currentDragElement?.htmlElement); this.positionDifference = { left: indexData?.x, top: 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(() => { this.drop.emit({ item: this._currentDragElement?.data, target: this._currentTargetElement?.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 = this._currentDragElement?.htmlElement; if (htmlElement) { this.toggleVisibility(htmlElement, false); } this.resetPositions(this._currentDragElement?.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) { if (!draggedElement) return; const parentRect = draggedElement.parentElement?.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?.width && mousePosition.y >= parentRect.y && mousePosition.y <= parentRect.y + parentRect?.height) { const draggedRect = draggedElement.getBoundingClientRect(); this.positionDifference = { ...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) { return this._dragElements.find((handle) => { return (element === handle.htmlElement || element.contains(element)); })?.htmlElement; } /** Get the current index of the dragged element in the unsorted list */ getCurrentIndexPosition(lastMouseX, lastMouseY, draggedElement) { const parentRect = draggedElement.parentElement?.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) { 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 = this._currentDragElement?.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?.getHostComponent() }); } } NgxExplorerContainerDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: NgxExplorerContainerDirective, deps: [{ token: i0.NgZone }, { token: DOCUMENT }, { token: i1.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: i1.DndService }]; }, propDecorators: { dragInProgress: [{ type: Output }], drop: [{ type: Output }], targetChange: [{ type: Output }], dragData: [{ type: Input }], badge: [{ type: Input }], cancelAnimation: [{ type: Input }], sortingEnabled: [{ type: Input }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LWV4cGxvcmVyLWNvbnRhaW5lci5kaXJlY3RpdmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9uZ3gtZXhwbG9yZXItZG5kL3NyYy9saWIvZGlyZWN0aXZlcy9uZ3gtZXhwbG9yZXItY29udGFpbmVyLmRpcmVjdGl2ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDM0MsT0FBTyxFQUNMLFNBQVMsRUFDVCxZQUFZLEVBQ1osTUFBTSxFQUNOLGNBQWMsRUFDZCxLQUFLLEVBSUwsTUFBTSxHQUNQLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNsRCxPQUFPLEVBQ0wsZUFBZSxHQUdoQixNQUFNLGVBQWUsQ0FBQzs7O0FBS3ZCLE1BQU0sQ0FBQyxNQUFNLHNCQUFzQixHQUNqQyxJQUFJLGNBQWMsQ0FBZ0Msd0JBQXdCLENBQUMsQ0FBQztBQVc5RSxNQUFNLE9BQU8sNkJBQTZCO0lBb25CeEMsWUFBWTtJQUVaLFlBQ1UsT0FBZSxFQUNHLFNBQW1CLEVBQ3JDLFVBQXNCO1FBRnRCLFlBQU8sR0FBUCxPQUFPLENBQVE7UUFDRyxjQUFTLEdBQVQsU0FBUyxDQUFVO1FBQ3JDLGVBQVUsR0FBVixVQUFVLENBQVk7UUFobkJoQyw2RUFBNkU7UUFDckUsa0JBQWEsR0FBK0IsRUFBRSxDQUFDO1FBS3ZELHVCQUF1QjtRQUNmLGdCQUFXLEdBQUcsS0FBSyxDQUFDO1FBRTVCLDBGQUEwRjtRQUNsRixZQUFPLEdBQUcsS0FBSyxDQUFDO1FBV3hCLG1EQUFtRDtRQUMzQyxrQkFBYSxHQUdmLEVBQUUsQ0FBQztRQUVULGlGQUFpRjtRQUN6RSxnQkFBVyxHQUdiLEVBQUUsQ0FBQztRQUVULHdEQUF3RDtRQUM5QyxtQkFBYyxHQUEwQixJQUFJLFlBQVksRUFBVyxDQUFDO1FBRTlF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