1 | import * as i0 from '@angular/core';
|
2 | import { Injectable, Directive, EventEmitter, Optional, Inject, Input, Output, NgModule } from '@angular/core';
|
3 | import { Subject, Observable, ReplaySubject, merge, combineLatest, fromEvent } from 'rxjs';
|
4 | import { filter, mergeMap, startWith, map, share, takeUntil, take, takeLast, count, pairwise, distinctUntilChanged } from 'rxjs/operators';
|
5 | import { DOCUMENT } from '@angular/common';
|
6 | import autoScroll from '@mattlewis92/dom-autoscroller';
|
7 |
|
8 | function addClass(renderer, element, classToAdd) {
|
9 | if (classToAdd) {
|
10 | classToAdd
|
11 | .split(' ')
|
12 | .forEach((className) => renderer.addClass(element.nativeElement, className));
|
13 | }
|
14 | }
|
15 | function removeClass(renderer, element, classToRemove) {
|
16 | if (classToRemove) {
|
17 | classToRemove
|
18 | .split(' ')
|
19 | .forEach((className) => renderer.removeClass(element.nativeElement, className));
|
20 | }
|
21 | }
|
22 |
|
23 | class DraggableHelper {
|
24 | constructor() {
|
25 | this.currentDrag = new Subject();
|
26 | }
|
27 | }
|
28 | DraggableHelper.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: DraggableHelper, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
29 | DraggableHelper.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: DraggableHelper, providedIn: 'root' });
|
30 | i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: DraggableHelper, decorators: [{
|
31 | type: Injectable,
|
32 | args: [{
|
33 | providedIn: 'root',
|
34 | }]
|
35 | }] });
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 | class DraggableScrollContainerDirective {
|
46 | |
47 |
|
48 |
|
49 | constructor(elementRef) {
|
50 | this.elementRef = elementRef;
|
51 | }
|
52 | }
|
53 | DraggableScrollContainerDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: DraggableScrollContainerDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
|
54 | DraggableScrollContainerDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.3", type: DraggableScrollContainerDirective, selector: "[mwlDraggableScrollContainer]", ngImport: i0 });
|
55 | i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: DraggableScrollContainerDirective, decorators: [{
|
56 | type: Directive,
|
57 | args: [{
|
58 | selector: '[mwlDraggableScrollContainer]',
|
59 | }]
|
60 | }], ctorParameters: function () { return [{ type: i0.ElementRef }]; } });
|
61 |
|
62 | class DraggableDirective {
|
63 | |
64 |
|
65 |
|
66 | constructor(element, renderer, draggableHelper, zone, vcr, scrollContainer, document) {
|
67 | this.element = element;
|
68 | this.renderer = renderer;
|
69 | this.draggableHelper = draggableHelper;
|
70 | this.zone = zone;
|
71 | this.vcr = vcr;
|
72 | this.scrollContainer = scrollContainer;
|
73 | this.document = document;
|
74 | |
75 |
|
76 |
|
77 | this.dragAxis = { x: true, y: true };
|
78 | |
79 |
|
80 |
|
81 | this.dragSnapGrid = {};
|
82 | |
83 |
|
84 |
|
85 | this.ghostDragEnabled = true;
|
86 | |
87 |
|
88 |
|
89 | this.showOriginalElementWhileDragging = false;
|
90 | |
91 |
|
92 |
|
93 | this.dragCursor = '';
|
94 | |
95 |
|
96 |
|
97 | this.autoScroll = {
|
98 | margin: 20,
|
99 | };
|
100 | |
101 |
|
102 |
|
103 | this.dragPointerDown = new EventEmitter();
|
104 | |
105 |
|
106 |
|
107 |
|
108 |
|
109 | this.dragStart = new EventEmitter();
|
110 | |
111 |
|
112 |
|
113 | this.ghostElementCreated = new EventEmitter();
|
114 | |
115 |
|
116 |
|
117 | this.dragging = new EventEmitter();
|
118 | |
119 |
|
120 |
|
121 | this.dragEnd = new EventEmitter();
|
122 | |
123 |
|
124 |
|
125 | this.pointerDown$ = new Subject();
|
126 | |
127 |
|
128 |
|
129 | this.pointerMove$ = new Subject();
|
130 | |
131 |
|
132 |
|
133 | this.pointerUp$ = new Subject();
|
134 | this.eventListenerSubscriptions = {};
|
135 | this.destroy$ = new Subject();
|
136 | this.timeLongPress = { timerBegin: 0, timerEnd: 0 };
|
137 | }
|
138 | ngOnInit() {
|
139 | this.checkEventListeners();
|
140 | const pointerDragged$ = this.pointerDown$.pipe(filter(() => this.canDrag()), mergeMap((pointerDownEvent) => {
|
141 |
|
142 |
|
143 | if (pointerDownEvent.event.stopPropagation && !this.scrollContainer) {
|
144 | pointerDownEvent.event.stopPropagation();
|
145 | }
|
146 |
|
147 | const globalDragStyle = this.renderer.createElement('style');
|
148 | this.renderer.setAttribute(globalDragStyle, 'type', 'text/css');
|
149 | this.renderer.appendChild(globalDragStyle, this.renderer.createText(`
|
150 | body * {
|
151 | -moz-user-select: none;
|
152 | -ms-user-select: none;
|
153 | -webkit-user-select: none;
|
154 | user-select: none;
|
155 | }
|
156 | `));
|
157 | requestAnimationFrame(() => {
|
158 | this.document.head.appendChild(globalDragStyle);
|
159 | });
|
160 | const startScrollPosition = this.getScrollPosition();
|
161 | const scrollContainerScroll$ = new Observable((observer) => {
|
162 | const scrollContainer = this.scrollContainer
|
163 | ? this.scrollContainer.elementRef.nativeElement
|
164 | : 'window';
|
165 | return this.renderer.listen(scrollContainer, 'scroll', (e) => observer.next(e));
|
166 | }).pipe(startWith(startScrollPosition), map(() => this.getScrollPosition()));
|
167 | const currentDrag$ = new Subject();
|
168 | const cancelDrag$ = new ReplaySubject();
|
169 | if (this.dragPointerDown.observers.length > 0) {
|
170 | this.zone.run(() => {
|
171 | this.dragPointerDown.next({ x: 0, y: 0 });
|
172 | });
|
173 | }
|
174 | const dragComplete$ = merge(this.pointerUp$, this.pointerDown$, cancelDrag$, this.destroy$).pipe(share());
|
175 | const pointerMove = combineLatest([
|
176 | this.pointerMove$,
|
177 | scrollContainerScroll$,
|
178 | ]).pipe(map(([pointerMoveEvent, scroll]) => {
|
179 | return {
|
180 | currentDrag$,
|
181 | transformX: pointerMoveEvent.clientX - pointerDownEvent.clientX,
|
182 | transformY: pointerMoveEvent.clientY - pointerDownEvent.clientY,
|
183 | clientX: pointerMoveEvent.clientX,
|
184 | clientY: pointerMoveEvent.clientY,
|
185 | scrollLeft: scroll.left,
|
186 | scrollTop: scroll.top,
|
187 | target: pointerMoveEvent.event.target,
|
188 | };
|
189 | }), map((moveData) => {
|
190 | if (this.dragSnapGrid.x) {
|
191 | moveData.transformX =
|
192 | Math.round(moveData.transformX / this.dragSnapGrid.x) *
|
193 | this.dragSnapGrid.x;
|
194 | }
|
195 | if (this.dragSnapGrid.y) {
|
196 | moveData.transformY =
|
197 | Math.round(moveData.transformY / this.dragSnapGrid.y) *
|
198 | this.dragSnapGrid.y;
|
199 | }
|
200 | return moveData;
|
201 | }), map((moveData) => {
|
202 | if (!this.dragAxis.x) {
|
203 | moveData.transformX = 0;
|
204 | }
|
205 | if (!this.dragAxis.y) {
|
206 | moveData.transformY = 0;
|
207 | }
|
208 | return moveData;
|
209 | }), map((moveData) => {
|
210 | const scrollX = moveData.scrollLeft - startScrollPosition.left;
|
211 | const scrollY = moveData.scrollTop - startScrollPosition.top;
|
212 | return {
|
213 | ...moveData,
|
214 | x: moveData.transformX + scrollX,
|
215 | y: moveData.transformY + scrollY,
|
216 | };
|
217 | }), filter(({ x, y, transformX, transformY }) => !this.validateDrag ||
|
218 | this.validateDrag({
|
219 | x,
|
220 | y,
|
221 | transform: { x: transformX, y: transformY },
|
222 | })), takeUntil(dragComplete$), share());
|
223 | const dragStarted$ = pointerMove.pipe(take(1), share());
|
224 | const dragEnded$ = pointerMove.pipe(takeLast(1), share());
|
225 | dragStarted$.subscribe(({ clientX, clientY, x, y }) => {
|
226 | if (this.dragStart.observers.length > 0) {
|
227 | this.zone.run(() => {
|
228 | this.dragStart.next({ cancelDrag$ });
|
229 | });
|
230 | }
|
231 | this.scroller = autoScroll([
|
232 | this.scrollContainer
|
233 | ? this.scrollContainer.elementRef.nativeElement
|
234 | : this.document.defaultView,
|
235 | ], {
|
236 | ...this.autoScroll,
|
237 | autoScroll() {
|
238 | return true;
|
239 | },
|
240 | });
|
241 | addClass(this.renderer, this.element, this.dragActiveClass);
|
242 | if (this.ghostDragEnabled) {
|
243 | const rect = this.element.nativeElement.getBoundingClientRect();
|
244 | const clone = this.element.nativeElement.cloneNode(true);
|
245 | if (!this.showOriginalElementWhileDragging) {
|
246 | this.renderer.setStyle(this.element.nativeElement, 'visibility', 'hidden');
|
247 | }
|
248 | if (this.ghostElementAppendTo) {
|
249 | this.ghostElementAppendTo.appendChild(clone);
|
250 | }
|
251 | else {
|
252 | this.element.nativeElement.parentNode.insertBefore(clone, this.element.nativeElement.nextSibling);
|
253 | }
|
254 | this.ghostElement = clone;
|
255 | this.document.body.style.cursor = this.dragCursor;
|
256 | this.setElementStyles(clone, {
|
257 | position: 'fixed',
|
258 | top: `${rect.top}px`,
|
259 | left: `${rect.left}px`,
|
260 | width: `${rect.width}px`,
|
261 | height: `${rect.height}px`,
|
262 | cursor: this.dragCursor,
|
263 | margin: '0',
|
264 | willChange: 'transform',
|
265 | pointerEvents: 'none',
|
266 | });
|
267 | if (this.ghostElementTemplate) {
|
268 | const viewRef = this.vcr.createEmbeddedView(this.ghostElementTemplate);
|
269 | clone.innerHTML = '';
|
270 | viewRef.rootNodes
|
271 | .filter((node) => node instanceof Node)
|
272 | .forEach((node) => {
|
273 | clone.appendChild(node);
|
274 | });
|
275 | dragEnded$.subscribe(() => {
|
276 | this.vcr.remove(this.vcr.indexOf(viewRef));
|
277 | });
|
278 | }
|
279 | if (this.ghostElementCreated.observers.length > 0) {
|
280 | this.zone.run(() => {
|
281 | this.ghostElementCreated.emit({
|
282 | clientX: clientX - x,
|
283 | clientY: clientY - y,
|
284 | element: clone,
|
285 | });
|
286 | });
|
287 | }
|
288 | dragEnded$.subscribe(() => {
|
289 | clone.parentElement.removeChild(clone);
|
290 | this.ghostElement = null;
|
291 | this.renderer.setStyle(this.element.nativeElement, 'visibility', '');
|
292 | });
|
293 | }
|
294 | this.draggableHelper.currentDrag.next(currentDrag$);
|
295 | });
|
296 | dragEnded$
|
297 | .pipe(mergeMap((dragEndData) => {
|
298 | const dragEndData$ = cancelDrag$.pipe(count(), take(1), map((calledCount) => ({
|
299 | ...dragEndData,
|
300 | dragCancelled: calledCount > 0,
|
301 | })));
|
302 | cancelDrag$.complete();
|
303 | return dragEndData$;
|
304 | }))
|
305 | .subscribe(({ x, y, dragCancelled }) => {
|
306 | this.scroller.destroy();
|
307 | if (this.dragEnd.observers.length > 0) {
|
308 | this.zone.run(() => {
|
309 | this.dragEnd.next({ x, y, dragCancelled });
|
310 | });
|
311 | }
|
312 | removeClass(this.renderer, this.element, this.dragActiveClass);
|
313 | currentDrag$.complete();
|
314 | });
|
315 | merge(dragComplete$, dragEnded$)
|
316 | .pipe(take(1))
|
317 | .subscribe(() => {
|
318 | requestAnimationFrame(() => {
|
319 | this.document.head.removeChild(globalDragStyle);
|
320 | });
|
321 | });
|
322 | return pointerMove;
|
323 | }), share());
|
324 | merge(pointerDragged$.pipe(take(1), map((value) => [, value])), pointerDragged$.pipe(pairwise()))
|
325 | .pipe(filter(([previous, next]) => {
|
326 | if (!previous) {
|
327 | return true;
|
328 | }
|
329 | return previous.x !== next.x || previous.y !== next.y;
|
330 | }), map(([previous, next]) => next))
|
331 | .subscribe(({ x, y, currentDrag$, clientX, clientY, transformX, transformY, target, }) => {
|
332 | if (this.dragging.observers.length > 0) {
|
333 | this.zone.run(() => {
|
334 | this.dragging.next({ x, y });
|
335 | });
|
336 | }
|
337 | requestAnimationFrame(() => {
|
338 | if (this.ghostElement) {
|
339 | const transform = `translate3d(${transformX}px, ${transformY}px, 0px)`;
|
340 | this.setElementStyles(this.ghostElement, {
|
341 | transform,
|
342 | '-webkit-transform': transform,
|
343 | '-ms-transform': transform,
|
344 | '-moz-transform': transform,
|
345 | '-o-transform': transform,
|
346 | });
|
347 | }
|
348 | });
|
349 | currentDrag$.next({
|
350 | clientX,
|
351 | clientY,
|
352 | dropData: this.dropData,
|
353 | target,
|
354 | });
|
355 | });
|
356 | }
|
357 | ngOnChanges(changes) {
|
358 | if (changes.dragAxis) {
|
359 | this.checkEventListeners();
|
360 | }
|
361 | }
|
362 | ngOnDestroy() {
|
363 | this.unsubscribeEventListeners();
|
364 | this.pointerDown$.complete();
|
365 | this.pointerMove$.complete();
|
366 | this.pointerUp$.complete();
|
367 | this.destroy$.next();
|
368 | }
|
369 | checkEventListeners() {
|
370 | const canDrag = this.canDrag();
|
371 | const hasEventListeners = Object.keys(this.eventListenerSubscriptions).length > 0;
|
372 | if (canDrag && !hasEventListeners) {
|
373 | this.zone.runOutsideAngular(() => {
|
374 | this.eventListenerSubscriptions.mousedown = this.renderer.listen(this.element.nativeElement, 'mousedown', (event) => {
|
375 | this.onMouseDown(event);
|
376 | });
|
377 | this.eventListenerSubscriptions.mouseup = this.renderer.listen('document', 'mouseup', (event) => {
|
378 | this.onMouseUp(event);
|
379 | });
|
380 | this.eventListenerSubscriptions.touchstart = this.renderer.listen(this.element.nativeElement, 'touchstart', (event) => {
|
381 | this.onTouchStart(event);
|
382 | });
|
383 | this.eventListenerSubscriptions.touchend = this.renderer.listen('document', 'touchend', (event) => {
|
384 | this.onTouchEnd(event);
|
385 | });
|
386 | this.eventListenerSubscriptions.touchcancel = this.renderer.listen('document', 'touchcancel', (event) => {
|
387 | this.onTouchEnd(event);
|
388 | });
|
389 | this.eventListenerSubscriptions.mouseenter = this.renderer.listen(this.element.nativeElement, 'mouseenter', () => {
|
390 | this.onMouseEnter();
|
391 | });
|
392 | this.eventListenerSubscriptions.mouseleave = this.renderer.listen(this.element.nativeElement, 'mouseleave', () => {
|
393 | this.onMouseLeave();
|
394 | });
|
395 | });
|
396 | }
|
397 | else if (!canDrag && hasEventListeners) {
|
398 | this.unsubscribeEventListeners();
|
399 | }
|
400 | }
|
401 | onMouseDown(event) {
|
402 | if (event.button === 0) {
|
403 | if (!this.eventListenerSubscriptions.mousemove) {
|
404 | this.eventListenerSubscriptions.mousemove = this.renderer.listen('document', 'mousemove', (mouseMoveEvent) => {
|
405 | this.pointerMove$.next({
|
406 | event: mouseMoveEvent,
|
407 | clientX: mouseMoveEvent.clientX,
|
408 | clientY: mouseMoveEvent.clientY,
|
409 | });
|
410 | });
|
411 | }
|
412 | this.pointerDown$.next({
|
413 | event,
|
414 | clientX: event.clientX,
|
415 | clientY: event.clientY,
|
416 | });
|
417 | }
|
418 | }
|
419 | onMouseUp(event) {
|
420 | if (event.button === 0) {
|
421 | if (this.eventListenerSubscriptions.mousemove) {
|
422 | this.eventListenerSubscriptions.mousemove();
|
423 | delete this.eventListenerSubscriptions.mousemove;
|
424 | }
|
425 | this.pointerUp$.next({
|
426 | event,
|
427 | clientX: event.clientX,
|
428 | clientY: event.clientY,
|
429 | });
|
430 | }
|
431 | }
|
432 | onTouchStart(event) {
|
433 | let startScrollPosition;
|
434 | let isDragActivated;
|
435 | let hasContainerScrollbar;
|
436 | if (this.touchStartLongPress) {
|
437 | this.timeLongPress.timerBegin = Date.now();
|
438 | isDragActivated = false;
|
439 | hasContainerScrollbar = this.hasScrollbar();
|
440 | startScrollPosition = this.getScrollPosition();
|
441 | }
|
442 | if (!this.eventListenerSubscriptions.touchmove) {
|
443 | const contextMenuListener = fromEvent(this.document, 'contextmenu').subscribe((e) => {
|
444 | e.preventDefault();
|
445 | });
|
446 | const touchMoveListener = fromEvent(this.document, 'touchmove', {
|
447 | passive: false,
|
448 | }).subscribe((touchMoveEvent) => {
|
449 | if (this.touchStartLongPress &&
|
450 | !isDragActivated &&
|
451 | hasContainerScrollbar) {
|
452 | isDragActivated = this.shouldBeginDrag(event, touchMoveEvent, startScrollPosition);
|
453 | }
|
454 | if (!this.touchStartLongPress ||
|
455 | !hasContainerScrollbar ||
|
456 | isDragActivated) {
|
457 | touchMoveEvent.preventDefault();
|
458 | this.pointerMove$.next({
|
459 | event: touchMoveEvent,
|
460 | clientX: touchMoveEvent.targetTouches[0].clientX,
|
461 | clientY: touchMoveEvent.targetTouches[0].clientY,
|
462 | });
|
463 | }
|
464 | });
|
465 | this.eventListenerSubscriptions.touchmove = () => {
|
466 | contextMenuListener.unsubscribe();
|
467 | touchMoveListener.unsubscribe();
|
468 | };
|
469 | }
|
470 | this.pointerDown$.next({
|
471 | event,
|
472 | clientX: event.touches[0].clientX,
|
473 | clientY: event.touches[0].clientY,
|
474 | });
|
475 | }
|
476 | onTouchEnd(event) {
|
477 | if (this.eventListenerSubscriptions.touchmove) {
|
478 | this.eventListenerSubscriptions.touchmove();
|
479 | delete this.eventListenerSubscriptions.touchmove;
|
480 | if (this.touchStartLongPress) {
|
481 | this.enableScroll();
|
482 | }
|
483 | }
|
484 | this.pointerUp$.next({
|
485 | event,
|
486 | clientX: event.changedTouches[0].clientX,
|
487 | clientY: event.changedTouches[0].clientY,
|
488 | });
|
489 | }
|
490 | onMouseEnter() {
|
491 | this.setCursor(this.dragCursor);
|
492 | }
|
493 | onMouseLeave() {
|
494 | this.setCursor('');
|
495 | }
|
496 | canDrag() {
|
497 | return this.dragAxis.x || this.dragAxis.y;
|
498 | }
|
499 | setCursor(value) {
|
500 | if (!this.eventListenerSubscriptions.mousemove) {
|
501 | this.renderer.setStyle(this.element.nativeElement, 'cursor', value);
|
502 | }
|
503 | }
|
504 | unsubscribeEventListeners() {
|
505 | Object.keys(this.eventListenerSubscriptions).forEach((type) => {
|
506 | this.eventListenerSubscriptions[type]();
|
507 | delete this.eventListenerSubscriptions[type];
|
508 | });
|
509 | }
|
510 | setElementStyles(element, styles) {
|
511 | Object.keys(styles).forEach((key) => {
|
512 | this.renderer.setStyle(element, key, styles[key]);
|
513 | });
|
514 | }
|
515 | getScrollElement() {
|
516 | if (this.scrollContainer) {
|
517 | return this.scrollContainer.elementRef.nativeElement;
|
518 | }
|
519 | else {
|
520 | return this.document.body;
|
521 | }
|
522 | }
|
523 | getScrollPosition() {
|
524 | if (this.scrollContainer) {
|
525 | return {
|
526 | top: this.scrollContainer.elementRef.nativeElement.scrollTop,
|
527 | left: this.scrollContainer.elementRef.nativeElement.scrollLeft,
|
528 | };
|
529 | }
|
530 | else {
|
531 | return {
|
532 | top: window.pageYOffset || this.document.documentElement.scrollTop,
|
533 | left: window.pageXOffset || this.document.documentElement.scrollLeft,
|
534 | };
|
535 | }
|
536 | }
|
537 | shouldBeginDrag(event, touchMoveEvent, startScrollPosition) {
|
538 | const moveScrollPosition = this.getScrollPosition();
|
539 | const deltaScroll = {
|
540 | top: Math.abs(moveScrollPosition.top - startScrollPosition.top),
|
541 | left: Math.abs(moveScrollPosition.left - startScrollPosition.left),
|
542 | };
|
543 | const deltaX = Math.abs(touchMoveEvent.targetTouches[0].clientX - event.touches[0].clientX) - deltaScroll.left;
|
544 | const deltaY = Math.abs(touchMoveEvent.targetTouches[0].clientY - event.touches[0].clientY) - deltaScroll.top;
|
545 | const deltaTotal = deltaX + deltaY;
|
546 | const longPressConfig = this.touchStartLongPress;
|
547 | if (deltaTotal > longPressConfig.delta ||
|
548 | deltaScroll.top > 0 ||
|
549 | deltaScroll.left > 0) {
|
550 | this.timeLongPress.timerBegin = Date.now();
|
551 | }
|
552 | this.timeLongPress.timerEnd = Date.now();
|
553 | const duration = this.timeLongPress.timerEnd - this.timeLongPress.timerBegin;
|
554 | if (duration >= longPressConfig.delay) {
|
555 | this.disableScroll();
|
556 | return true;
|
557 | }
|
558 | return false;
|
559 | }
|
560 | enableScroll() {
|
561 | if (this.scrollContainer) {
|
562 | this.renderer.setStyle(this.scrollContainer.elementRef.nativeElement, 'overflow', '');
|
563 | }
|
564 | this.renderer.setStyle(this.document.body, 'overflow', '');
|
565 | }
|
566 | disableScroll() {
|
567 |
|
568 | if (this.scrollContainer) {
|
569 | this.renderer.setStyle(this.scrollContainer.elementRef.nativeElement, 'overflow', 'hidden');
|
570 | }
|
571 | this.renderer.setStyle(this.document.body, 'overflow', 'hidden');
|
572 | }
|
573 | hasScrollbar() {
|
574 | const scrollContainer = this.getScrollElement();
|
575 | const containerHasHorizontalScroll = scrollContainer.scrollWidth > scrollContainer.clientWidth;
|
576 | const containerHasVerticalScroll = scrollContainer.scrollHeight > scrollContainer.clientHeight;
|
577 | return containerHasHorizontalScroll || containerHasVerticalScroll;
|
578 | }
|
579 | }
|
580 | DraggableDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: DraggableDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: DraggableHelper }, { token: i0.NgZone }, { token: i0.ViewContainerRef }, { token: DraggableScrollContainerDirective, optional: true }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Directive });
|
581 | DraggableDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.3", type: DraggableDirective, selector: "[mwlDraggable]", inputs: { dropData: "dropData", dragAxis: "dragAxis", dragSnapGrid: "dragSnapGrid", ghostDragEnabled: "ghostDragEnabled", showOriginalElementWhileDragging: "showOriginalElementWhileDragging", validateDrag: "validateDrag", dragCursor: "dragCursor", dragActiveClass: "dragActiveClass", ghostElementAppendTo: "ghostElementAppendTo", ghostElementTemplate: "ghostElementTemplate", touchStartLongPress: "touchStartLongPress", autoScroll: "autoScroll" }, outputs: { dragPointerDown: "dragPointerDown", dragStart: "dragStart", ghostElementCreated: "ghostElementCreated", dragging: "dragging", dragEnd: "dragEnd" }, usesOnChanges: true, ngImport: i0 });
|
582 | i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: DraggableDirective, decorators: [{
|
583 | type: Directive,
|
584 | args: [{
|
585 | selector: '[mwlDraggable]',
|
586 | }]
|
587 | }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: DraggableHelper }, { type: i0.NgZone }, { type: i0.ViewContainerRef }, { type: DraggableScrollContainerDirective, decorators: [{
|
588 | type: Optional
|
589 | }] }, { type: undefined, decorators: [{
|
590 | type: Inject,
|
591 | args: [DOCUMENT]
|
592 | }] }]; }, propDecorators: { dropData: [{
|
593 | type: Input
|
594 | }], dragAxis: [{
|
595 | type: Input
|
596 | }], dragSnapGrid: [{
|
597 | type: Input
|
598 | }], ghostDragEnabled: [{
|
599 | type: Input
|
600 | }], showOriginalElementWhileDragging: [{
|
601 | type: Input
|
602 | }], validateDrag: [{
|
603 | type: Input
|
604 | }], dragCursor: [{
|
605 | type: Input
|
606 | }], dragActiveClass: [{
|
607 | type: Input
|
608 | }], ghostElementAppendTo: [{
|
609 | type: Input
|
610 | }], ghostElementTemplate: [{
|
611 | type: Input
|
612 | }], touchStartLongPress: [{
|
613 | type: Input
|
614 | }], autoScroll: [{
|
615 | type: Input
|
616 | }], dragPointerDown: [{
|
617 | type: Output
|
618 | }], dragStart: [{
|
619 | type: Output
|
620 | }], ghostElementCreated: [{
|
621 | type: Output
|
622 | }], dragging: [{
|
623 | type: Output
|
624 | }], dragEnd: [{
|
625 | type: Output
|
626 | }] } });
|
627 |
|
628 | function isCoordinateWithinRectangle(clientX, clientY, rect) {
|
629 | return (clientX >= rect.left &&
|
630 | clientX <= rect.right &&
|
631 | clientY >= rect.top &&
|
632 | clientY <= rect.bottom);
|
633 | }
|
634 | class DroppableDirective {
|
635 | constructor(element, draggableHelper, zone, renderer, scrollContainer) {
|
636 | this.element = element;
|
637 | this.draggableHelper = draggableHelper;
|
638 | this.zone = zone;
|
639 | this.renderer = renderer;
|
640 | this.scrollContainer = scrollContainer;
|
641 | |
642 |
|
643 |
|
644 | this.dragEnter = new EventEmitter();
|
645 | |
646 |
|
647 |
|
648 | this.dragLeave = new EventEmitter();
|
649 | |
650 |
|
651 |
|
652 | this.dragOver = new EventEmitter();
|
653 | |
654 |
|
655 |
|
656 | this.drop = new EventEmitter();
|
657 | }
|
658 | ngOnInit() {
|
659 | this.currentDragSubscription = this.draggableHelper.currentDrag.subscribe((drag$) => {
|
660 | addClass(this.renderer, this.element, this.dragActiveClass);
|
661 | const droppableElement = {
|
662 | updateCache: true,
|
663 | };
|
664 | const deregisterScrollListener = this.renderer.listen(this.scrollContainer
|
665 | ? this.scrollContainer.elementRef.nativeElement
|
666 | : 'window', 'scroll', () => {
|
667 | droppableElement.updateCache = true;
|
668 | });
|
669 | let currentDragEvent;
|
670 | const overlaps$ = drag$.pipe(map(({ clientX, clientY, dropData, target }) => {
|
671 | currentDragEvent = { clientX, clientY, dropData, target };
|
672 | if (droppableElement.updateCache) {
|
673 | droppableElement.rect =
|
674 | this.element.nativeElement.getBoundingClientRect();
|
675 | if (this.scrollContainer) {
|
676 | droppableElement.scrollContainerRect =
|
677 | this.scrollContainer.elementRef.nativeElement.getBoundingClientRect();
|
678 | }
|
679 | droppableElement.updateCache = false;
|
680 | }
|
681 | const isWithinElement = isCoordinateWithinRectangle(clientX, clientY, droppableElement.rect);
|
682 | const isDropAllowed = !this.validateDrop ||
|
683 | this.validateDrop({ clientX, clientY, target, dropData });
|
684 | if (droppableElement.scrollContainerRect) {
|
685 | return (isWithinElement &&
|
686 | isDropAllowed &&
|
687 | isCoordinateWithinRectangle(clientX, clientY, droppableElement.scrollContainerRect));
|
688 | }
|
689 | else {
|
690 | return isWithinElement && isDropAllowed;
|
691 | }
|
692 | }));
|
693 | const overlapsChanged$ = overlaps$.pipe(distinctUntilChanged());
|
694 | let dragOverActive;
|
695 | overlapsChanged$
|
696 | .pipe(filter((overlapsNow) => overlapsNow))
|
697 | .subscribe(() => {
|
698 | dragOverActive = true;
|
699 | addClass(this.renderer, this.element, this.dragOverClass);
|
700 | if (this.dragEnter.observers.length > 0) {
|
701 | this.zone.run(() => {
|
702 | this.dragEnter.next(currentDragEvent);
|
703 | });
|
704 | }
|
705 | });
|
706 | overlaps$.pipe(filter((overlapsNow) => overlapsNow)).subscribe(() => {
|
707 | if (this.dragOver.observers.length > 0) {
|
708 | this.zone.run(() => {
|
709 | this.dragOver.next(currentDragEvent);
|
710 | });
|
711 | }
|
712 | });
|
713 | overlapsChanged$
|
714 | .pipe(pairwise(), filter(([didOverlap, overlapsNow]) => didOverlap && !overlapsNow))
|
715 | .subscribe(() => {
|
716 | dragOverActive = false;
|
717 | removeClass(this.renderer, this.element, this.dragOverClass);
|
718 | if (this.dragLeave.observers.length > 0) {
|
719 | this.zone.run(() => {
|
720 | this.dragLeave.next(currentDragEvent);
|
721 | });
|
722 | }
|
723 | });
|
724 | drag$.subscribe({
|
725 | complete: () => {
|
726 | deregisterScrollListener();
|
727 | removeClass(this.renderer, this.element, this.dragActiveClass);
|
728 | if (dragOverActive) {
|
729 | removeClass(this.renderer, this.element, this.dragOverClass);
|
730 | if (this.drop.observers.length > 0) {
|
731 | this.zone.run(() => {
|
732 | this.drop.next(currentDragEvent);
|
733 | });
|
734 | }
|
735 | }
|
736 | },
|
737 | });
|
738 | });
|
739 | }
|
740 | ngOnDestroy() {
|
741 | if (this.currentDragSubscription) {
|
742 | this.currentDragSubscription.unsubscribe();
|
743 | }
|
744 | }
|
745 | }
|
746 | DroppableDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: DroppableDirective, deps: [{ token: i0.ElementRef }, { token: DraggableHelper }, { token: i0.NgZone }, { token: i0.Renderer2 }, { token: DraggableScrollContainerDirective, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
|
747 | DroppableDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.3", type: DroppableDirective, selector: "[mwlDroppable]", inputs: { dragOverClass: "dragOverClass", dragActiveClass: "dragActiveClass", validateDrop: "validateDrop" }, outputs: { dragEnter: "dragEnter", dragLeave: "dragLeave", dragOver: "dragOver", drop: "drop" }, ngImport: i0 });
|
748 | i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: DroppableDirective, decorators: [{
|
749 | type: Directive,
|
750 | args: [{
|
751 | selector: '[mwlDroppable]',
|
752 | }]
|
753 | }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: DraggableHelper }, { type: i0.NgZone }, { type: i0.Renderer2 }, { type: DraggableScrollContainerDirective, decorators: [{
|
754 | type: Optional
|
755 | }] }]; }, propDecorators: { dragOverClass: [{
|
756 | type: Input
|
757 | }], dragActiveClass: [{
|
758 | type: Input
|
759 | }], validateDrop: [{
|
760 | type: Input
|
761 | }], dragEnter: [{
|
762 | type: Output
|
763 | }], dragLeave: [{
|
764 | type: Output
|
765 | }], dragOver: [{
|
766 | type: Output
|
767 | }], drop: [{
|
768 | type: Output
|
769 | }] } });
|
770 |
|
771 | class DragAndDropModule {
|
772 | }
|
773 | DragAndDropModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: DragAndDropModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
774 | DragAndDropModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.0.3", ngImport: i0, type: DragAndDropModule, declarations: [DraggableDirective,
|
775 | DroppableDirective,
|
776 | DraggableScrollContainerDirective], exports: [DraggableDirective,
|
777 | DroppableDirective,
|
778 | DraggableScrollContainerDirective] });
|
779 | DragAndDropModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: DragAndDropModule });
|
780 | i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: DragAndDropModule, decorators: [{
|
781 | type: NgModule,
|
782 | args: [{
|
783 | declarations: [
|
784 | DraggableDirective,
|
785 | DroppableDirective,
|
786 | DraggableScrollContainerDirective,
|
787 | ],
|
788 | exports: [
|
789 | DraggableDirective,
|
790 | DroppableDirective,
|
791 | DraggableScrollContainerDirective,
|
792 | ],
|
793 | }]
|
794 | }] });
|
795 |
|
796 |
|
797 |
|
798 |
|
799 |
|
800 |
|
801 |
|
802 |
|
803 |
|
804 | export { DragAndDropModule, DraggableDirective, DraggableScrollContainerDirective, DroppableDirective };
|
805 |
|
806 |
|