Skip to content
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

Drag Drop Sortable, mixed orientation support #13372

Closed
abdulkareemnalband opened this issue Oct 1, 2018 · 234 comments · Fixed by #29216
Closed

Drag Drop Sortable, mixed orientation support #13372

abdulkareemnalband opened this issue Oct 1, 2018 · 234 comments · Fixed by #29216
Assignees
Labels
area: cdk/drag-drop feature This issue represents a new feature or feature request rather than a bug or bug fix P2 The issue is important to a large percentage of users, with a workaround

Comments

@abdulkareemnalband
Copy link

abdulkareemnalband commented Oct 1, 2018

Feature request,

Mixed direction support, such as items placed by flex-wrap: wrap

What is the expected behavior?

Should be able to use sortable drag drop on both vertical and horizontal axis at the same time

What is the current behavior?

Only vertical or horizontal is supported

What are the steps to reproduce?

https://stackblitz.com/github/vbwioxyq

Github repo link

https://github.com/abdulkareemnalband/ng-drag-drop/tree/prebuilt-css

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

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


Angular CLI: 7.0.0-beta.4
Node: 8.11.4
OS: win32 x64
Angular: 7.0.0-rc.0
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, platform-server, router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.9.0-beta.4
@angular-devkit/build-angular     0.9.0-beta.4
@angular-devkit/build-optimizer   0.9.0-beta.4
@angular-devkit/build-webpack     0.9.0-beta.4
@angular-devkit/core              7.0.0-beta.4
@angular-devkit/schematics        7.0.0-beta.4
@angular/cdk                      7.0.0-beta.2
@angular/cli                      7.0.0-beta.4
@angular/flex-layout              6.0.0-beta.16
@angular/material                 7.0.0-beta.2
@ngtools/webpack                  7.0.0-beta.4
@schematics/angular               7.0.0-beta.4
@schematics/update                0.9.0-beta.4
rxjs                              6.3.3
typescript                        3.1.1
webpack                           4.19.1
@jelbourn jelbourn added feature This issue represents a new feature or feature request rather than a bug or bug fix discussion labels Oct 3, 2018
@100cm
Copy link

100cm commented Oct 22, 2018

@jelbourn hey ,have any progress about this issue now?

@IhorHolovatsky
Copy link

IhorHolovatsky commented Oct 30, 2018

Are there any workarounds for drag and drop in mat-grid-list (reordering items in the grid)?

I have an idea, to mark each item in the grid as 'cdkDropList', and handle reordering as dropping item from one cdkDropList to another

  <mat-grid-list [cols]="photoCols"
                       rowHeight="1:1"
                       gutterSize="20px">
          <mat-grid-tile *ngFor="let photo of photos$ | async"
                         cdkDrag
                         cdkDropList
                         (cdkDragEnded)="dragEnded($event)">
            <app-photo-item [Photo]="photo"
                            (OnSelectionChange)="onSelectionChange($event)"></app-photo-item>
          </mat-grid-tile>
</mat-grid-list>

If it will work, I will provide demo

@vupham-zingbox
Copy link

How come the demo gif shows different with what it can provide ? https://cdn-images-1.medium.com/max/800/1*i30ZQdBC7CKbXXdOrUNQcg.gif

@100cm
Copy link

100cm commented Oct 31, 2018

@vupham-zingbox +1 , i want to know how to implement what the demo shows

@abdulkareemnalband
Copy link
Author

@IhorHolovatsky Nice idea
but only works when no of items in per row is known before hand
But when content reflows on window resize, this is not pretty

@demisx
Copy link

demisx commented Nov 1, 2018

Would be nice if what's shown in the demos was actually backed up by the framework code.

@josephperrott josephperrott changed the title Drag Drop Soratable, mixed orientation support Drag Drop Sortable, mixed orientation support Nov 2, 2018
@BovineEnthusiast
Copy link

BovineEnthusiast commented Nov 2, 2018

Copy and pasting my comment from #13889 since it was a duplicate:

To give an example of the issue, I have a horizontal list of photos that needs to wrap (using fxLayout="row wrap", as the issue description states). However, when it spans multiple lines, it breaks:

drag

@ducalai

This comment has been minimized.

@100cm

This comment has been minimized.

@eduardogiorgio

This comment has been minimized.

@PhoebePan
Copy link

PhoebePan commented Nov 14, 2018

@MrSnappingTurtle Do you have demo code of multiple list wrap? Thanks :)

@100cm

This comment has been minimized.

@hristo-iliev

