docs/DragHost.js
import { Inject } from './di';
import {
DocumentRef,
DropTarget,
DropArea,
RenderableDropTarget,
DragStatus,
DragEvent
} from './common';
import { Renderable, RenderableArea } from './dom';
import { Observable, Subject } from './events';
import { Draggable } from './Draggable';
import { isObject, isFunction, clamp } from './utils';
export class DragHostContainer {
item: Renderable;
dragArea: RenderableArea;
draggable: Draggable<Renderable>;
}
export class DragHost {
dropped: Observable<DropArea>;
start: Observable<DragHostContainer>;
fail: Observable<Renderable>;
success: Observable<Renderable>;
private _item: Renderable;
private _dropped: Subject<DropArea> = new Subject();
private _start: Subject<DragHostContainer> = new Subject();
private _fail: Subject<Renderable> = new Subject();
private _success: Subject<Renderable> = new Subject();
private _areas: DropArea[]|null = null;
private _dropArea: DropArea|null = null;
private _element: HTMLElement = this._document.createElement('div');
private _bounds: RenderableArea|null = null;
private _dragArea: RenderableArea;
get bounds(): RenderableArea|null {
return this._bounds;
}
set bounds(val: RenderableArea|null) {
this._bounds = val;
}
constructor(
@Inject(DocumentRef) private _document: Document
) {
this.dropped = this._dropped.asObservable();
this.start = this._start.asObservable();
this.fail = this._fail.asObservable();
this.success = this._success.asObservable();
this._element.classList.add('ug-layout__drop-indicator');
this._element.hidden = true;
this._document.body.appendChild(this._element);
}
destroy(): void {
if (this._document.body.contains(this._element)) {
this._document.body.removeChild(this._element);
}
}
initialize(container: DragHostContainer): void {
const { item, dragArea, draggable } = container;
this._item = item;
this._dragArea = dragArea;
this._start.next(container);
draggable.drag
.filter(Draggable.isDraggingEvent)
.takeUntil(draggable.drag.filter(Draggable.isDragStopEvent))
.subscribe(this._onDrag.bind(this));
draggable.drag
.filter(Draggable.isDragStopEvent)
.takeUntil(this._dropped)
.subscribe(this._onDragStop.bind(this));
this._element.hidden = false;
}
setDropAreas(areas: DropArea[]): void {
this._areas = areas;
}
private _onDrag(e: DragEvent<Renderable>): void {
if (!this._areas) {
return;
}
const pageX = this._bounds ? this._bounds.clampX(e.pageX) : e.pageX;
const pageY = this._bounds ? this._bounds.clampY(e.pageY) : e.pageY;
let minSurface = Infinity;
let result: DropArea|null = null;
for (const entry of this._areas) {
if (
pageX >= entry.area.x &&
pageX <= entry.area.x2 &&
pageY >= entry.area.y &&
pageY <= entry.area.y2 &&
minSurface > entry.area.surface
) {
minSurface = entry.area.surface;
result = entry;
}
}
if (result) {
if (this._dropArea) {
this._dropArea.item.onDropHighlightExit();
}
this._dropArea = result;
const area = this._dropArea.item.getHighlightCoordinates({
pageX, pageY,
dropArea: result,
dragArea: this._dragArea
});
this._element.style.transform = `translate(${area.x}px, ${area.y}px)`;
this._element.style.height = `${area.height}px`;
this._element.style.width = `${area.width}px`;
}
}
private _onDragStop(e: DragEvent<Renderable>): void {
if (!this._dropArea) {
this._fail.next(e.host);
} else {
this._areas = null;
this._element.hidden = true;
this._item.handleDropCleanup();
this._dropArea.item.handleDrop(this._item, this._dropArea, e);
this._success.next(e.host);
}
this._dropped.next();
}
static isDropTarget(item: any): item is RenderableDropTarget {
return isObject(item)
&& item instanceof Renderable
&& isFunction(item['getHighlightCoordinates'])
&& isFunction(item['handleDrop']);
}
}