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 Object.assign(Object.assign({}, moveData), { x: moveData.transformX + scrollX, y: moveData.transformY + scrollY });
|
213 | }), filter(({ x, y, transformX, transformY }) => !this.validateDrag ||
|
214 | this.validateDrag({
|
215 | x,
|
216 | y,
|
217 | transform: { x: transformX, y: transformY },
|
218 | })), takeUntil(dragComplete$), share());
|
219 | const dragStarted$ = pointerMove.pipe(take(1), share());
|
220 | const dragEnded$ = pointerMove.pipe(takeLast(1), share());
|
221 | dragStarted$.subscribe(({ clientX, clientY, x, y }) => {
|
222 | if (this.dragStart.observers.length > 0) {
|
223 | this.zone.run(() => {
|
224 | this.dragStart.next({ cancelDrag$ });
|
225 | });
|
226 | }
|
227 | this.scroller = autoScroll([
|
228 | this.scrollContainer
|
229 | ? this.scrollContainer.elementRef.nativeElement
|
230 | : this.document.defaultView,
|
231 | ], Object.assign(Object.assign({}, this.autoScroll), { autoScroll() {
|
232 | return true;
|
233 | } }));
|
234 | addClass(this.renderer, this.element, this.dragActiveClass);
|
235 | if (this.ghostDragEnabled) {
|
236 | const rect = this.element.nativeElement.getBoundingClientRect();
|
237 | const clone = this.element.nativeElement.cloneNode(true);
|
238 | if (!this.showOriginalElementWhileDragging) {
|
239 | this.renderer.setStyle(this.element.nativeElement, 'visibility', 'hidden');
|
240 | }
|
241 | if (this.ghostElementAppendTo) {
|
242 | this.ghostElementAppendTo.appendChild(clone);
|
243 | }
|
244 | else {
|
245 | this.element.nativeElement.parentNode.insertBefore(clone, this.element.nativeElement.nextSibling);
|
246 | }
|
247 | this.ghostElement = clone;
|
248 | this.document.body.style.cursor = this.dragCursor;
|
249 | this.setElementStyles(clone, {
|
250 | position: 'fixed',
|
251 | top: `${rect.top}px`,
|
252 | left: `${rect.left}px`,
|
253 | width: `${rect.width}px`,
|
254 | height: `${rect.height}px`,
|
255 | cursor: this.dragCursor,
|
256 | margin: '0',
|
257 | willChange: 'transform',
|
258 | pointerEvents: 'none',
|
259 | });
|
260 | if (this.ghostElementTemplate) {
|
261 | const viewRef = this.vcr.createEmbeddedView(this.ghostElementTemplate);
|
262 | clone.innerHTML = '';
|
263 | viewRef.rootNodes
|
264 | .filter((node) => node instanceof Node)
|
265 | .forEach((node) => {
|
266 | clone.appendChild(node);
|
267 | });
|
268 | dragEnded$.subscribe(() => {
|
269 | this.vcr.remove(this.vcr.indexOf(viewRef));
|
270 | });
|
271 | }
|
272 | if (this.ghostElementCreated.observers.length > 0) {
|
273 | this.zone.run(() => {
|
274 | this.ghostElementCreated.emit({
|
275 | clientX: clientX - x,
|
276 | clientY: clientY - y,
|
277 | element: clone,
|
278 | });
|
279 | });
|
280 | }
|
281 | dragEnded$.subscribe(() => {
|
282 | clone.parentElement.removeChild(clone);
|
283 | this.ghostElement = null;
|
284 | this.renderer.setStyle(this.element.nativeElement, 'visibility', '');
|
285 | });
|
286 | }
|
287 | this.draggableHelper.currentDrag.next(currentDrag$);
|
288 | });
|
289 | dragEnded$
|
290 | .pipe(mergeMap((dragEndData) => {
|
291 | const dragEndData$ = cancelDrag$.pipe(count(), take(1), map((calledCount) => (Object.assign(Object.assign({}, dragEndData), { dragCancelled: calledCount > 0 }))));
|
292 | cancelDrag$.complete();
|
293 | return dragEndData$;
|
294 | }))
|
295 | .subscribe(({ x, y, dragCancelled }) => {
|
296 | this.scroller.destroy();
|
297 | if (this.dragEnd.observers.length > 0) {
|
298 | this.zone.run(() => {
|
299 | this.dragEnd.next({ x, y, dragCancelled });
|
300 | });
|
301 | }
|
302 | removeClass(this.renderer, this.element, this.dragActiveClass);
|
303 | currentDrag$.complete();
|
304 | });
|
305 | merge(dragComplete$, dragEnded$)
|
306 | .pipe(take(1))
|
307 | .subscribe(() => {
|
308 | requestAnimationFrame(() => {
|
309 | this.document.head.removeChild(globalDragStyle);
|
310 | });
|
311 | });
|
312 | return pointerMove;
|
313 | }), share());
|
314 | merge(pointerDragged$.pipe(take(1), map((value) => [, value])), pointerDragged$.pipe(pairwise()))
|
315 | .pipe(filter(([previous, next]) => {
|
316 | if (!previous) {
|
317 | return true;
|
318 | }
|
319 | return previous.x !== next.x || previous.y !== next.y;
|
320 | }), map(([previous, next]) => next))
|
321 | .subscribe(({ x, y, currentDrag$, clientX, clientY, transformX, transformY, target, }) => {
|
322 | if (this.dragging.observers.length > 0) {
|
323 | this.zone.run(() => {
|
324 | this.dragging.next({ x, y });
|
325 | });
|
326 | }
|
327 | requestAnimationFrame(() => {
|
328 | if (this.ghostElement) {
|
329 | const transform = `translate3d(${transformX}px, ${transformY}px, 0px)`;
|
330 | this.setElementStyles(this.ghostElement, {
|
331 | transform,
|
332 | '-webkit-transform': transform,
|
333 | '-ms-transform': transform,
|
334 | '-moz-transform': transform,
|
335 | '-o-transform': transform,
|
336 | });
|
337 | }
|
338 | });
|
339 | currentDrag$.next({
|
340 | clientX,
|
341 | clientY,
|
342 | dropData: this.dropData,
|
343 | target,
|
344 | });
|
345 | });
|
346 | }
|
347 | ngOnChanges(changes) {
|
348 | if (changes.dragAxis) {
|
349 | this.checkEventListeners();
|
350 | }
|
351 | }
|
352 | ngOnDestroy() {
|
353 | this.unsubscribeEventListeners();
|
354 | this.pointerDown$.complete();
|
355 | this.pointerMove$.complete();
|
356 | this.pointerUp$.complete();
|
357 | this.destroy$.next();
|
358 | }
|
359 | checkEventListeners() {
|
360 | const canDrag = this.canDrag();
|
361 | const hasEventListeners = Object.keys(this.eventListenerSubscriptions).length > 0;
|
362 | if (canDrag && !hasEventListeners) {
|
363 | this.zone.runOutsideAngular(() => {
|
364 | this.eventListenerSubscriptions.mousedown = this.renderer.listen(this.element.nativeElement, 'mousedown', (event) => {
|
365 | this.onMouseDown(event);
|
366 | });
|
367 | this.eventListenerSubscriptions.mouseup = this.renderer.listen('document', 'mouseup', (event) => {
|
368 | this.onMouseUp(event);
|
369 | });
|
370 | this.eventListenerSubscriptions.touchstart = this.renderer.listen(this.element.nativeElement, 'touchstart', (event) => {
|
371 | this.onTouchStart(event);
|
372 | });
|
373 | this.eventListenerSubscriptions.touchend = this.renderer.listen('document', 'touchend', (event) => {
|
374 | this.onTouchEnd(event);
|
375 | });
|
376 | this.eventListenerSubscriptions.touchcancel = this.renderer.listen('document', 'touchcancel', (event) => {
|
377 | this.onTouchEnd(event);
|
378 | });
|
379 | this.eventListenerSubscriptions.mouseenter = this.renderer.listen(this.element.nativeElement, 'mouseenter', () => {
|
380 | this.onMouseEnter();
|
381 | });
|
382 | this.eventListenerSubscriptions.mouseleave = this.renderer.listen(this.element.nativeElement, 'mouseleave', () => {
|
383 | this.onMouseLeave();
|
384 | });
|
385 | });
|
386 | }
|
387 | else if (!canDrag && hasEventListeners) {
|
388 | this.unsubscribeEventListeners();
|
389 | }
|
390 | }
|
391 | onMouseDown(event) {
|
392 | if (event.button === 0) {
|
393 | if (!this.eventListenerSubscriptions.mousemove) {
|
394 | this.eventListenerSubscriptions.mousemove = this.renderer.listen('document', 'mousemove', (mouseMoveEvent) => {
|
395 | this.pointerMove$.next({
|
396 | event: mouseMoveEvent,
|
397 | clientX: mouseMoveEvent.clientX,
|
398 | clientY: mouseMoveEvent.clientY,
|
399 | });
|
400 | });
|
401 | }
|
402 | this.pointerDown$.next({
|
403 | event,
|
404 | clientX: event.clientX,
|
405 | clientY: event.clientY,
|
406 | });
|
407 | }
|
408 | }
|
409 | onMouseUp(event) {
|
410 | if (event.button === 0) {
|
411 | if (this.eventListenerSubscriptions.mousemove) {
|
412 | this.eventListenerSubscriptions.mousemove();
|
413 | delete this.eventListenerSubscriptions.mousemove;
|
414 | }
|
415 | this.pointerUp$.next({
|
416 | event,
|
417 | clientX: event.clientX,
|
418 | clientY: event.clientY,
|
419 | });
|
420 | }
|
421 | }
|
422 | onTouchStart(event) {
|
423 | let startScrollPosition;
|
424 | let isDragActivated;
|
425 | let hasContainerScrollbar;
|
426 | if (this.touchStartLongPress) {
|
427 | this.timeLongPress.timerBegin = Date.now();
|
428 | isDragActivated = false;
|
429 | hasContainerScrollbar = this.hasScrollbar();
|
430 | startScrollPosition = this.getScrollPosition();
|
431 | }
|
432 | if (!this.eventListenerSubscriptions.touchmove) {
|
433 | const contextMenuListener = fromEvent(this.document, 'contextmenu').subscribe((e) => {
|
434 | e.preventDefault();
|
435 | });
|
436 | const touchMoveListener = fromEvent(this.document, 'touchmove', {
|
437 | passive: false,
|
438 | }).subscribe((touchMoveEvent) => {
|
439 | if (this.touchStartLongPress &&
|
440 | !isDragActivated &&
|
441 | hasContainerScrollbar) {
|
442 | isDragActivated = this.shouldBeginDrag(event, touchMoveEvent, startScrollPosition);
|
443 | }
|
444 | if (!this.touchStartLongPress ||
|
445 | !hasContainerScrollbar ||
|
446 | isDragActivated) {
|
447 | touchMoveEvent.preventDefault();
|
448 | this.pointerMove$.next({
|
449 | event: touchMoveEvent,
|
450 | clientX: touchMoveEvent.targetTouches[0].clientX,
|
451 | clientY: touchMoveEvent.targetTouches[0].clientY,
|
452 | });
|
453 | }
|
454 | });
|
455 | this.eventListenerSubscriptions.touchmove = () => {
|
456 | contextMenuListener.unsubscribe();
|
457 | touchMoveListener.unsubscribe();
|
458 | };
|
459 | }
|
460 | this.pointerDown$.next({
|
461 | event,
|
462 | clientX: event.touches[0].clientX,
|
463 | clientY: event.touches[0].clientY,
|
464 | });
|
465 | }
|
466 | onTouchEnd(event) {
|
467 | if (this.eventListenerSubscriptions.touchmove) {
|
468 | this.eventListenerSubscriptions.touchmove();
|
469 | delete this.eventListenerSubscriptions.touchmove;
|
470 | if (this.touchStartLongPress) {
|
471 | this.enableScroll();
|
472 | }
|
473 | }
|
474 | this.pointerUp$.next({
|
475 | event,
|
476 | clientX: event.changedTouches[0].clientX,
|
477 | clientY: event.changedTouches[0].clientY,
|
478 | });
|
479 | }
|
480 | onMouseEnter() {
|
481 | this.setCursor(this.dragCursor);
|
482 | }
|
483 | onMouseLeave() {
|
484 | this.setCursor('');
|
485 | }
|
486 | canDrag() {
|
487 | return this.dragAxis.x || this.dragAxis.y;
|
488 | }
|
489 | setCursor(value) {
|
490 | if (!this.eventListenerSubscriptions.mousemove) {
|
491 | this.renderer.setStyle(this.element.nativeElement, 'cursor', value);
|
492 | }
|
493 | }
|
494 | unsubscribeEventListeners() {
|
495 | Object.keys(this.eventListenerSubscriptions).forEach((type) => {
|
496 | this.eventListenerSubscriptions[type]();
|
497 | delete this.eventListenerSubscriptions[type];
|
498 | });
|
499 | }
|
500 | setElementStyles(element, styles) {
|
501 | Object.keys(styles).forEach((key) => {
|
502 | this.renderer.setStyle(element, key, styles[key]);
|
503 | });
|
504 | }
|
505 | getScrollElement() {
|
506 | if (this.scrollContainer) {
|
507 | return this.scrollContainer.elementRef.nativeElement;
|
508 | }
|
509 | else {
|
510 | return this.document.body;
|
511 | }
|
512 | }
|
513 | getScrollPosition() {
|
514 | if (this.scrollContainer) {
|
515 | return {
|
516 | top: this.scrollContainer.elementRef.nativeElement.scrollTop,
|
517 | left: this.scrollContainer.elementRef.nativeElement.scrollLeft,
|
518 | };
|
519 | }
|
520 | else {
|
521 | return {
|
522 | top: window.pageYOffset || this.document.documentElement.scrollTop,
|
523 | left: window.pageXOffset || this.document.documentElement.scrollLeft,
|
524 | };
|
525 | }
|
526 | }
|
527 | shouldBeginDrag(event, touchMoveEvent, startScrollPosition) {
|
528 | const moveScrollPosition = this.getScrollPosition();
|
529 | const deltaScroll = {
|
530 | top: Math.abs(moveScrollPosition.top - startScrollPosition.top),
|
531 | left: Math.abs(moveScrollPosition.left - startScrollPosition.left),
|
532 | };
|
533 | const deltaX = Math.abs(touchMoveEvent.targetTouches[0].clientX - event.touches[0].clientX) - deltaScroll.left;
|
534 | const deltaY = Math.abs(touchMoveEvent.targetTouches[0].clientY - event.touches[0].clientY) - deltaScroll.top;
|
535 | const deltaTotal = deltaX + deltaY;
|
536 | const longPressConfig = this.touchStartLongPress;
|
537 | if (deltaTotal > longPressConfig.delta ||
|
538 | deltaScroll.top > 0 ||
|
539 | deltaScroll.left > 0) {
|
540 | this.timeLongPress.timerBegin = Date.now();
|
541 | }
|
542 | this.timeLongPress.timerEnd = Date.now();
|
543 | const duration = this.timeLongPress.timerEnd - this.timeLongPress.timerBegin;
|
544 | if (duration >= longPressConfig.delay) {
|
545 | this.disableScroll();
|
546 | return true;
|
547 | }
|
548 | return false;
|
549 | }
|
550 | enableScroll() {
|
551 | if (this.scrollContainer) {
|
552 | this.renderer.setStyle(this.scrollContainer.elementRef.nativeElement, 'overflow', '');
|
553 | }
|
554 | this.renderer.setStyle(this.document.body, 'overflow', '');
|
555 | }
|
556 | disableScroll() {
|
557 |
|
558 | if (this.scrollContainer) {
|
559 | this.renderer.setStyle(this.scrollContainer.elementRef.nativeElement, 'overflow', 'hidden');
|
560 | }
|
561 | this.renderer.setStyle(this.document.body, 'overflow', 'hidden');
|
562 | }
|
563 | hasScrollbar() {
|
564 | const scrollContainer = this.getScrollElement();
|
565 | const containerHasHorizontalScroll = scrollContainer.scrollWidth > scrollContainer.clientWidth;
|
566 | const containerHasVerticalScroll = scrollContainer.scrollHeight > scrollContainer.clientHeight;
|
567 | return containerHasHorizontalScroll || containerHasVerticalScroll;
|
568 | }
|
569 | }
|
570 | 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 });
|
571 | 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 });
|
572 | i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: DraggableDirective, decorators: [{
|
573 | type: Directive,
|
574 | args: [{
|
575 | selector: '[mwlDraggable]',
|
576 | }]
|
577 | }], ctorParameters: function () {
|
578 | return [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: DraggableHelper }, { type: i0.NgZone }, { type: i0.ViewContainerRef }, { type: DraggableScrollContainerDirective, decorators: [{
|
579 | type: Optional
|
580 | }] }, { type: undefined, decorators: [{
|
581 | type: Inject,
|
582 | args: [DOCUMENT]
|
583 | }] }];
|
584 | }, propDecorators: { dropData: [{
|
585 | type: Input
|
586 | }], dragAxis: [{
|
587 | type: Input
|
588 | }], dragSnapGrid: [{
|
589 | type: Input
|
590 | }], ghostDragEnabled: [{
|
591 | type: Input
|
592 | }], showOriginalElementWhileDragging: [{
|
593 | type: Input
|
594 | }], validateDrag: [{
|
595 | type: Input
|
596 | }], dragCursor: [{
|
597 | type: Input
|
598 | }], dragActiveClass: [{
|
599 | type: Input
|
600 | }], ghostElementAppendTo: [{
|
601 | type: Input
|
602 | }], ghostElementTemplate: [{
|
603 | type: Input
|
604 | }], touchStartLongPress: [{
|
605 | type: Input
|
606 | }], autoScroll: [{
|
607 | type: Input
|
608 | }], dragPointerDown: [{
|
609 | type: Output
|
610 | }], dragStart: [{
|
611 | type: Output
|
612 | }], ghostElementCreated: [{
|
613 | type: Output
|
614 | }], dragging: [{
|
615 | type: Output
|
616 | }], dragEnd: [{
|
617 | type: Output
|
618 | }] } });
|
619 |
|
620 | function isCoordinateWithinRectangle(clientX, clientY, rect) {
|
621 | return (clientX >= rect.left &&
|
622 | clientX <= rect.right &&
|
623 | clientY >= rect.top &&
|
624 | clientY <= rect.bottom);
|
625 | }
|
626 | class DroppableDirective {
|
627 | constructor(element, draggableHelper, zone, renderer, scrollContainer) {
|
628 | this.element = element;
|
629 | this.draggableHelper = draggableHelper;
|
630 | this.zone = zone;
|
631 | this.renderer = renderer;
|
632 | this.scrollContainer = scrollContainer;
|
633 | |
634 |
|
635 |
|
636 | this.dragEnter = new EventEmitter();
|
637 | |
638 |
|
639 |
|
640 | this.dragLeave = new EventEmitter();
|
641 | |
642 |
|
643 |
|
644 | this.dragOver = new EventEmitter();
|
645 | |
646 |
|
647 |
|
648 | this.drop = new EventEmitter();
|
649 | }
|
650 | ngOnInit() {
|
651 | this.currentDragSubscription = this.draggableHelper.currentDrag.subscribe((drag$) => {
|
652 | addClass(this.renderer, this.element, this.dragActiveClass);
|
653 | const droppableElement = {
|
654 | updateCache: true,
|
655 | };
|
656 | const deregisterScrollListener = this.renderer.listen(this.scrollContainer
|
657 | ? this.scrollContainer.elementRef.nativeElement
|
658 | : 'window', 'scroll', () => {
|
659 | droppableElement.updateCache = true;
|
660 | });
|
661 | let currentDragEvent;
|
662 | const overlaps$ = drag$.pipe(map(({ clientX, clientY, dropData, target }) => {
|
663 | currentDragEvent = { clientX, clientY, dropData, target };
|
664 | if (droppableElement.updateCache) {
|
665 | droppableElement.rect =
|
666 | this.element.nativeElement.getBoundingClientRect();
|
667 | if (this.scrollContainer) {
|
668 | droppableElement.scrollContainerRect =
|
669 | this.scrollContainer.elementRef.nativeElement.getBoundingClientRect();
|
670 | }
|
671 | droppableElement.updateCache = false;
|
672 | }
|
673 | const isWithinElement = isCoordinateWithinRectangle(clientX, clientY, droppableElement.rect);
|
674 | const isDropAllowed = !this.validateDrop ||
|
675 | this.validateDrop({ clientX, clientY, target, dropData });
|
676 | if (droppableElement.scrollContainerRect) {
|
677 | return (isWithinElement &&
|
678 | isDropAllowed &&
|
679 | isCoordinateWithinRectangle(clientX, clientY, droppableElement.scrollContainerRect));
|
680 | }
|
681 | else {
|
682 | return isWithinElement && isDropAllowed;
|
683 | }
|
684 | }));
|
685 | const overlapsChanged$ = overlaps$.pipe(distinctUntilChanged());
|
686 | let dragOverActive;
|
687 | overlapsChanged$
|
688 | .pipe(filter((overlapsNow) => overlapsNow))
|
689 | .subscribe(() => {
|
690 | dragOverActive = true;
|
691 | addClass(this.renderer, this.element, this.dragOverClass);
|
692 | if (this.dragEnter.observers.length > 0) {
|
693 | this.zone.run(() => {
|
694 | this.dragEnter.next(currentDragEvent);
|
695 | });
|
696 | }
|
697 | });
|
698 | overlaps$.pipe(filter((overlapsNow) => overlapsNow)).subscribe(() => {
|
699 | if (this.dragOver.observers.length > 0) {
|
700 | this.zone.run(() => {
|
701 | this.dragOver.next(currentDragEvent);
|
702 | });
|
703 | }
|
704 | });
|
705 | overlapsChanged$
|
706 | .pipe(pairwise(), filter(([didOverlap, overlapsNow]) => didOverlap && !overlapsNow))
|
707 | .subscribe(() => {
|
708 | dragOverActive = false;
|
709 | removeClass(this.renderer, this.element, this.dragOverClass);
|
710 | if (this.dragLeave.observers.length > 0) {
|
711 | this.zone.run(() => {
|
712 | this.dragLeave.next(currentDragEvent);
|
713 | });
|
714 | }
|
715 | });
|
716 | drag$.subscribe({
|
717 | complete: () => {
|
718 | deregisterScrollListener();
|
719 | removeClass(this.renderer, this.element, this.dragActiveClass);
|
720 | if (dragOverActive) {
|
721 | removeClass(this.renderer, this.element, this.dragOverClass);
|
722 | if (this.drop.observers.length > 0) {
|
723 | this.zone.run(() => {
|
724 | this.drop.next(currentDragEvent);
|
725 | });
|
726 | }
|
727 | }
|
728 | },
|
729 | });
|
730 | });
|
731 | }
|
732 | ngOnDestroy() {
|
733 | if (this.currentDragSubscription) {
|
734 | this.currentDragSubscription.unsubscribe();
|
735 | }
|
736 | }
|
737 | }
|
738 | 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 });
|
739 | 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 });
|
740 | i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: DroppableDirective, decorators: [{
|
741 | type: Directive,
|
742 | args: [{
|
743 | selector: '[mwlDroppable]',
|
744 | }]
|
745 | }], ctorParameters: function () {
|
746 | return [{ type: i0.ElementRef }, { type: DraggableHelper }, { type: i0.NgZone }, { type: i0.Renderer2 }, { type: DraggableScrollContainerDirective, decorators: [{
|
747 | type: Optional
|
748 | }] }];
|
749 | }, propDecorators: { dragOverClass: [{
|
750 | type: Input
|
751 | }], dragActiveClass: [{
|
752 | type: Input
|
753 | }], validateDrop: [{
|
754 | type: Input
|
755 | }], dragEnter: [{
|
756 | type: Output
|
757 | }], dragLeave: [{
|
758 | type: Output
|
759 | }], dragOver: [{
|
760 | type: Output
|
761 | }], drop: [{
|
762 | type: Output
|
763 | }] } });
|
764 |
|
765 | class DragAndDropModule {
|
766 | }
|
767 | DragAndDropModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: DragAndDropModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
768 | DragAndDropModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.0.3", ngImport: i0, type: DragAndDropModule, declarations: [DraggableDirective,
|
769 | DroppableDirective,
|
770 | DraggableScrollContainerDirective], exports: [DraggableDirective,
|
771 | DroppableDirective,
|
772 | DraggableScrollContainerDirective] });
|
773 | DragAndDropModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: DragAndDropModule });
|
774 | i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: DragAndDropModule, decorators: [{
|
775 | type: NgModule,
|
776 | args: [{
|
777 | declarations: [
|
778 | DraggableDirective,
|
779 | DroppableDirective,
|
780 | DraggableScrollContainerDirective,
|
781 | ],
|
782 | exports: [
|
783 | DraggableDirective,
|
784 | DroppableDirective,
|
785 | DraggableScrollContainerDirective,
|
786 | ],
|
787 | }]
|
788 | }] });
|
789 |
|
790 |
|
791 |
|
792 |
|
793 |
|
794 |
|
795 |
|
796 |
|
797 |
|
798 | export { DragAndDropModule, DraggableDirective, DraggableScrollContainerDirective, DroppableDirective };
|
799 |
|
800 |
|