This comment has been minimized.

@AsafAgranat
Copy link

This is difficult to implement in the current way it was implemented in the CdkDrag directive. The positioning of the placeholder, and the positions of each item that is clearing way for the placeholder, are done by setting inline CSS transforms to each of the involved elements. This is done in a predictable fashion, meaning that since the dimensions of the dragged element are known from the moment it is dragged, it can be calculated for each of the elements in the list how many pixels they should be shifting back/forth. The calculation is eased further by limiting the transforms to a single axis, allowing the transforms to ignore the widths (or heights in horizontal mode) of elements in the list.

On the other hand, when a list is wrapping, the wrapped elements are being wrapped in an arbitrary manner, calculated by the browser. When so, it is impossible to translate (transform) list elements in a similar predictability, because it is not known (upon drag event) which list elements are wrapping, and where they are positioned relatively within the list.

To circumvent that, upon every drag event, a new snapshot of the list (and all connected lists) must be generated, where the list DOM is being traversed, and each list element gets registered for width and height (practically creating a matrix). Considering that Material devs tried to solve this directive through a performative approach, traversing the DOM and registering each element is not an option as it taxes the browser significantly, and veers from the predictability approach.

Alternatively, a middle ground can be implemented. It is less performative than current solution, but at the same time does not require any heavy calculations. Instead of shifting elements via transform, the placeholder element should be moved (currently it remains in the place of the original dragging element), to be positioned before or after the target drop element. It then could animate the max-height property (or max-width for horizontal). This, as I mentioned, is less performative, but it does allow to shift the placeholder anywhere in the list - even in a wrapping list.

@allenz0810
Copy link

@flolu
Copy link

flolu commented Dec 13, 2018 via email

@c3rber
Copy link

c3rber commented Dec 13, 2018

here is a workaround with using multiple lists
https://stackblitz.com/edit/angular-nuiviw

@100cm
Copy link

100cm commented Dec 14, 2018

@c3rber That was wonderful! You are my best friend! Thanks a lot ! i will try it.

@NurGuz
Copy link

NurGuz commented Dec 17, 2018

@c3rber Wow, it's work for me, thx.

But.... Any OFFICIAL progress about this issue? It's for make and standar to this.

Thx

@RobinBomkampDv
Copy link

@c3rber Yeah, it is working fine.

Is there a way of animating @c3rber workaround?

@NurGuz
Copy link

NurGuz commented Dec 18, 2018

@c3rber

Don't work correcty.

If u add a button, for example, for add new items and after reorder the items doesn't work. If you move the ultimate element in tother place don't reorganize correctly

I think that the problem is <div class="example-container" cdkDropListGroup> besacuse @ViewChild(CdkDropListGroup) listGroup: CdkDropListGroup<CdkDropList>; don't update correctly

@Zefling
Copy link

Zefling commented Nov 15, 2023

As promised, posting the updated solution of angular 16 drag and drop workaround with renderer2 based on @c3rber' s solution - https://stackblitz.com/edit/angular-m4vl96?file=src%2Fapp%2Fapp.component.ts

Seems not work when item length are not same.

With a little change, it's possible, but not perfect. An adaptation of the c3rber solution, in this example with this open source project:
https://classement.ikilote.net/edit/1bf35e6df5c43b29c6120d9a3514f9160f564f7d

@hoangngk
Copy link

As promised, posting the updated solution of angular 16 drag and drop workaround with renderer2 based on @c3rber' s solution - https://stackblitz.com/edit/angular-m4vl96?file=src%2Fapp%2Fapp.component.ts

Seems not work when item length are not same.

Me neither

@jsculsp
Copy link

jsculsp commented Nov 15, 2023

As promised, posting the updated solution of angular 16 drag and drop workaround with renderer2 based on @c3rber' s solution - https://stackblitz.com/edit/angular-m4vl96?file=src%2Fapp%2Fapp.component.ts

Seems not work when item length are not same.

With a little change, it's possible, but not perfect. An adaptation of the c3rber solution, in this example with this open source project: https://classement.ikilote.net/edit/1bf35e6df5c43b29c6120d9a3514f9160f564f7d

It works, thx! Here's a little optimization in drop method:

let indexTarget = event.container.data.index;
// Even if the item is dragged, the original drop list still exists(with zero item).
if (indexFrom < indexTarget) {
    indexTarget--;
 }
// currentIndex === 1 means the dragged item is on the right side of target drop list
if (event.currentIndex === 1) {
    indexTarget++;
}

