|
|
|
@ -6,56 +6,90 @@ |
|
|
|
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|
|
|
*/ |
|
|
|
|
|
|
|
import { Directive, EventEmitter, Output } from '@angular/core'; |
|
|
|
import { Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output, Renderer2 } from '@angular/core'; |
|
|
|
|
|
|
|
import { |
|
|
|
DragDropSortableService, |
|
|
|
SortableComponent, |
|
|
|
SortableContainer |
|
|
|
} from 'ng2-dnd'; |
|
|
|
import * as dragula from 'dragula'; |
|
|
|
|
|
|
|
@Directive({ |
|
|
|
selector: '[sqxSorted]' |
|
|
|
selector: '[sqxSortModel]' |
|
|
|
}) |
|
|
|
export class SortedDirective { |
|
|
|
private oldArray: any[]; |
|
|
|
export class SortedDirective implements OnDestroy, OnInit { |
|
|
|
private drake: dragula.Drake; |
|
|
|
|
|
|
|
@Input('sqxSortModel') |
|
|
|
public sortModel: any[]; |
|
|
|
|
|
|
|
@Input() |
|
|
|
public handleClass: string | null = null; |
|
|
|
|
|
|
|
@Output('sqxSorted') |
|
|
|
public sorted = new EventEmitter<Array<any>>(); |
|
|
|
public sorted = new EventEmitter<any[]>(); |
|
|
|
|
|
|
|
constructor( |
|
|
|
sortableComponent: SortableComponent, |
|
|
|
sortableContainer: SortableContainer, |
|
|
|
sortableDragDropService: DragDropSortableService |
|
|
|
private readonly elementRef: ElementRef, |
|
|
|
private readonly renderer: Renderer2 |
|
|
|
) { |
|
|
|
const oldDragStartCallback = sortableComponent._onDragStartCallback.bind(sortableComponent); |
|
|
|
} |
|
|
|
|
|
|
|
if (Array.isArray(sortableContainer.sortableData)) { |
|
|
|
sortableComponent._onDragStartCallback = () => { |
|
|
|
oldDragStartCallback(); |
|
|
|
public ngOnDestroy() { |
|
|
|
this.drake.destroy(); |
|
|
|
} |
|
|
|
|
|
|
|
this.oldArray = [...<any>sortableContainer.sortableData]; |
|
|
|
}; |
|
|
|
public ngOnInit() { |
|
|
|
const handleClass = this.handleClass; |
|
|
|
|
|
|
|
const oldDropCallback = sortableComponent._onDropCallback.bind(sortableComponent); |
|
|
|
this.drake = dragula([this.elementRef.nativeElement], { |
|
|
|
ignoreInputTextSelection: true, |
|
|
|
|
|
|
|
sortableComponent._onDropCallback = (event: Event) => { |
|
|
|
oldDropCallback(event); |
|
|
|
moves: (element, container, handle: HTMLElement) => { |
|
|
|
if (!handleClass) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
if (sortableDragDropService.isDragged) { |
|
|
|
const newArray: any[] = <any>sortableContainer.sortableData; |
|
|
|
const oldArray = this.oldArray; |
|
|
|
let current: HTMLElement | null = handle; |
|
|
|
|
|
|
|
if (newArray && oldArray && newArray.length === oldArray.length) { |
|
|
|
for (let i = 0; i < oldArray.length; i++) { |
|
|
|
if (oldArray[i] !== newArray[i]) { |
|
|
|
this.sorted.emit(newArray); |
|
|
|
break; |
|
|
|
while (current && current !== container) { |
|
|
|
if (current.classList.contains(handleClass)) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
current = <any>current.parentNode; |
|
|
|
} |
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
let dragIndex: number; |
|
|
|
let dropIndex: number; |
|
|
|
|
|
|
|
this.drake.on('dragend', (element: any, container: any) => { |
|
|
|
this.renderer.removeClass(element, 'sorting'); |
|
|
|
}); |
|
|
|
|
|
|
|
this.drake.on('drag', (element: any, container: any) => { |
|
|
|
this.renderer.addClass(element, 'sorting'); |
|
|
|
|
|
|
|
dragIndex = this.domIndexOf(container, element); |
|
|
|
}); |
|
|
|
|
|
|
|
this.drake.on('drop', (element: any, container: any) => { |
|
|
|
dropIndex = this.domIndexOf(container, element); |
|
|
|
|
|
|
|
if (this.sortModel && dragIndex !== dropIndex) { |
|
|
|
const newModel = [...this.sortModel]; |
|
|
|
|
|
|
|
const item = this.sortModel[dragIndex]; |
|
|
|
|
|
|
|
newModel.splice(dragIndex, 1); |
|
|
|
newModel.splice(dropIndex, 0, item); |
|
|
|
|
|
|
|
this.sorted.emit(newModel); |
|
|
|
} |
|
|
|
}; |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
private domIndexOf(parent: any, child: any): any { |
|
|
|
return Array.prototype.indexOf.call(parent.children, child); |
|
|
|
} |
|
|
|
} |