2
votes

So I am working in a sandbox to imitate the environment of the application I wish to implement this drag and drop in. Sorry to be so wordy but I really want to make sure I explain the problem properly as I have seen several older posts on this issue that either was not answered or answered the wrong question.

SHORT VERSION

  1. cdk dnd wouldnt autoscroll
  2. made my own autoscroll
  3. cdk dnd wont drop once scrolled

The trick is it appears that the cdk Drag and Drop autosroll functionality only works out of the box in relation to either the whole window size or, if it is a fixed height div with overflow-y: scroll (which is what I'm trying to do), then that needs to be at the level of the cdkDropList. Unfortunately due to the component architecture of my application, this is NOT currently an option.

I wrote my own autoscrolling functionality that scrolls the div based on the screen position of the mouse while an element is being dragged, I got that working but the big issue is that once the div starts scrolling you lose the ability to drop an item in the list.

Here is the HTML for my parent component (the div with the fixed height and overflow position)

<div #ScrollDiv style="height: 200px; width: 200px; overflow-y: scroll; padding: 5px;">
  <app-child (itemSelected)="itemSelection($event)"></app-child>
</div>

Here is the child component template with the cdk directives

   <div cdkDropList style="border: 2px solid purple;" (cdkDropListDropped)="drop($event)">
    <div cdkDrag [cdkDragData]="draggers" *ngFor="let item of draggers; let i = index"
            style="height: 30px; border: 2px solid blue; margin: 5px"
            (mousedown)="grabDrag()">
            {{item}}</div>   
    </div>

At this point, I can drag in my window, but auto-scroll doesn't work, so I added the auto scroll to the Type Script file of the parent component changing the scroll position on (mousemove). For the positions to start scrolling up/down the values are hardcoded bases on the page I'm working with.

import { Component, OnInit, ElementRef, ViewChild, AfterViewInit, ChangeDetectionStrategy } from '@angular/core';
import { Subscription, fromEvent } from 'rxjs';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterViewInit  {
  title = 'drag-sizing';
  itemSelected = false;
  isScrolling = false;
  smoothScroll;
  scrollSpeed = 2;
  @ViewChild('ScrollDiv', {static: true}) public myScrollContainer: ElementRef;

ngAfterViewInit() {
  fromEvent(this.myScrollContainer.nativeElement, 'mousemove').subscribe((event: MouseEvent) => {
    if (this.itemSelected) {
      if (event.clientY < 150) {
        this.isScrolling = true;
        if (!this.smoothScroll) {
          this.smoothScroll = setInterval(() => {
            this.scrollUp();
          }, 10);
        }
      } else if (event.clientY > 300) {
        this.isScrolling = true;
        if (!this.smoothScroll) {
          this.smoothScroll = setInterval(() => {
            this.scrollDown();
          }, 10);
        }
      } else {
        clearInterval(this.smoothScroll);
        this.smoothScroll = undefined;
        this.isScrolling = false;
      }
    } else {
      clearInterval(this.smoothScroll);
      this.smoothScroll = undefined;
      this.isScrolling = false;
    }
  });
}
  scrollInner() {
    this.myScrollContainer.nativeElement.scrollTop += 2;
  }
  itemSelection(event) {
    this.itemSelected = event;
    if (!this.itemSelected) {
      clearInterval(this.smoothScroll);
      this.smoothScroll = undefined;
    }
  } 
  scrollUp() {
    if (this.isScrolling === true) {
      this.myScrollContainer.nativeElement.scrollTop -= this.scrollSpeed;
    }
  }

  scrollDown() {
    if (this.isScrolling === true) {
      this.myScrollContainer.nativeElement.scrollTop += this.scrollSpeed;
    }
  }
}

and the child component TypeScript which emits that an item is being dragged and has the drop functionality

import { Component, OnInit, Input, ElementRef, ViewChild, AfterViewInit, Output, EventEmitter } from '@angular/core';
import { Subscription, fromEvent, BehaviorSubject, Subject } from 'rxjs';
import { CdkDragDrop, transferArrayItem, moveItemInArray } from '@angular/cdk/drag-drop';

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.css']
})
export class ChildComponent implements  AfterViewInit {
@Output() itemSelected = new EventEmitter<boolean>();

  itemIsSelected = false;
  draggers = ['item 0', 'item 1', 'item 2'...]

  ngAfterViewInit(): void {
    fromEvent(document, 'mouseup').subscribe(() => {
      this.itemIsSelected = false;
      this.itemSelected.emit(this.itemIsSelected);
  });
  }
  grabDrag() {
    this.itemIsSelected = true;
    this.itemSelected.emit(this.itemIsSelected);
  }
  drop(event: CdkDragDrop<any>) {
    console.log(event);
    moveItemInArray(this.draggers, event.previousIndex, event.currentIndex);
  }
}

(there might be some stuff that I don't use left-over from some failed attempts)

ANYWAYS: I know this was long but the main behavior I am trying to overcome/ figure out is When I am dragging an item and scroll away from the small piece of the array in its initial view port, the cdk drag and drop no longer lets me drop the item into that position of the array... the item still appears to be DRAGGING but this leads me to believe that it no longer recognizes the DROP ZONE

Heres What it looks like

1

1 Answers

5
votes

I think solution might be to put the cdkScrollable directive in a container which has components inside with CDK drag and drop.