@hoangngk
Copy link

As promised, posting the updated solution of angular 16 drag and drop workaround with renderer2 based on @c3rber' s solution - https://stackblitz.com/edit/angular-m4vl96?file=src%2Fapp%2Fapp.component.ts

Seems not work when item length are not same.

With a little change, it's possible, but not perfect. An adaptation of the c3rber solution, in this example with this open source project: https://classement.ikilote.net/edit/1bf35e6df5c43b29c6120d9a3514f9160f564f7d

It works, thx! Here's a little optimization in drop method:

let indexTarget = event.container.data.index;
// Even if the item is dragged, the original drop list still exists(with zero item).
if (indexFrom < indexTarget) {
    indexTarget--;
 }
// currentIndex === 1 means the dragged item is on the right side of target drop list
if (event.currentIndex === 1) {
    indexTarget++;
}

do you have a stackblitz? @jsculsp thanks a lot

@julianosouzanh
Copy link

As promised, posting the updated solution of angular 16 drag and drop workaround with renderer2 based on @c3rber' s solution - https://stackblitz.com/edit/angular-m4vl96?file=src%2Fapp%2Fapp.component.ts

Seems not work when item length are not same.

With a little change, it's possible, but not perfect. An adaptation of the c3rber solution, in this example with this open source project: https://classement.ikilote.net/edit/1bf35e6df5c43b29c6120d9a3514f9160f564f7d

It works, thx! Here's a little optimization in drop method:

let indexTarget = event.container.data.index;
// Even if the item is dragged, the original drop list still exists(with zero item).
if (indexFrom < indexTarget) {
    indexTarget--;
 }
// currentIndex === 1 means the dragged item is on the right side of target drop list
if (event.currentIndex === 1) {
    indexTarget++;
}

do you have a stackblitz? @jsculsp thanks a lot

I tried it but not working for me, would have an example this optimization?

@Cammeritz
Copy link

@hoangngk @julianosouzanh

This is what @Zefling was refering to:
https://git.ikilote.net/classement/classement/-/blob/master/src/app/content/classement/classement-edit.component.ts?ref_type=heads

With a little change, it's possible, but not perfect. An adaptation of the c3rber solution, in this example with this open source project:
https://classement.ikilote.net/edit/1bf35e6df5c43b29c6120d9a3514f9160f564f7d

@iFederx
Copy link

iFederx commented Jan 23, 2024

@aybarsakgun

Yes, i already updated. Here: https://stackblitz.com/edit/angular-cdk-mixed-orientation-drag-drop

This is the best solution for me, thanks

@stasberkov
Copy link

Angular/material team, we are waiting for the fix for 5 years. For my team this is the only issue that prevents us from replacing jquery draggable with material drag-drop.

@nabby27
Copy link

nabby27 commented Apr 4, 2024

Hello, I have been subscribed to this issue for more than 4 years, I even made an approximation of what this issue is intended to solve in this codepen.

I have recently released Opire and I think this issue is ideal for the use cases it solves. With Opire anyone can create rewards on issues (as long as the repository has the bot installed) so all the people affected by this issue, like me, could put up money for someone to solve it. Without a doubt, this would be an issue in which I would put a reward

@Zefling
Copy link

Zefling commented Apr 4, 2024

Angular/material team, we are waiting for the fix for 5 years. For my team this is the only issue that prevents us from replacing jquery draggable with material drag-drop.

I think at some point I'll do my version. I had to extend and break the code to successfully create a mix between the 2 drap'n drop modes, but it's not satisfactory:
https://git.ikilote.net/classement/classement/-/blob/master/src/app/directives/dropzone.directive.ts

@evheniyrz
Copy link

evheniyrz commented Apr 12, 2024

"@angular/cdk": "^17.3.4",
"@angular/material": "^17.3.4",
The CdkDragDrop event returns the wrong value for previousIndex if multiline content is defined.

VIEW
<div class="drop-list" CdkDropListGroup>
<div
    class="target-drop-container"
    id="targetContainer"
    #targetContainer
    cdkDropList
    cdkDropListOrientation="horizontal"
    (cdkDropListDropped)="drop($event)"
  >
@for(item of itemList; track item;let idx = $index){
    <div class="drag-item" [id]="'drag-item-' + idx" cdkDrag>
     <p>draggable item</p>
    </div>
    }
</div> 
</div>
TS
  drop(event: CdkDragDrop<DropItem>): void {
    if (event.container === event.previousContainer) {
      console.log('IN SAME', { event });
     }
  }

