@@ -21,7 +21,7 @@ import {
2121import { coerceNumberProperty , NumberInput } from './coercion/number-property' ;
2222import { KtdGridItemComponent } from './grid-item/grid-item.component' ;
2323import { combineLatest , merge , NEVER , Observable , of , Subscription } from 'rxjs' ;
24- import { exhaustMap , map , startWith , switchMap , takeUntil } from 'rxjs/operators' ;
24+ import { exhaustMap , map , startWith , switchMap , takeUntil } from 'rxjs/operators' ;
2525import {
2626 ktdGetGridItemRowHeight ,
2727 ktdGridItemDragging , ktdGridItemLayoutItemAreEqual ,
@@ -40,7 +40,7 @@ import {
4040} from './grid.definitions' ;
4141import { ktdPointerClientX , ktdPointerClientY } from './utils/pointer.utils' ;
4242import { KtdDictionary } from '../types' ;
43- import { KtdGridService } from './grid.service' ;
43+ import { KtdGridService , PointerEventInfo } from './grid.service' ;
4444import { getMutableClientRect , KtdClientRect } from './utils/client-rect' ;
4545import { ktdGetScrollTotalRelativeDifference$ , ktdScrollIfNearElementClientRect$ } from './utils/scroll' ;
4646import { BooleanInput , coerceBooleanProperty } from './coercion/boolean-property' ;
@@ -50,6 +50,7 @@ import {KtdRegistryService} from "./ktd-registry.service";
5050import { DragRef } from "./utils/drag-ref" ;
5151import { KtdDrag } from "./directives/ktd-drag" ;
5252
53+ // region Types
5354
5455interface KtdGridDrag {
5556 dragSubscription : Subscription ;
@@ -178,6 +179,8 @@ const defaultBackgroundConfig: Required<Omit<KtdGridBackgroundCfg, 'show'>> = {
178179 borderWidth : 1 ,
179180} ;
180181
182+ // endregion
183+
181184@Component ( {
182185 selector : 'ktd-grid' ,
183186 templateUrl : './grid.component.html' ,
@@ -193,6 +196,7 @@ const defaultBackgroundConfig: Required<Omit<KtdGridBackgroundCfg, 'show'>> = {
193196 ]
194197} )
195198export class KtdGridComponent implements OnChanges , AfterContentInit , AfterContentChecked , OnDestroy {
199+ // region Parameters
196200 private static _nextUniqueId : number = 0 ;
197201
198202 /** Query list of grid items that are being rendered. */
@@ -368,6 +372,8 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
368372
369373 private readonly gridElement : HTMLElement ;
370374
375+ // endregion
376+
371377 constructor ( private gridService : KtdGridService ,
372378 private ktdRegistryService : KtdRegistryService ,
373379 private elementRef : ElementRef ,
@@ -499,7 +505,7 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
499505 }
500506
501507 private initSubscriptions ( ) {
502- const connectedToItems $ = this . ktdRegistryService . getKtdDragItemsConnectedToGrid ( this ) ;
508+ const itemsConnectedToGrid $ = this . ktdRegistryService . getKtdDragItemsConnectedToGrid ( this ) ;
503509 this . subscriptions = [
504510 this . _gridItems . changes . pipe (
505511 startWith ( this . _gridItems ) ,
@@ -533,8 +539,8 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
533539 return this . gridService . startDrag ( event , gridItem . dragRef , type , this ) ;
534540 } ) ,
535541
536- connectedToItems $. pipe (
537- startWith ( connectedToItems $. value ) ,
542+ itemsConnectedToGrid $. pipe (
543+ startWith ( itemsConnectedToGrid $. value ) ,
538544 switchMap ( ( draggableItems ) => {
539545 return merge (
540546 ...draggableItems . map ( ( draggableItem ) => draggableItem . dragStart . pipe (
@@ -547,27 +553,94 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
547553 } ) ,
548554
549555 this . dragEntered . subscribe ( ( { event, } ) => {
550- this . startDragSequenceOld ( event ) ;
556+ this . startRestoreDragSequence ( event ) ;
551557 } ) ,
552558 this . dragExited . subscribe ( ( ) => {
553- this . stopDragSequence ( ) ;
559+ this . pauseDragSequence ( ) ;
560+ } ) ,
561+ this . gridService . pointerBeforeEnd$ . subscribe ( ( { dragInfo} ) => {
562+ if ( this . drag !== null && dragInfo !== null ) {
563+ this . updateLayout ( dragInfo ) ;
564+ this . stopDragSequence ( dragInfo ) ;
565+ }
566+ console . log ( this . drag , dragInfo ) ;
554567 } ) ,
555568 this . gridService . pointerEnd$ . subscribe ( ( ) => {
556- this . stopDragSequence ( ) ;
557- } )
569+ this . drag = null ;
570+ } ) ,
558571 ] ;
559572 }
560573
561- private startDragSequenceOld ( event : PointingDeviceEvent ) : void {
574+ /**
575+ * Starts the drag sequence when a drag event is triggered. It will restore paused drag sequence if it's already started.
576+ * @param event The event that triggered the drag sequence.
577+ */
578+ private startRestoreDragSequence ( event : PointingDeviceEvent ) : void {
562579 const dragInfo = this . gridService . drag ! ;
580+ const scrollableParent = typeof this . scrollableParent === 'string' ? document . getElementById ( this . scrollableParent ) : this . scrollableParent ;
581+
582+ // TODO (enhancement): consider move this 'side effect' observable inside the main drag loop.
583+ // - Pros are that we would not repeat subscriptions and takeUntil would shut down observables at the same time.
584+ // - Cons are that moving this functionality as a side effect inside the main drag loop would be confusing.
585+ const scrollSubscription = this . ngZone . runOutsideAngular ( ( ) =>
586+ ( ! scrollableParent ? NEVER : this . gridService . pointerMove$ . pipe (
587+ map ( ( event ) => ( {
588+ pointerX : ktdPointerClientX ( event ) ,
589+ pointerY : ktdPointerClientY ( event )
590+ } ) ) ,
591+ ktdScrollIfNearElementClientRect$ ( scrollableParent , { scrollStep : this . scrollSpeed } )
592+ ) ) . pipe (
593+ takeUntil ( this . gridService . pointerEnd$ ) ,
594+ ) . subscribe ( ) ) ;
595+
596+ if ( this . drag != null ) {
597+ this . drag . dragSubscription = this . createDragResizeLoop ( scrollableParent , dragInfo ) ;
598+ this . drag . scrollSubscription = scrollSubscription ;
599+ return ;
600+ }
601+
602+ this . drag = {
603+ dragSubscription : this . createDragResizeLoop ( scrollableParent , dragInfo ) ,
604+ scrollSubscription,
605+ startEvent : event ,
606+ newLayout : null ,
607+ newLayoutItem : dragInfo . dragRef . itemRef instanceof KtdDrag ? {
608+ id : dragInfo . dragRef . id ,
609+ w : 1 ,
610+ h : 1 ,
611+ x : - 1 ,
612+ y : - 1 ,
613+ } : null ,
614+ } ;
615+ }
616+
617+ private pauseDragSequence ( ) : void {
618+ const dragInfo = this . gridService . drag ! ;
619+
620+ // If the drag is a resize, we don't need to pause the drag sequence.
621+ if ( dragInfo . type === 'resize' ) {
622+ return ;
623+ }
624+
625+ if ( this . drag != null ) {
626+ this . drag . dragSubscription . unsubscribe ( ) ;
627+ this . drag . scrollSubscription . unsubscribe ( ) ;
628+ this . destroyPlaceholder ( ) ;
629+ }
630+ }
631+
632+ /**
633+ * Creates the drag loop. It listens for 'pointer move' and 'scroll' events and recalculates the layout on each emission.
634+ * @param scrollableParent The parent element that contains the scroll.
635+ * @param dragInfo The drag info.
636+ */
637+ private createDragResizeLoop ( scrollableParent : HTMLElement | Document | null , dragInfo : PointerEventInfo ) : Subscription {
563638 let renderData : KtdGridItemRenderData < number > | null = null ;
564639
565640 // Retrieve grid (parent) and gridItem (draggedElem) client rects.
566641 const gridElemClientRect : KtdClientRect = getMutableClientRect ( this . gridElement ) ;
567642 const dragElemClientRect : KtdClientRect = getMutableClientRect ( dragInfo . dragRef . elementRef . nativeElement as HTMLElement ) ;
568643
569- const scrollableParent = typeof this . scrollableParent === 'string' ? document . getElementById ( this . scrollableParent ) : this . scrollableParent ;
570-
571644 this . renderer . addClass ( dragInfo . dragRef . elementRef . nativeElement , 'no-transitions' ) ;
572645 this . renderer . addClass ( dragInfo . dragRef . elementRef . nativeElement , 'ktd-grid-item-dragging' ) ;
573646
@@ -579,22 +652,8 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
579652
580653 this . createPlaceholderElement ( placeholderClientRect , dragInfo . dragRef . placeholder ) ;
581654
582- // TODO (enhancement): consider move this 'side effect' observable inside the main drag loop.
583- // - Pros are that we would not repeat subscriptions and takeUntil would shut down observables at the same time.
584- // - Cons are that moving this functionality as a side effect inside the main drag loop would be confusing.
585- const scrollSubscription = this . ngZone . runOutsideAngular ( ( ) =>
586- ( ! scrollableParent ? NEVER : this . gridService . pointerMove$ . pipe (
587- map ( ( event ) => ( {
588- pointerX : ktdPointerClientX ( event ) ,
589- pointerY : ktdPointerClientY ( event )
590- } ) ) ,
591- ktdScrollIfNearElementClientRect$ ( scrollableParent , { scrollStep : this . scrollSpeed } )
592- ) ) . pipe (
593- takeUntil ( this . gridService . pointerEnd$ )
594- ) . subscribe ( ) ) ;
595-
596655 // Main subscription, it listens for 'pointer move' and 'scroll' events and recalculates the layout on each emission
597- const dragSubscription = this . ngZone . runOutsideAngular ( ( ) =>
656+ return this . ngZone . runOutsideAngular ( ( ) =>
598657 merge (
599658 combineLatest ( [
600659 this . gridService . pointerMove$ ,
@@ -693,47 +752,33 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
693752 }
694753 } )
695754 ) ;
696-
697- this . drag = {
698- dragSubscription,
699- scrollSubscription,
700- startEvent : event ,
701- newLayout : null ,
702- newLayoutItem : dragInfo . dragRef . itemRef instanceof KtdDrag ? {
703- id : dragInfo . dragRef . id ,
704- w : 1 ,
705- h : 1 ,
706- x : - 1 ,
707- y : - 1 ,
708- } : null ,
709- } ;
710755 }
711756
712757 // TODO: Call this only when the drag ended, when the drag is paused do nothing.
713- public stopDragSequence ( ) : void {
714- const dragInfo = this . gridService . drag ! ;
758+ private stopDragSequence ( dragInfo : PointerEventInfo ) : void {
759+ if ( this . drag === null ) {
760+ return ;
761+ }
715762
716- if ( this . drag != null ) {
717- // Remove drag classes
718- this . renderer . removeClass ( dragInfo . dragRef . elementRef . nativeElement , 'no-transitions' ) ;
719- this . renderer . removeClass ( dragInfo . dragRef . elementRef . nativeElement , 'ktd-grid-item-dragging' ) ;
763+ console . log ( 'stopDragSequence' ) ;
720764
721- this . ngZone . run ( ( ) => {
722- ( dragInfo . type === 'drag' ? this . dragEnded : this . resizeEnded ) . emit ( getDragResizeEventData ( dragInfo . dragRef , this . layout ) ) ;
723- } ) ;
765+ // Remove drag classes
766+ this . renderer . removeClass ( dragInfo . dragRef . elementRef . nativeElement , 'no-transitions' ) ;
767+ this . renderer . removeClass ( dragInfo . dragRef . elementRef . nativeElement , 'ktd-grid-item-dragging' ) ;
724768
725- this . addGridItemAnimatingClass ( dragInfo . dragRef ) . subscribe ( ) ;
726- // Consider destroying the placeholder after the animation has finished.
727- this . destroyPlaceholder ( ) ;
728- this . drag . dragSubscription . unsubscribe ( ) ;
729- this . drag . scrollSubscription ?. unsubscribe ( ) ;
730- this . drag = null ;
731- }
732- }
769+ this . ngZone . run ( ( ) => {
770+ ( dragInfo . type === 'drag' ? this . dragEnded : this . resizeEnded ) . emit ( getDragResizeEventData ( dragInfo . dragRef , this . layout ) ) ;
771+ } ) ;
733772
734- public updateLayout ( ) : void {
735- const dragInfo = this . gridService . drag ! ;
773+ this . addGridItemAnimatingClass ( dragInfo . dragRef ) . subscribe ( ) ;
774+ // Consider destroying the placeholder after the animation has finished.
775+ this . destroyPlaceholder ( ) ;
776+ this . drag . dragSubscription . unsubscribe ( ) ;
777+ this . drag . scrollSubscription ?. unsubscribe ( ) ;
778+ this . drag = null ;
779+ }
736780
781+ public updateLayout ( dragInfo : PointerEventInfo ) : void {
737782 if ( this . drag != null && this . drag . newLayout ) {
738783 const previousLayoutItem = this . layout . find ( item => item . id === dragInfo . dragRef . id ) ;
739784 const currentLayoutItem = this . drag ! . newLayout . find ( item => item . id === dragInfo . dragRef . id ) ;
@@ -760,8 +805,6 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
760805 } ) ;
761806 } ) ;
762807 }
763-
764- this . stopDragSequence ( ) ;
765808 }
766809
767810 public isPointerInsideGridElement ( event : MouseEvent | TouchEvent ) : boolean {
0 commit comments