UNPKG

ngx-explorer-dnd

Version:

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

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