when the content is one line then the value for index is correct.

@kritarth2121
Copy link

I faced this problem too. Based on @c3rber 's solution, I created a solution for Angular 14. Solution example: https://stackblitz.com/edit/angular-cdk-mixed-orientation-drag-drop

Well done!

Thanks i

I faced this problem too. Based on @c3rber 's solution, I created a solution for Angular 14. Solution example: https://stackblitz.com/edit/angular-cdk-mixed-orientation-drag-drop

Well done!

It worked as well for me.

@crisbeto crisbeto self-assigned this Jun 8, 2024
crisbeto added a commit to crisbeto/material2 that referenced this issue Jun 8, 2024
Currently the drop list sorts items by moving them using a `transform` which keeps the DOM stable and allows for the sorting to be animated, but has the drawback of only allowing sorting in one direction.

These changes implement a new `DropListSortStrategy` that allows sorting of lists that can wrap by moving the DOM nodes around directly, rather than via a `transform`. It has the caveat that it can't animate the sorting action.

The new strategy can be enabled by setting `cdkDropListOrientation="mixed"`.

Fixes angular#13372.
crisbeto added a commit to crisbeto/material2 that referenced this issue Jun 8, 2024
Currently the drop list sorts items by moving them using a `transform` which keeps the DOM stable and allows for the sorting to be animated, but has the drawback of only allowing sorting in one direction.

These changes implement a new `DropListSortStrategy` that allows sorting of lists that can wrap by moving the DOM nodes around directly, rather than via a `transform`. It has the caveat that it can't animate the sorting action.

The new strategy can be enabled by setting `cdkDropListOrientation="mixed"`.

Fixes angular#13372.
crisbeto added a commit to crisbeto/material2 that referenced this issue Jun 10, 2024
Currently the drop list sorts items by moving them using a `transform` which keeps the DOM stable and allows for the sorting to be animated, but has the drawback of only allowing sorting in one direction.

These changes implement a new `DropListSortStrategy` that allows sorting of lists that can wrap by moving the DOM nodes around directly, rather than via a `transform`. It has the caveat that it can't animate the sorting action.

The new strategy can be enabled by setting `cdkDropListOrientation="mixed"`.

Fixes angular#13372.
crisbeto added a commit that referenced this issue Jun 12, 2024
Currently the drop list sorts items by moving them using a `transform` which keeps the DOM stable and allows for the sorting to be animated, but has the drawback of only allowing sorting in one direction.

These changes implement a new `DropListSortStrategy` that allows sorting of lists that can wrap by moving the DOM nodes around directly, rather than via a `transform`. It has the caveat that it can't animate the sorting action.

The new strategy can be enabled by setting `cdkDropListOrientation="mixed"`.

Fixes #13372.
@stasberkov
Copy link

Thank you so much! Long-awaited feature.

@nghiadg
Copy link

nghiadg commented Jun 28, 2024

I have tried and realized the solutions above don't work perfectly. So I make my own solution using cdkDragFreeDragPosition.
It works as well for me!
I hope it can be helpful for everyone!

https://stackblitz.com/edit/stackblitz-starters-ccrfbf?file=src%2Fapp%2Fdrag-drop-grid%2Fdrag-drop-grid.component.ts

@nghiadg
Copy link

nghiadg commented Jul 1, 2024

I have tried and realized the solutions above don't work perfectly. So I make my own solution using cdkDragFreeDragPosition. It works as well for me! I hope it can be helpful for everyone!

https://stackblitz.com/edit/stackblitz-starters-ccrfbf?file=src%2Fapp%2Fdrag-drop-grid%2Fdrag-drop-grid.component.ts

I made version 2 based on the pull request of the feature for angular 18.1.0-next 4
https://stackblitz.com/edit/stackblitz-starters-w8nsnk?file=src%2Fmain.ts
Angular CDK Drag Drop Grid Sortable Version 2 - StackBlitz - Google Chrome 2024-07-01 13-53-10

@nghiadg
Copy link

nghiadg commented Jul 7, 2024

I have refactored the code before
https://stackblitz.com/edit/stackblitz-starters-3ybwzj?file=src%2Fapp%2Fdrag-drop-mixed%2Fdrag-drop-mixed.component.ts

Angular CDK Drag Drop Mixed - StackBlitz - Google Chrome 2024-07-07 07-55-26

@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 Aug 7, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: cdk/drag-drop feature This issue represents a new feature or feature request rather than a bug or bug fix 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.