Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
lfroment0 opened this issue Oct 12, 2018 · 108 comments · Fixed by #16382
Closed
Assignees
Labels
P2 The issue is important to a large percentage of users, with a workaround

Comments

@lfroment0
Copy link

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?

  • Angular Cdk 7.0.0 beta to 7.0.0-rc.1
@crisbeto crisbeto self-assigned this Oct 12, 2018
@crisbeto
Copy link
Member

There's been a TODO for it, but I've mostly held back until now since it's tricky figuring out which element should be scrolled.

@sanketi
Copy link

sanketi commented Oct 26, 2018

Is there any intermediate way to make it work with dom-autoscroller until TODO is addressed?
https://github.com/hollowdoor/dom_autoscroller

@JohnKis
Copy link

JohnKis commented Nov 16, 2018

@crisbeto I've implemented auto scrolling on CdkDropList in my private fork. It doesn't cover scenarios where the drop list is wider/taller than its scroll parent but it works fine when the drop list fits on the screen. If you think it'd be useful, I can set aside some time to prepare a PR.

@crisbeto
Copy link
Member

@JohnKis the scrolling logic here isn't really the issue. We've held back on implementing it until now, because it's tricky knowing which element we're supposed to scroll.

@JohnKis
Copy link

JohnKis commented Nov 17, 2018

@crisbeto Understood, just figured I’d ask. Thanks

@abdulkareemnalband
Copy link

@crisbeto I think we could use cdk-scrollable as marker for element to scroll

@ghost
Copy link

ghost commented Nov 28, 2018

Hi guys. Any ideas on when to expect the fix?

@rimvydas-urbonas
Copy link

Hey, any update on this thread?

@varminas
Copy link

varminas commented Nov 29, 2018

Hi. I have a business case where multiple (say more than 10) containers are stacked vertical. So, need to think about this case as well while implementing auto-scrolling. Maybe for this solution it's possible to incorporate event cdkDropListEntered.

@lfroment0
Copy link
Author

lfroment0 commented Nov 29, 2018

The cdkDropListEntered event already exists (cdkDragEntered), but doesn't solve the problem.

eg. the case where your dropzones are in a scrollable container and the height of a drop zone is greater than the one of the container, so as only one dropZone is displayed. The supposed cdkDropListEntered event will never be emitted.

Would it be possible to make a CdkdropListGroup aware of it's container, so that it can be conscious of how it should behave when moving an item towards the displayed edges of that container?

@agent0
Copy link

agent0 commented Nov 29, 2018

I have been trying to come up with a work-around that does not rely on specific functionality by the component library. My approach was to add transparent divs at the top and the bottom of the scrollable container und to watch for mouseenter/mouseleave events to scroll the container. Unfortunately, this did not work as the framework stops the propagation of these events. I don't know if this behavior could be chanhged more easily than coming up with brand new concepts to deal with the matter.

@ghost
Copy link

ghost commented Nov 30, 2018

Any updates so far?

@ghost
Copy link

ghost commented Dec 4, 2018

I have been trying to come up with a work-around that does not rely on specific functionality by the component library. My approach was to add transparent divs at the top and the bottom of the scrollable container und to watch for mouseenter/mouseleave events to scroll the container. Unfortunately, this did not work as the framework stops the propagation of these events. I don't know if this behavior could be chanhged more easily than coming up with brand new concepts to deal with the matter.

Can you just watch for window.height and window.width and check, if the container extends further, then if it is drag action, then if mouse is down (meaning something is being dragged), then scroll the page accordingly?

@abdulkareemnalband
Copy link

@aidvb I have a solution but it comes with its own problems
see repro example in #14098

@agent0
Copy link

agent0 commented Dec 4, 2018

@aidvb Maybe this would work with scrolling the entire page, in my use case, however, I would like to scroll individual divs. Also, I am not sure if I need browser events to capture the mouse actions and since material cancels the event popagation, I would end up where I started.

@ghost
Copy link

ghost commented Dec 7, 2018

@agent0 How's it going to solve the problem?

@fromage9747
Copy link

+1 for this

@agent0
Copy link

agent0 commented Dec 16, 2018

@aidvb It would not solve the problem as such but it would enable me to implement my own scrolling trigger to scroll the underlying div manually. As it stands now, I cannot detect that the drag is hovering a border area of the div.

@RobinBomkampDv
Copy link

+1 for this

@ghost
Copy link

ghost commented Dec 20, 2018

Any progress on solving this issue?

@techy2493
Copy link

Definitely going to +1 this. We're getting ready to implement a work-around using the cdkDragMoved event but that feels like a really poor decision for long run.

@crisbeto crisbeto added the P2 The issue is important to a large percentage of users, with a workaround label Dec 21, 2018
@codeweazel
Copy link

Would love to have a solution for this. Need to scroll something out of the viewport.

@TheKeymaster
Copy link

Is there any workaround for this? I really need this because I have not that much space and I want to allow users to drag and drop inside a small container that is scrollable.

@techy2493
Copy link

@TheKeymaster The CDK drag&Drop currently calculates drop position in such a way that moving the scroll position of the container while dragging interferes with this calculation. Any work-around to detect the hover position of the drug object is limited by that. If you are There is an open issue to address that concern #14098 as well.

@shyamal890
Copy link

