mutable-div
Version:
Directives to enable moving or resizing a div element.
369 lines (361 loc) • 20.6 kB
JavaScript
import * as i0 from '@angular/core';
import { EventEmitter, Directive, Input, Output, HostListener, Component, ViewChildren, NgModule } from '@angular/core';
import * as i1 from '@angular/common';
import { CommonModule } from '@angular/common';
class DraggableElementDirective {
constructor(el) {
this.el = el;
this.pointerDown = false;
this.rotate = 0;
this.coordinates = new EventEmitter();
this.stopped = new EventEmitter();
el.nativeElement.style.position = 'absolute';
}
onPointerDown(event) {
event.preventDefault();
this.pointerDown = true;
this.setOriginalPosition(event);
}
onPointerUp() {
this.stopped.emit();
}
onPointerMove(event) {
if (this.pointerDown) {
const element = this.el.nativeElement;
const newPosition = this.setNewPosition(event);
this.setOriginalPosition(event);
element.style.left = (element.offsetLeft + newPosition.x) + 'px';
element.style.top = (element.offsetTop + newPosition.y) + 'px';
this.coordinates.emit({ x: newPosition.x, y: newPosition.y });
}
}
onWindowPointerUp() {
this.pointerDown = false;
}
onPanStart(event) {
this.onPointerDown(event.srcEvent);
}
onPanEnd() {
this.onPointerUp();
}
onPanMove(event) {
this.onPointerMove(event.srcEvent);
}
onWindowPanEnd() {
this.onWindowPointerUp();
}
setOriginalPosition(event) {
switch (this.rotate) {
case 90:
this.originalPosition = {
x: event.clientY,
y: screen.width - event.clientX
};
break;
case 180:
this.originalPosition = {
x: screen.width - event.clientX,
y: screen.height - event.clientY
};
break;
case 270:
this.originalPosition = {
x: screen.height - event.clientY,
y: event.clientX
};
break;
default:
this.originalPosition = {
x: event.clientX,
y: event.clientY
};
}
}
setNewPosition(event) {
let newPosition = {
x: 0,
y: 0
};
switch (this.rotate) {
case 90:
newPosition = {
x: event.clientY - this.originalPosition.x,
y: (screen.width - event.clientX) - this.originalPosition.y
};
break;
case 180:
newPosition = {
x: (screen.width - event.clientX) - this.originalPosition.x,
y: (screen.height - event.clientY) - this.originalPosition.y
};
break;
case 270:
newPosition = {
x: (screen.height - event.clientY) - this.originalPosition.x,
y: event.clientX - this.originalPosition.y
};
break;
default:
newPosition = {
x: event.clientX - this.originalPosition.x,
y: event.clientY - this.originalPosition.y
};
}
return newPosition;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DraggableElementDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: DraggableElementDirective, selector: "[draggable]", inputs: { rotate: "rotate" }, outputs: { coordinates: "coordinates", stopped: "stopped" }, host: { listeners: { "pointerdown": "onPointerDown($event)", "pointerup": "onPointerUp()", "window:pointermove": "onPointerMove($event)", "window:pointerup": "onWindowPointerUp()", "panstart": "onPanStart($event)", "panend": "onPanEnd()", "window:panmove": "onPanMove($event)", "window:panend": "onWindowPanEnd()" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DraggableElementDirective, decorators: [{
type: Directive,
args: [{
selector: '[draggable]'
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { rotate: [{
type: Input
}], coordinates: [{
type: Output
}], stopped: [{
type: Output
}], onPointerDown: [{
type: HostListener,
args: ['pointerdown', ['$event']]
}], onPointerUp: [{
type: HostListener,
args: ['pointerup']
}], onPointerMove: [{
type: HostListener,
args: ['window:pointermove', ['$event']]
}], onWindowPointerUp: [{
type: HostListener,
args: ['window:pointerup']
}], onPanStart: [{
type: HostListener,
args: ['panstart', ['$event']]
}], onPanEnd: [{
type: HostListener,
args: ['panend']
}], onPanMove: [{
type: HostListener,
args: ['window:panmove', ['$event']]
}], onWindowPanEnd: [{
type: HostListener,
args: ['window:panend']
}] } });
class ResizeHandlersComponent {
constructor() {
this.selected = false;
this.minimumSize = 20;
}
ngAfterViewInit() {
this.setHandlePositions();
}
ngOnChanges() {
if (this.selected) {
this.setHandlePositions();
}
}
setHandlePositions() {
this.handles.forEach((element, index) => {
switch (index) {
case 0:
this.setHandleStyles(element, { left: '-7px', right: '', top: '-7px', bottom: '' });
break;
case 1:
this.setHandleStyles(element, { left: '', right: '-7px', top: '-7px', bottom: '' });
break;
case 2:
this.setHandleStyles(element, { left: '-7px', right: '', top: '', bottom: '-7px' });
break;
default:
this.setHandleStyles(element, { left: '', right: '-7px', top: '', bottom: '-7px' });
}
});
}
setHandleStyles(element, styles) {
element.nativeElement.style.left = styles.left;
element.nativeElement.style.right = styles.right;
element.nativeElement.style.top = styles.top;
element.nativeElement.style.bottom = styles.bottom;
}
onPointerDown(event) {
event.stopPropagation();
}
resize(className, coordinates) {
const parentElement = this.parentElement.nativeElement;
const size = this.calculatingPosition(parentElement, className, coordinates);
if (size.width > this.minimumSize && size.height > this.minimumSize) {
if (className.includes('TOP-LEFT')) {
parentElement.style.left = size.position.x + 'px';
parentElement.style.width = size.width + 'px';
parentElement.style.top = size.position.y + 'px';
parentElement.style.height = size.height + 'px';
}
else if (className.includes('TOP-RIGHT')) {
parentElement.style.width = size.width + 'px';
parentElement.style.top = size.position.y + 'px';
parentElement.style.height = size.height + 'px';
}
else if (className.includes('BOTTOM-LEFT')) {
parentElement.style.left = size.position.x + 'px';
parentElement.style.width = size.width + 'px';
parentElement.style.height = size.height + 'px';
}
else {
parentElement.style.width = size.width + 'px';
parentElement.style.height = size.height + 'px';
}
}
this.setHandlePositions();
}
calculatingPosition(parentElement, className, coordinates) {
switch (this.rotate) {
case 90:
return {
width: className.includes('LEFT') ? parentElement.offsetWidth - coordinates.y : parentElement.offsetWidth + coordinates.y,
height: className.includes('TOP') ? parentElement.offsetHeight + coordinates.x : parentElement.offsetHeight - coordinates.x,
position: {
x: parentElement.offsetLeft + coordinates.y,
y: parentElement.offsetTop - coordinates.x
}
};
break;
case 180:
return {
width: className.includes('LEFT') ? parentElement.offsetWidth + coordinates.x : parentElement.offsetWidth - coordinates.x,
height: className.includes('TOP') ? parentElement.offsetHeight + coordinates.y : parentElement.offsetHeight - coordinates.y,
position: {
x: parentElement.offsetLeft - coordinates.x,
y: parentElement.offsetTop - coordinates.y
}
};
break;
case 270:
return {
width: className.includes('LEFT') ? parentElement.offsetWidth + coordinates.y : parentElement.offsetWidth - coordinates.y,
height: className.includes('TOP') ? parentElement.offsetHeight - coordinates.x : parentElement.offsetHeight + coordinates.x,
position: {
x: parentElement.offsetLeft - coordinates.y,
y: parentElement.offsetTop + coordinates.x
}
};
break;
default:
return {
width: className.includes('LEFT') ? parentElement.offsetWidth - coordinates.x : parentElement.offsetWidth + coordinates.x,
height: className.includes('TOP') ? parentElement.offsetHeight - coordinates.y : parentElement.offsetHeight + coordinates.y,
position: {
x: parentElement.offsetLeft + coordinates.x,
y: parentElement.offsetTop + coordinates.y
}
};
}
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ResizeHandlersComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: ResizeHandlersComponent, selector: "lib-resize-handlers", inputs: { parentElement: "parentElement", rotate: "rotate", selected: "selected" }, viewQueries: [{ propertyName: "handles", predicate: ["handle"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div *ngIf=\"selected\" class=\"resize-handlers\">\n <div #handle #topLeft\n class=\"TOP-LEFT handle\"\n draggable\n (pointerdown)=\"onPointerDown($event)\"\n (coordinates)=\"resize(topLeft.className, $event)\"></div>\n <div #handle #topRight\n class=\"TOP-RIGHT handle\"\n draggable\n (pointerdown)=\"onPointerDown($event)\"\n (coordinates)=\"resize(topRight.className, $event)\"></div>\n <div #handle #bottomLeft\n class=\"BOTTOM-LEFT handle\"\n draggable\n (pointerdown)=\"onPointerDown($event)\"\n (coordinates)=\"resize(bottomLeft.className, $event)\"></div>\n <div #handle #bottomRight\n class=\"BOTTOM-RIGHT handle\"\n draggable\n (pointerdown)=\"onPointerDown($event)\"\n (coordinates)=\"resize(bottomRight.className, $event)\"></div>\n</div>\n", styles: [".resize-handlers{width:100%;height:100%;position:absolute;top:0;left:0;border:2px solid #424242;box-sizing:border-box}.handle{position:absolute;width:20px;height:20px;background:#fff;opacity:50%}.TOP-LEFT{left:-7px;top:-7px;border-top:3px solid #424242;border-left:3px solid #424242}.TOP-LEFT:hover,.TOP-LEFT:active{border-radius:unset;background:transparent;border-bottom:black;border-right:black}.TOP-RIGHT{right:-7px;top:-7px;border-top:3px solid #424242;border-right:3px solid #424242}.TOP-RIGHT:hover,.TOP-RIGHT:active{border-radius:unset;background:transparent;border-bottom:black;border-left:black}.BOTTOM-LEFT{left:-7px;bottom:-7px;border-bottom:3px solid #424242;border-left:3px solid #424242}.BOTTOM-LEFT:hover,.BOTTOM-LEFT:active{border-radius:unset;background:transparent;border-top:black;border-right:black}.BOTTOM-RIGHT{right:-7px;bottom:-7px;border-bottom:3px solid #424242;border-right:3px solid #424242}.BOTTOM-RIGHT:hover,.BOTTOM-RIGHT:active{border-radius:unset;background:transparent;border-top:black;border-left:black}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: DraggableElementDirective, selector: "[draggable]", inputs: ["rotate"], outputs: ["coordinates", "stopped"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ResizeHandlersComponent, decorators: [{
type: Component,
args: [{ selector: 'lib-resize-handlers', template: "<div *ngIf=\"selected\" class=\"resize-handlers\">\n <div #handle #topLeft\n class=\"TOP-LEFT handle\"\n draggable\n (pointerdown)=\"onPointerDown($event)\"\n (coordinates)=\"resize(topLeft.className, $event)\"></div>\n <div #handle #topRight\n class=\"TOP-RIGHT handle\"\n draggable\n (pointerdown)=\"onPointerDown($event)\"\n (coordinates)=\"resize(topRight.className, $event)\"></div>\n <div #handle #bottomLeft\n class=\"BOTTOM-LEFT handle\"\n draggable\n (pointerdown)=\"onPointerDown($event)\"\n (coordinates)=\"resize(bottomLeft.className, $event)\"></div>\n <div #handle #bottomRight\n class=\"BOTTOM-RIGHT handle\"\n draggable\n (pointerdown)=\"onPointerDown($event)\"\n (coordinates)=\"resize(bottomRight.className, $event)\"></div>\n</div>\n", styles: [".resize-handlers{width:100%;height:100%;position:absolute;top:0;left:0;border:2px solid #424242;box-sizing:border-box}.handle{position:absolute;width:20px;height:20px;background:#fff;opacity:50%}.TOP-LEFT{left:-7px;top:-7px;border-top:3px solid #424242;border-left:3px solid #424242}.TOP-LEFT:hover,.TOP-LEFT:active{border-radius:unset;background:transparent;border-bottom:black;border-right:black}.TOP-RIGHT{right:-7px;top:-7px;border-top:3px solid #424242;border-right:3px solid #424242}.TOP-RIGHT:hover,.TOP-RIGHT:active{border-radius:unset;background:transparent;border-bottom:black;border-left:black}.BOTTOM-LEFT{left:-7px;bottom:-7px;border-bottom:3px solid #424242;border-left:3px solid #424242}.BOTTOM-LEFT:hover,.BOTTOM-LEFT:active{border-radius:unset;background:transparent;border-top:black;border-right:black}.BOTTOM-RIGHT{right:-7px;bottom:-7px;border-bottom:3px solid #424242;border-right:3px solid #424242}.BOTTOM-RIGHT:hover,.BOTTOM-RIGHT:active{border-radius:unset;background:transparent;border-top:black;border-left:black}\n"] }]
}], ctorParameters: function () { return []; }, propDecorators: { parentElement: [{
type: Input
}], rotate: [{
type: Input
}], selected: [{
type: Input
}], handles: [{
type: ViewChildren,
args: ['handle']
}] } });
class ResizableElementDirective {
constructor(el, container) {
this.el = el;
this.container = container;
this.rotate = 0;
this.selected = false;
this.stopped = new EventEmitter();
}
ngOnInit() {
this.addHandlersToElement();
}
ngOnChanges() {
if (this.resizeHandlersComponent) {
this.resizeHandlersComponent.instance.selected = this.selected;
this.resizeHandlersComponent.instance.rotate = this.rotate;
}
}
addHandlersToElement() {
this.resizeHandlersComponent = this.container.createComponent(ResizeHandlersComponent);
this.resizeHandlersComponent.instance.parentElement = this.el;
this.resizeHandlersComponent.instance.rotate = this.rotate;
this.resizeHandlersComponent.instance.selected = this.selected;
this.el.nativeElement.appendChild(this.resizeHandlersComponent.location.nativeElement);
}
onPointerDown() {
this.resizeHandlersComponent.instance.selected = true;
}
onPointerUp() {
this.stopped.emit();
}
onWindowPointerUp(targetEvent) {
if (!this.el.nativeElement.contains(targetEvent)) {
this.resizeHandlersComponent.instance.selected = false;
}
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ResizableElementDirective, deps: [{ token: i0.ElementRef }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: ResizableElementDirective, selector: "[resizable]", inputs: { rotate: "rotate", selected: "selected" }, outputs: { stopped: "stopped" }, host: { listeners: { "pointerdown": "onPointerDown()", "pointerup": "onPointerUp()", "window:pointerup": "onWindowPointerUp($event.target)" } }, usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ResizableElementDirective, decorators: [{
type: Directive,
args: [{
selector: '[resizable]'
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.ViewContainerRef }]; }, propDecorators: { rotate: [{
type: Input
}], selected: [{
type: Input
}], stopped: [{
type: Output
}], onPointerDown: [{
type: HostListener,
args: ['pointerdown']
}], onPointerUp: [{
type: HostListener,
args: ['pointerup']
}], onWindowPointerUp: [{
type: HostListener,
args: ['window:pointerup', ['$event.target']]
}] } });
class MutableDivModule {
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MutableDivModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
/** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.2.12", ngImport: i0, type: MutableDivModule, declarations: [DraggableElementDirective,
ResizableElementDirective,
ResizeHandlersComponent], imports: [CommonModule], exports: [DraggableElementDirective,
ResizableElementDirective] }); }
/** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MutableDivModule, imports: [CommonModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MutableDivModule, decorators: [{
type: NgModule,
args: [{
declarations: [
DraggableElementDirective,
ResizableElementDirective,
ResizeHandlersComponent
],
imports: [
CommonModule
],
exports: [
DraggableElementDirective,
ResizableElementDirective,
]
}]
}] });
/*
* Public API Surface of mutable-div
*/
/**
* Generated bundle index. Do not edit.
*/
export { DraggableElementDirective, MutableDivModule, ResizableElementDirective };
//# sourceMappingURL=mutable-div.mjs.map