angular / components

Component infrastructure and Material Design components for Angular
https://material.angular.io
MIT License
24.35k stars 6.74k forks source link

drag-drop: make the view scroll when trying to move a draggable outside the current view #13588

Closed lfroment0 closed 5 years ago

lfroment0 commented 6 years ago

Bug, feature request, or proposal:

When trying to drag a cdkDragItem outside of the view, it does not scroll to show the rest of the cdkDrop div / the rest of the page.

What is the expected behavior?

The view should scroll towards the direction in which the element is being dragged to reveal the rest of the page.

What is the current behavior?

The view doesn't scroll, the droppable item cannot move out of the current view

What are the steps to reproduce?

In the following stackblitz, try to drag an item from the start of the list to the bottom of the list. https://stackblitz.com/edit/angular-emaoau

What is the use-case or motivation for changing an existing behavior?

The current behaviour feels very restrictive. you could have to take multiple steps if you want to drag and drop an element in a larger list.

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

hb1998 commented 5 years ago

Works for me but only for nearest scrollable parent. Also, is there a way to set the scroll speed?

<div>  -- has horizontal scroll (new drag scroll feature does not work here)
    <div>    -- has vertical scroll (new drag scroll feature works here)
        <element>  -- draggable
    </div>
</div>

need a way to set scroll speed, especially when the list is too long

Anzil-Aufait commented 5 years ago

DND auto scroll is not working on custom scroll(ngx-scrollbar).

Mustafa-Omran commented 4 years ago

Inject Document and set id to container for all drag list items to move the horizontal scrollbar when emitting dragMove event

import { DOCUMENT } from '@angular/common'; import { Inject } from '@angular/core';

constructor( @Inject(DOCUMENT) private document: Document ) { }

onDragMoved(event) { if (event.delta.x === 1) { this.document.getElementById('container').scrollLeft += 10; } else { this.document.getElementById('container').scrollLeft -= 10; } }

SurjithP commented 4 years ago

@Mustafa-Omran can you explain bit more ?

KevynTD commented 4 years ago

I had made this code, but I just found that it works if updating haha It works only with overflow:hidden case.

I will stay here if anyone wants to do this in older versions.

This is an object that you place in the document where dragMoved is in function, and call part of it inside dragMoved to work:

    //AUTOSCROLL IMPLEMENTATION
    autoScrollSetup = {
        isOnEdge:false,
        itsStarted:false,
        scroll(side:any,elContainer:any,everyXms:any,stepPx:any,direction:any):any{
            if(this.isOnEdge){ // Mouse in the edge enough to scroll?
                if(!this.itsStarted){ // Event already started?
                    this.itsStarted = true;

                    // Check the direction
                    let elScroll:any;elScroll;
                    if(direction==="horizontal"){
                        elScroll = "scrollLeft";
                    }
                    else
                    if(direction==="vertical"){
                        elScroll = "scrollTop";
                    }

                    // Runs from time to time
                    setTimeout(() => {
                        if(side==="leftOrTop")     {elContainer![elScroll] -= stepPx} // It's on the left side (if it's horizontal) or top (if it's vertical) ?
                        else
                        if(side==="rightOrBottom") {elContainer![elScroll] += stepPx} // It's on the right side (if it's horizontal) or bottom (if it's vertical) ?
                        this.itsStarted = false;    // Finish
                        this.scroll(side,elContainer,everyXms,stepPx,direction); // Rerun
                    }, everyXms);
                }
            }
        },
        autoScroll(objConfig:any){
            // Set default values
            var {event,lateralTolerancePx=40,stepPx=5,everyXms=30,direction='horizontal'} = objConfig;

            // Get element Container and this position
            var elContainer = event.source.dropContainer.element.nativeElement;
            var elContainerBCR = elContainer!.getBoundingClientRect();

            // Check direction
            let elContainer1:any;
            let elContainer2:any;
            let xy:any;
            if(direction==="horizontal"){
                elContainer1 = elContainerBCR.left;
                elContainer2 = elContainerBCR.right;
                xy = 'x';
            }
            else
            if(direction==="vertical"){
                elContainer1 = elContainerBCR.top;
                elContainer2 = elContainerBCR.bottom;
                xy = 'y';
            }

            if(event.pointerPosition[xy] <= elContainer1+lateralTolerancePx){ // Is mouse position left/top enough to start scrolling?
                this.isOnEdge = true;
                this.scroll("leftOrTop",elContainer,everyXms,stepPx,direction);
            }
            else if(event.pointerPosition[xy] >= elContainer2-lateralTolerancePx){ // Is mouse position right/bottom enough to start scrolling?
                this.isOnEdge = true;
                this.scroll("rightOrBottom",elContainer,everyXms,stepPx,direction);
            }
            else{
                this.isOnEdge = false;
            }
        }
    }

Inside dragMoved:

    dragMoved(event: CdkDragMove<string[]>){
            //Auto Scroll: event*(required), direction(horizontal or vertical), lateralTolerancePx, stepPx, everyXms
            this.autoScrollSetup.autoScroll({'event':event,'direction':'horizontal'})
    }

besides calling dragMoved inside each list element. It worked well with me and I tried to make it very easy to deploy.

Mustafa-Omran commented 4 years ago

@Mustafa-Omran can you explain bit more ?

HTML

`

Itmes

<div class="overflow-container" cdkDropList (cdkDropListDropped)="onDropItem($event)" 
    [cdkDropListData]="items">
    <ul *ngFor="let item of items" class="item" [cdkDragData]="item" cdkDrag
        (cdkDragMoved)="onDragMoved($event)">
        <app-item [item]="item">
        </app-item>
    </ul>
</div>

` TS

onDragMoved(event) { if (event.delta.x === 1) { this.document.getElementById('container').scrollLeft += 10; } else { this.document.getElementById('container').scrollLeft -= 10; } }

crisbeto commented 4 years ago

18082 will allow for elements that aren't the list or the viewport to be scrolled.

angular-automatic-lock-bot[bot] commented 4 years ago

This issue has been automatically locked due to inactivity. Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.