UNPKG

29.3 kBJavaScriptView Raw
1import { EventEmitter, Directive, Inject, PLATFORM_ID, Renderer2, ElementRef, NgZone, Input, Output, Optional, NgModule } from '@angular/core';
2import { isPlatformBrowser } from '@angular/common';
3import { Subject, merge, Observable, fromEvent } from 'rxjs';
4import { tap, share, mergeMap, take, map, pairwise, filter, takeUntil } from 'rxjs/operators';
5
6const ɵ0 = () => {
7 // In case we're in Node.js environment.
8 if (typeof window === 'undefined') {
9 return false;
10 }
11 else {
12 return ('ontouchstart' in window ||
13 navigator.maxTouchPoints > 0 ||
14 navigator.msMaxTouchPoints > 0);
15 }
16};
17/**
18 * @hidden
19 */
20const IS_TOUCH_DEVICE = (ɵ0)();
21
22/** Creates a deep clone of an element. */
23function deepCloneNode(node) {
24 const clone = node.cloneNode(true);
25 const descendantsWithId = clone.querySelectorAll('[id]');
26 const nodeName = node.nodeName.toLowerCase();
27 // Remove the `id` to avoid having multiple elements with the same id on the page.
28 clone.removeAttribute('id');
29 descendantsWithId.forEach((descendant) => {
30 descendant.removeAttribute('id');
31 });
32 if (nodeName === 'canvas') {
33 transferCanvasData(node, clone);
34 }
35 else if (nodeName === 'input' ||
36 nodeName === 'select' ||
37 nodeName === 'textarea') {
38 transferInputData(node, clone);
39 }
40 transferData('canvas', node, clone, transferCanvasData);
41 transferData('input, textarea, select', node, clone, transferInputData);
42 return clone;
43}
44/** Matches elements between an element and its clone and allows for their data to be cloned. */
45function transferData(selector, node, clone, callback) {
46 const descendantElements = node.querySelectorAll(selector);
47 if (descendantElements.length) {
48 const cloneElements = clone.querySelectorAll(selector);
49 for (let i = 0; i < descendantElements.length; i++) {
50 callback(descendantElements[i], cloneElements[i]);
51 }
52 }
53}
54// Counter for unique cloned radio button names.
55let cloneUniqueId = 0;
56/** Transfers the data of one input element to another. */
57function transferInputData(source, clone) {
58 // Browsers throw an error when assigning the value of a file input programmatically.
59 if (clone.type !== 'file') {
60 clone.value = source.value;
61 }
62 // Radio button `name` attributes must be unique for radio button groups
63 // otherwise original radio buttons can lose their checked state
64 // once the clone is inserted in the DOM.
65 if (clone.type === 'radio' && clone.name) {
66 clone.name = `mat-clone-${clone.name}-${cloneUniqueId++}`;
67 }
68}
69/** Transfers the data of one canvas element to another. */
70function transferCanvasData(source, clone) {
71 const context = clone.getContext('2d');
72 if (context) {
73 // In some cases `drawImage` can throw (e.g. if the canvas size is 0x0).
74 // We can't do much about it so just ignore the error.
75 try {
76 context.drawImage(source, 0, 0);
77 }
78 catch (_a) { }
79 }
80}
81
82function getNewBoundingRectangle(startingRect, edges, clientX, clientY) {
83 const newBoundingRect = {
84 top: startingRect.top,
85 bottom: startingRect.bottom,
86 left: startingRect.left,
87 right: startingRect.right,
88 };
89 if (edges.top) {
90 newBoundingRect.top += clientY;
91 }
92 if (edges.bottom) {
93 newBoundingRect.bottom += clientY;
94 }
95 if (edges.left) {
96 newBoundingRect.left += clientX;
97 }
98 if (edges.right) {
99 newBoundingRect.right += clientX;
100 }
101 newBoundingRect.height = newBoundingRect.bottom - newBoundingRect.top;
102 newBoundingRect.width = newBoundingRect.right - newBoundingRect.left;
103 return newBoundingRect;
104}
105function getElementRect(element, ghostElementPositioning) {
106 let translateX = 0;
107 let translateY = 0;
108 const style = element.nativeElement.style;
109 const transformProperties = [
110 'transform',
111 '-ms-transform',
112 '-moz-transform',
113 '-o-transform',
114 ];
115 const transform = transformProperties
116 .map((property) => style[property])
117 .find((value) => !!value);
118 if (transform && transform.includes('translate')) {
119 translateX = transform.replace(/.*translate3?d?\((-?[0-9]*)px, (-?[0-9]*)px.*/, '$1');
120 translateY = transform.replace(/.*translate3?d?\((-?[0-9]*)px, (-?[0-9]*)px.*/, '$2');
121 }
122 if (ghostElementPositioning === 'absolute') {
123 return {
124 height: element.nativeElement.offsetHeight,
125 width: element.nativeElement.offsetWidth,
126 top: element.nativeElement.offsetTop - translateY,
127 bottom: element.nativeElement.offsetHeight +
128 element.nativeElement.offsetTop -
129 translateY,
130 left: element.nativeElement.offsetLeft - translateX,
131 right: element.nativeElement.offsetWidth +
132 element.nativeElement.offsetLeft -
133 translateX,
134 };
135 }
136 else {
137 const boundingRect = element.nativeElement.getBoundingClientRect();
138 return {
139 height: boundingRect.height,
140 width: boundingRect.width,
141 top: boundingRect.top - translateY,
142 bottom: boundingRect.bottom - translateY,
143 left: boundingRect.left - translateX,
144 right: boundingRect.right - translateX,
145 scrollTop: element.nativeElement.scrollTop,
146 scrollLeft: element.nativeElement.scrollLeft,
147 };
148 }
149}
150const DEFAULT_RESIZE_CURSORS = Object.freeze({
151 topLeft: 'nw-resize',
152 topRight: 'ne-resize',
153 bottomLeft: 'sw-resize',
154 bottomRight: 'se-resize',
155 leftOrRight: 'col-resize',
156 topOrBottom: 'row-resize',
157});
158function getResizeCursor(edges, cursors) {
159 if (edges.left && edges.top) {
160 return cursors.topLeft;
161 }
162 else if (edges.right && edges.top) {
163 return cursors.topRight;
164 }
165 else if (edges.left && edges.bottom) {
166 return cursors.bottomLeft;
167 }
168 else if (edges.right && edges.bottom) {
169 return cursors.bottomRight;
170 }
171 else if (edges.left || edges.right) {
172 return cursors.leftOrRight;
173 }
174 else if (edges.top || edges.bottom) {
175 return cursors.topOrBottom;
176 }
177 else {
178 return '';
179 }
180}
181function getEdgesDiff({ edges, initialRectangle, newRectangle, }) {
182 const edgesDiff = {};
183 Object.keys(edges).forEach((edge) => {
184 edgesDiff[edge] = (newRectangle[edge] || 0) - (initialRectangle[edge] || 0);
185 });
186 return edgesDiff;
187}
188const RESIZE_ACTIVE_CLASS = 'resize-active';
189const RESIZE_GHOST_ELEMENT_CLASS = 'resize-ghost-element';
190const MOUSE_MOVE_THROTTLE_MS = 50;
191/**
192 * Place this on an element to make it resizable. For example:
193 *
194 * ```html
195 * <div
196 * mwlResizable
197 * [resizeEdges]="{bottom: true, right: true, top: true, left: true}"
198 * [enableGhostResize]="true">
199 * </div>
200 * ```
201 * Or in case they are sibling elements:
202 * ```html
203 * <div mwlResizable #resizableElement="mwlResizable"></div>
204 * <div mwlResizeHandle [resizableContainer]="resizableElement" [resizeEdges]="{bottom: true, right: true}"></div>
205 * ```
206 */
207class ResizableDirective {
208 /**
209 * @hidden
210 */
211 constructor(platformId, renderer, elm, zone) {
212 this.platformId = platformId;
213 this.renderer = renderer;
214 this.elm = elm;
215 this.zone = zone;
216 /**
217 * Set to `true` to enable a temporary resizing effect of the element in between the `resizeStart` and `resizeEnd` events.
218 */
219 this.enableGhostResize = false;
220 /**
221 * A snap grid that resize events will be locked to.
222 *
223 * e.g. to only allow the element to be resized every 10px set it to `{left: 10, right: 10}`
224 */
225 this.resizeSnapGrid = {};
226 /**
227 * The mouse cursors that will be set on the resize edges
228 */
229 this.resizeCursors = DEFAULT_RESIZE_CURSORS;
230 /**
231 * Define the positioning of the ghost element (can be fixed or absolute)
232 */
233 this.ghostElementPositioning = 'fixed';
234 /**
235 * Allow elements to be resized to negative dimensions
236 */
237 this.allowNegativeResizes = false;
238 /**
239 * The mouse move throttle in milliseconds, default: 50 ms
240 */
241 this.mouseMoveThrottleMS = MOUSE_MOVE_THROTTLE_MS;
242 /**
243 * Called when the mouse is pressed and a resize event is about to begin. `$event` is a `ResizeEvent` object.
244 */
245 this.resizeStart = new EventEmitter();
246 /**
247 * Called as the mouse is dragged after a resize event has begun. `$event` is a `ResizeEvent` object.
248 */
249 this.resizing = new EventEmitter();
250 /**
251 * Called after the mouse is released after a resize event. `$event` is a `ResizeEvent` object.
252 */
253 this.resizeEnd = new EventEmitter();
254 /**
255 * @hidden
256 */
257 this.mouseup = new Subject();
258 /**
259 * @hidden
260 */
261 this.mousedown = new Subject();
262 /**
263 * @hidden
264 */
265 this.mousemove = new Subject();
266 this.destroy$ = new Subject();
267 this.pointerEventListeners = PointerEventListeners.getInstance(renderer, zone);
268 }
269 /**
270 * @hidden
271 */
272 ngOnInit() {
273 const mousedown$ = merge(this.pointerEventListeners.pointerDown, this.mousedown);
274 const mousemove$ = merge(this.pointerEventListeners.pointerMove, this.mousemove).pipe(tap(({ event }) => {
275 if (currentResize) {
276 try {
277 event.preventDefault();
278 }
279 catch (e) {
280 // just adding try-catch not to see errors in console if there is a passive listener for same event somewhere
281 // browser does nothing except of writing errors to console
282 }
283 }
284 }), share());
285 const mouseup$ = merge(this.pointerEventListeners.pointerUp, this.mouseup);
286 let currentResize;
287 const removeGhostElement = () => {
288 if (currentResize && currentResize.clonedNode) {
289 this.elm.nativeElement.parentElement.removeChild(currentResize.clonedNode);
290 this.renderer.setStyle(this.elm.nativeElement, 'visibility', 'inherit');
291 }
292 };
293 const getResizeCursors = () => {
294 return Object.assign(Object.assign({}, DEFAULT_RESIZE_CURSORS), this.resizeCursors);
295 };
296 const mousedrag = mousedown$
297 .pipe(mergeMap((startCoords) => {
298 function getDiff(moveCoords) {
299 return {
300 clientX: moveCoords.clientX - startCoords.clientX,
301 clientY: moveCoords.clientY - startCoords.clientY,
302 };
303 }
304 const getSnapGrid = () => {
305 const snapGrid = { x: 1, y: 1 };
306 if (currentResize) {
307 if (this.resizeSnapGrid.left && currentResize.edges.left) {
308 snapGrid.x = +this.resizeSnapGrid.left;
309 }
310 else if (this.resizeSnapGrid.right &&
311 currentResize.edges.right) {
312 snapGrid.x = +this.resizeSnapGrid.right;
313 }
314 if (this.resizeSnapGrid.top && currentResize.edges.top) {
315 snapGrid.y = +this.resizeSnapGrid.top;
316 }
317 else if (this.resizeSnapGrid.bottom &&
318 currentResize.edges.bottom) {
319 snapGrid.y = +this.resizeSnapGrid.bottom;
320 }
321 }
322 return snapGrid;
323 };
324 function getGrid(coords, snapGrid) {
325 return {
326 x: Math.ceil(coords.clientX / snapGrid.x),
327 y: Math.ceil(coords.clientY / snapGrid.y),
328 };
329 }
330 return merge(mousemove$.pipe(take(1)).pipe(map((coords) => [, coords])), mousemove$.pipe(pairwise()))
331 .pipe(map(([previousCoords, newCoords]) => {
332 return [
333 previousCoords ? getDiff(previousCoords) : previousCoords,
334 getDiff(newCoords),
335 ];
336 }))
337 .pipe(filter(([previousCoords, newCoords]) => {
338 if (!previousCoords) {
339 return true;
340 }
341 const snapGrid = getSnapGrid();
342 const previousGrid = getGrid(previousCoords, snapGrid);
343 const newGrid = getGrid(newCoords, snapGrid);
344 return (previousGrid.x !== newGrid.x || previousGrid.y !== newGrid.y);
345 }))
346 .pipe(map(([, newCoords]) => {
347 const snapGrid = getSnapGrid();
348 return {
349 clientX: Math.round(newCoords.clientX / snapGrid.x) * snapGrid.x,
350 clientY: Math.round(newCoords.clientY / snapGrid.y) * snapGrid.y,
351 };
352 }))
353 .pipe(takeUntil(merge(mouseup$, mousedown$)));
354 }))
355 .pipe(filter(() => !!currentResize));
356 mousedrag
357 .pipe(map(({ clientX, clientY }) => {
358 return getNewBoundingRectangle(currentResize.startingRect, currentResize.edges, clientX, clientY);
359 }))
360 .pipe(filter((newBoundingRect) => {
361 return (this.allowNegativeResizes ||
362 !!(newBoundingRect.height &&
363 newBoundingRect.width &&
364 newBoundingRect.height > 0 &&
365 newBoundingRect.width > 0));
366 }))
367 .pipe(filter((newBoundingRect) => {
368 return this.validateResize
369 ? this.validateResize({
370 rectangle: newBoundingRect,
371 edges: getEdgesDiff({
372 edges: currentResize.edges,
373 initialRectangle: currentResize.startingRect,
374 newRectangle: newBoundingRect,
375 }),
376 })
377 : true;
378 }), takeUntil(this.destroy$))
379 .subscribe((newBoundingRect) => {
380 if (currentResize && currentResize.clonedNode) {
381 this.renderer.setStyle(currentResize.clonedNode, 'height', `${newBoundingRect.height}px`);
382 this.renderer.setStyle(currentResize.clonedNode, 'width', `${newBoundingRect.width}px`);
383 this.renderer.setStyle(currentResize.clonedNode, 'top', `${newBoundingRect.top}px`);
384 this.renderer.setStyle(currentResize.clonedNode, 'left', `${newBoundingRect.left}px`);
385 }
386 if (this.resizing.observers.length > 0) {
387 this.zone.run(() => {
388 this.resizing.emit({
389 edges: getEdgesDiff({
390 edges: currentResize.edges,
391 initialRectangle: currentResize.startingRect,
392 newRectangle: newBoundingRect,
393 }),
394 rectangle: newBoundingRect,
395 });
396 });
397 }
398 currentResize.currentRect = newBoundingRect;
399 });
400 mousedown$
401 .pipe(map(({ edges }) => {
402 return edges || {};
403 }), filter((edges) => {
404 return Object.keys(edges).length > 0;
405 }), takeUntil(this.destroy$))
406 .subscribe((edges) => {
407 if (currentResize) {
408 removeGhostElement();
409 }
410 const startingRect = getElementRect(this.elm, this.ghostElementPositioning);
411 currentResize = {
412 edges,
413 startingRect,
414 currentRect: startingRect,
415 };
416 const resizeCursors = getResizeCursors();
417 const cursor = getResizeCursor(currentResize.edges, resizeCursors);
418 this.renderer.setStyle(document.body, 'cursor', cursor);
419 this.setElementClass(this.elm, RESIZE_ACTIVE_CLASS, true);
420 if (this.enableGhostResize) {
421 currentResize.clonedNode = deepCloneNode(this.elm.nativeElement);
422 this.elm.nativeElement.parentElement.appendChild(currentResize.clonedNode);
423 this.renderer.setStyle(this.elm.nativeElement, 'visibility', 'hidden');
424 this.renderer.setStyle(currentResize.clonedNode, 'position', this.ghostElementPositioning);
425 this.renderer.setStyle(currentResize.clonedNode, 'left', `${currentResize.startingRect.left}px`);
426 this.renderer.setStyle(currentResize.clonedNode, 'top', `${currentResize.startingRect.top}px`);
427 this.renderer.setStyle(currentResize.clonedNode, 'height', `${currentResize.startingRect.height}px`);
428 this.renderer.setStyle(currentResize.clonedNode, 'width', `${currentResize.startingRect.width}px`);
429 this.renderer.setStyle(currentResize.clonedNode, 'cursor', getResizeCursor(currentResize.edges, resizeCursors));
430 this.renderer.addClass(currentResize.clonedNode, RESIZE_GHOST_ELEMENT_CLASS);
431 currentResize.clonedNode.scrollTop = currentResize.startingRect
432 .scrollTop;
433 currentResize.clonedNode.scrollLeft = currentResize.startingRect
434 .scrollLeft;
435 }
436 if (this.resizeStart.observers.length > 0) {
437 this.zone.run(() => {
438 this.resizeStart.emit({
439 edges: getEdgesDiff({
440 edges,
441 initialRectangle: startingRect,
442 newRectangle: startingRect,
443 }),
444 rectangle: getNewBoundingRectangle(startingRect, {}, 0, 0),
445 });
446 });
447 }
448 });
449 mouseup$.pipe(takeUntil(this.destroy$)).subscribe(() => {
450 if (currentResize) {
451 this.renderer.removeClass(this.elm.nativeElement, RESIZE_ACTIVE_CLASS);
452 this.renderer.setStyle(document.body, 'cursor', '');
453 this.renderer.setStyle(this.elm.nativeElement, 'cursor', '');
454 if (this.resizeEnd.observers.length > 0) {
455 this.zone.run(() => {
456 this.resizeEnd.emit({
457 edges: getEdgesDiff({
458 edges: currentResize.edges,
459 initialRectangle: currentResize.startingRect,
460 newRectangle: currentResize.currentRect,
461 }),
462 rectangle: currentResize.currentRect,
463 });
464 });
465 }
466 removeGhostElement();
467 currentResize = null;
468 }
469 });
470 }
471 /**
472 * @hidden
473 */
474 ngOnDestroy() {
475 // browser check for angular universal, because it doesn't know what document is
476 if (isPlatformBrowser(this.platformId)) {
477 this.renderer.setStyle(document.body, 'cursor', '');
478 }
479 this.mousedown.complete();
480 this.mouseup.complete();
481 this.mousemove.complete();
482 this.destroy$.next();
483 }
484 setElementClass(elm, name, add) {
485 if (add) {
486 this.renderer.addClass(elm.nativeElement, name);
487 }
488 else {
489 this.renderer.removeClass(elm.nativeElement, name);
490 }
491 }
492}
493ResizableDirective.decorators = [
494 { type: Directive, args: [{
495 selector: '[mwlResizable]',
496 exportAs: 'mwlResizable',
497 },] }
498];
499ResizableDirective.ctorParameters = () => [
500 { type: undefined, decorators: [{ type: Inject, args: [PLATFORM_ID,] }] },
501 { type: Renderer2 },
502 { type: ElementRef },
503 { type: NgZone }
504];
505ResizableDirective.propDecorators = {
506 validateResize: [{ type: Input }],
507 enableGhostResize: [{ type: Input }],
508 resizeSnapGrid: [{ type: Input }],
509 resizeCursors: [{ type: Input }],
510 ghostElementPositioning: [{ type: Input }],
511 allowNegativeResizes: [{ type: Input }],
512 mouseMoveThrottleMS: [{ type: Input }],
513 resizeStart: [{ type: Output }],
514 resizing: [{ type: Output }],
515 resizeEnd: [{ type: Output }]
516};
517class PointerEventListeners {
518 constructor(renderer, zone) {
519 this.pointerDown = new Observable((observer) => {
520 let unsubscribeMouseDown;
521 let unsubscribeTouchStart;
522 zone.runOutsideAngular(() => {
523 unsubscribeMouseDown = renderer.listen('document', 'mousedown', (event) => {
524 observer.next({
525 clientX: event.clientX,
526 clientY: event.clientY,
527 event,
528 });
529 });
530 if (IS_TOUCH_DEVICE) {
531 unsubscribeTouchStart = renderer.listen('document', 'touchstart', (event) => {
532 observer.next({
533 clientX: event.touches[0].clientX,
534 clientY: event.touches[0].clientY,
535 event,
536 });
537 });
538 }
539 });
540 return () => {
541 unsubscribeMouseDown();
542 if (IS_TOUCH_DEVICE) {
543 unsubscribeTouchStart();
544 }
545 };
546 }).pipe(share());
547 this.pointerMove = new Observable((observer) => {
548 let unsubscribeMouseMove;
549 let unsubscribeTouchMove;
550 zone.runOutsideAngular(() => {
551 unsubscribeMouseMove = renderer.listen('document', 'mousemove', (event) => {
552 observer.next({
553 clientX: event.clientX,
554 clientY: event.clientY,
555 event,
556 });
557 });
558 if (IS_TOUCH_DEVICE) {
559 unsubscribeTouchMove = renderer.listen('document', 'touchmove', (event) => {
560 observer.next({
561 clientX: event.targetTouches[0].clientX,
562 clientY: event.targetTouches[0].clientY,
563 event,
564 });
565 });
566 }
567 });
568 return () => {
569 unsubscribeMouseMove();
570 if (IS_TOUCH_DEVICE) {
571 unsubscribeTouchMove();
572 }
573 };
574 }).pipe(share());
575 this.pointerUp = new Observable((observer) => {
576 let unsubscribeMouseUp;
577 let unsubscribeTouchEnd;
578 let unsubscribeTouchCancel;
579 zone.runOutsideAngular(() => {
580 unsubscribeMouseUp = renderer.listen('document', 'mouseup', (event) => {
581 observer.next({
582 clientX: event.clientX,
583 clientY: event.clientY,
584 event,
585 });
586 });
587 if (IS_TOUCH_DEVICE) {
588 unsubscribeTouchEnd = renderer.listen('document', 'touchend', (event) => {
589 observer.next({
590 clientX: event.changedTouches[0].clientX,
591 clientY: event.changedTouches[0].clientY,
592 event,
593 });
594 });
595 unsubscribeTouchCancel = renderer.listen('document', 'touchcancel', (event) => {
596 observer.next({
597 clientX: event.changedTouches[0].clientX,
598 clientY: event.changedTouches[0].clientY,
599 event,
600 });
601 });
602 }
603 });
604 return () => {
605 unsubscribeMouseUp();
606 if (IS_TOUCH_DEVICE) {
607 unsubscribeTouchEnd();
608 unsubscribeTouchCancel();
609 }
610 };
611 }).pipe(share());
612 }
613 static getInstance(renderer, zone) {
614 if (!PointerEventListeners.instance) {
615 PointerEventListeners.instance = new PointerEventListeners(renderer, zone);
616 }
617 return PointerEventListeners.instance;
618 }
619}
620
621/**
622 * An element placed inside a `mwlResizable` directive to be used as a drag and resize handle
623 *
624 * For example
625 *
626 * ```html
627 * <div mwlResizable>
628 * <div mwlResizeHandle [resizeEdges]="{bottom: true, right: true}"></div>
629 * </div>
630 * ```
631 * Or in case they are sibling elements:
632 * ```html
633 * <div mwlResizable #resizableElement="mwlResizable"></div>
634 * <div mwlResizeHandle [resizableContainer]="resizableElement" [resizeEdges]="{bottom: true, right: true}"></div>
635 * ```
636 */
637class ResizeHandleDirective {
638 constructor(renderer, element, zone, resizableDirective) {
639 this.renderer = renderer;
640 this.element = element;
641 this.zone = zone;
642 this.resizableDirective = resizableDirective;
643 /**
644 * The `Edges` object that contains the edges of the parent element that dragging the handle will trigger a resize on
645 */
646 this.resizeEdges = {};
647 this.eventListeners = {};
648 this.destroy$ = new Subject();
649 }
650 ngOnInit() {
651 this.zone.runOutsideAngular(() => {
652 this.listenOnTheHost('mousedown').subscribe((event) => {
653 this.onMousedown(event, event.clientX, event.clientY);
654 });
655 this.listenOnTheHost('mouseup').subscribe((event) => {
656 this.onMouseup(event.clientX, event.clientY);
657 });
658 if (IS_TOUCH_DEVICE) {
659 this.listenOnTheHost('touchstart').subscribe((event) => {
660 this.onMousedown(event, event.touches[0].clientX, event.touches[0].clientY);
661 });
662 merge(this.listenOnTheHost('touchend'), this.listenOnTheHost('touchcancel')).subscribe((event) => {
663 this.onMouseup(event.changedTouches[0].clientX, event.changedTouches[0].clientY);
664 });
665 }
666 });
667 }
668 ngOnDestroy() {
669 this.destroy$.next();
670 this.unsubscribeEventListeners();
671 }
672 /**
673 * @hidden
674 */
675 onMousedown(event, clientX, clientY) {
676 event.preventDefault();
677 if (!this.eventListeners.touchmove) {
678 this.eventListeners.touchmove = this.renderer.listen(this.element.nativeElement, 'touchmove', (touchMoveEvent) => {
679 this.onMousemove(touchMoveEvent, touchMoveEvent.targetTouches[0].clientX, touchMoveEvent.targetTouches[0].clientY);
680 });
681 }
682 if (!this.eventListeners.mousemove) {
683 this.eventListeners.mousemove = this.renderer.listen(this.element.nativeElement, 'mousemove', (mouseMoveEvent) => {
684 this.onMousemove(mouseMoveEvent, mouseMoveEvent.clientX, mouseMoveEvent.clientY);
685 });
686 }
687 this.resizable.mousedown.next({
688 clientX,
689 clientY,
690 edges: this.resizeEdges,
691 });
692 }
693 /**
694 * @hidden
695 */
696 onMouseup(clientX, clientY) {
697 this.unsubscribeEventListeners();
698 this.resizable.mouseup.next({
699 clientX,
700 clientY,
701 edges: this.resizeEdges,
702 });
703 }
704 // directive might be passed from DI or as an input
705 get resizable() {
706 return this.resizableDirective || this.resizableContainer;
707 }
708 onMousemove(event, clientX, clientY) {
709 this.resizable.mousemove.next({
710 clientX,
711 clientY,
712 edges: this.resizeEdges,
713 event,
714 });
715 }
716 unsubscribeEventListeners() {
717 Object.keys(this.eventListeners).forEach((type) => {
718 this.eventListeners[type]();
719 delete this.eventListeners[type];
720 });
721 }
722 listenOnTheHost(eventName) {
723 return fromEvent(this.element.nativeElement, eventName).pipe(takeUntil(this.destroy$));
724 }
725}
726ResizeHandleDirective.decorators = [
727 { type: Directive, args: [{
728 selector: '[mwlResizeHandle]',
729 },] }
730];
731ResizeHandleDirective.ctorParameters = () => [
732 { type: Renderer2 },
733 { type: ElementRef },
734 { type: NgZone },
735 { type: ResizableDirective, decorators: [{ type: Optional }] }
736];
737ResizeHandleDirective.propDecorators = {
738 resizeEdges: [{ type: Input }],
739 resizableContainer: [{ type: Input }]
740};
741
742class ResizableModule {
743}
744ResizableModule.decorators = [
745 { type: NgModule, args: [{
746 declarations: [ResizableDirective, ResizeHandleDirective],
747 exports: [ResizableDirective, ResizeHandleDirective],
748 },] }
749];
750
751/*
752 * Public API Surface of angular-resizable-element
753 */
754
755/**
756 * Generated bundle index. Do not edit.
757 */
758
759export { ResizableDirective, ResizableModule, ResizeHandleDirective };
760//# sourceMappingURL=angular-resizable-element.js.map