+1 Any temporary fix for this?

@crisbeto
Copy link
Member

crisbeto commented Jul 17, 2019

@Achilles1515 it depends on which is your scrollable element. At the moment it's set up to only scroll the window or the list itself. It starts scrolling when you're dragging and the pointer is close to the edge of the element.

@neryortez
Copy link

@crisbeto I got it working as you say: scroll just the list itself.
To make it work one has to make the list scrollable (fixed height and/or overflow set to auto).
Is there a way to enable scrolling also in the viewport?

@Achilles1515
Copy link

Achilles1515 commented Jul 17, 2019

@crisbeto
I also got it to work. In the gif below, is auto-scroll supposed to stop like it does when getting close to the left edge of the container? This does not happen on the right side of the container.
https://stackblitz.com/edit/angular-dggptq

GIF

@woteska
Copy link

woteska commented Jul 17, 2019

Unfortunately it does not work within a Material Table at all. :( I can scroll with mouse wheel now, but the distance between the drag placeholder and the dragged element is increasing while scrolling.

@rajibhasan11
Copy link

Unfortunately it does not work within nested cdkDropList. In my case I need mainly vertical auto scroll. In the attached image red circled scorllbar should be scrolled automatically when I try to drop a field in a form section.

circled_scrollbar

@digaus
Copy link

digaus commented Jul 28, 2019

Should be able to add directive to the container which we want to be able to scroll y and scroll x

@nkartchner
Copy link

Trying to build a Kanban board and dragging a card won't scroll the parent container horizontally.

https://stackblitz.com/edit/angular-dbp5r5
chrome-capture

@fatmaE19
Copy link

but this works only with drop list if i have a box inside a container div when i drag the box it's container is not scrolling ! :/

@dawda92
Copy link

dawda92 commented Aug 15, 2019

@crisbeto
When grabbing a scrollable item and going past the bottom of the viewport or past the top of the viewport, the browser window doesn't scroll.
(the container is larger than the browser viewport.)
Is there a way to do this?
I want the same functionality but within a container that is larger than the viewport. (So that the window automatically starts scrolling up or down & no extra scroll bar, just the windows scrollbar.)
This seems like something that should just work out of the box?

@crisbeto
Copy link
Member

That might be because the scroll regions are overlapping. I've got #16675 to help with it.

@htreb
Copy link

htreb commented Aug 20, 2019

+1 for @nkartchner 's stakblitz example. Even if you manually scroll the board when the ticket is being dragged the drop containers do not 'register' so when they scroll into view you still can't drop the ticket on them

@healydwya
Copy link

@crisbeto hi! are there plans to make this possible for mobile as well?

@crisbeto
Copy link
Member

crisbeto commented Sep 5, 2019

It should work on mobile already.

@tchpowdog
Copy link

tchpowdog commented Sep 19, 2019

Works for me but only for nearest scrollable parent it seems. 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>

@hb1998
Copy link

hb1998 commented Sep 22, 2019

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
Copy link

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

@Mustafa-Omran
Copy link

Mustafa-Omran commented Nov 6, 2019

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
Copy link

@Mustafa-Omran can you explain bit more ?

@KevynTD
Copy link

KevynTD commented Nov 24, 2019

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
Copy link

@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">


` **TS**

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

crisbeto added a commit to crisbeto/material2 that referenced this issue Jan 1, 2020
…d viewport

Currently for performance reasons we only support scrolling within the drop list itself or the viewport, however in some cases the scrollable container might be different. These changes add a new input that consumers can use to tell the CDK which other parents can be scrolled.

Fixes angular#18072.
Relates to angular#13588.
@crisbeto
Copy link
Member

crisbeto commented Jan 1, 2020

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

crisbeto added a commit to crisbeto/material2 that referenced this issue Jan 1, 2020
…d viewport

Currently for performance reasons we only support scrolling within the drop list itself or the viewport, however in some cases the scrollable container might be different. These changes add a new input that consumers can use to tell the CDK which other parents can be scrolled.

Fixes angular#18072.
Relates to angular#13588.
crisbeto added a commit to crisbeto/material2 that referenced this issue Jan 11, 2020
…d viewport

Currently for performance reasons we only support scrolling within the drop list itself or the viewport, however in some cases the scrollable container might be different. These changes add a new input that consumers can use to tell the CDK which other parents can be scrolled.

Fixes angular#18072.
Relates to angular#13588.
jelbourn pushed a commit that referenced this issue Jan 22, 2020
…d viewport (#18082)

Currently for performance reasons we only support scrolling within the drop list itself or the viewport, however in some cases the scrollable container might be different. These changes add a new input that consumers can use to tell the CDK which other parents can be scrolled.

Fixes #18072.
Relates to #13588.
yifange pushed a commit to yifange/components that referenced this issue Jan 30, 2020
…d viewport (angular#18082)

Currently for performance reasons we only support scrolling within the drop list itself or the viewport, however in some cases the scrollable container might be different. These changes add a new input that consumers can use to tell the CDK which other parents can be scrolled.

Fixes angular#18072.
Relates to angular#13588.
@angular-automatic-lock-bot
Copy link

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.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Feb 1, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
P2 The issue is important to a large percentage of users, with a workaround
Projects
None yet
Development

Successfully merging a pull request may close this issue.