draggable icon indicating copy to clipboard operation
draggable copied to clipboard

Nested Sortables?

Open toanjo opened this issue 6 years ago • 23 comments

Hi! Great work with this project, it's really good.

I was wondering if nested sortables are a possibility? For example, if I have a list of parent items and child items and I would like to sort the parents between each other or sort their children.

I am using React and have experimented with creating two different sortable objects but haven't been able to make it work as they conflict with one another. Is this not possible at this point or is it my mistake?

Thanks!

toanjo avatar Jan 23 '18 11:01 toanjo

Thanks for asking! We are planning to build a NestedSortable module or bake nesting into the existing Sortable module, but this is planned for post stable release. No ETA for this yet unfortunately

Related: https://github.com/Shopify/draggable/issues/56

tsov avatar Jan 23 '18 15:01 tsov

This would be awesome, currently using an adapted version of the retired(?) https://github.com/angular-ui-tree/angular-ui-tree . Happy to help with the logic if you'd like

saluton-mundo avatar Jun 24 '18 22:06 saluton-mundo

You have the handle property, so this does support nested drag & drop currently in the event that the parent list at-least has a handle property corresponding to a non-sortable sub-element.

The only problem I'm having is setting this to work nested is when I add another group for sub-items to be transferable between upper level lists. Like a KanBan.

https://codepen.io/Lewiscowles1986/pen/YvgPgP

I did think about adding the selector for the last list, but then the append action is off too. It's just a quick evaluation to see if this is capable of replacing jQuery UI sortable (it's so close).

Lewiscowles1986 avatar Jul 02 '18 14:07 Lewiscowles1986

Nesting is already possible with the current Sortable:

https://jsfiddle.net/zodqmx09/6/

fdietze avatar Aug 03 '18 13:08 fdietze

It's possible, but a bit of a dead end if you can only sort static elements, not receive new ones. Some form of mutation observer is probably needed.

Lewiscowles1986 avatar Aug 03 '18 14:08 Lewiscowles1986

You can create and patch the elements with a virtual dom library (like e.g. snabbdom). I just built a nested kanban-board with this technique.

fdietze avatar Aug 03 '18 20:08 fdietze

are there tomes of the internet containing the incantations to work such magic?

Lewiscowles1986 avatar Aug 03 '18 21:08 Lewiscowles1986

I'm sorry, I don't understand what you intend to say...

fdietze avatar Aug 04 '18 23:08 fdietze

You can create and patch the elements with a virtual dom library (like e.g. snabbdom). I just built a nested kanban-board with this technique.

@fdietze that sounds really cool! You don't happen to have a repo / example you could point to by chance?

beefchimi avatar Aug 05 '18 01:08 beefchimi

It's certainly the last time (this week) I'll try asking anything in a funny way.

@fdietze what @beefchimi just said.

Lewiscowles1986 avatar Aug 05 '18 08:08 Lewiscowles1986

@Lewiscowles1986 I apologize for not getting the joke. I'm not a native english speaker. Please don't stop making humor because of language barriers. :wink:

The kanban board I built is very wired with my project written in Scala, so this would probably not be a helpful example. But I'll try to explain it in more detail here instead:

  • Create one Sortable instance for the whole kanban-board.
  • To display a nested kanban board, you obviously have to store your data in a tree.
  • You can traverse this tree recursively and create virtual dom. For inner nodes of the tree you create (sub-)columns which contain more sub-trees. For the leafs you create cards. Create the dom with the necessary classes to make Sortable work.
  • Store ids or references to your nodes in the tree in data-attributes.
  • In Snabbdom you can use insert hooks to add the containers to the Sortable instance. In React this would be done via lifecycle hooks. Add each (sub-)column as a container.
  • Listen to sortable:sorted of the Sortable instance to react to dropped elements. From this event you can extract the ids of the element, the source and the target container from their data attributes. Use these ids to change your tree data structure.
  • Re-render your kanban-board with your updated tree.

Just ask, if a step is unclear.

fdietze avatar Aug 05 '18 21:08 fdietze

Hi @fdietze,

I think what I'm doing is probably incompatible with that. For one at the moment I'm not backing anything anywhere so tree structure really doesn't matter (If it's a tree it's a darn special one). I have an example of this working without virtualdom, it just has bugs, it's probably the listening to the sortable:sorted of the Sortable instance that will resolve them. Right now I remove the listeners before adding the sortable and then re-add sortable. that's obviously not ideal should I take it further.

The strange thing is that it works when I have just one instance of Sortable, however I also want to be able to change column order. I think I'll end up just adding nav-button overlay to each to ease the burden of drag-and-drop. It'll achieve the same end result without too much technical debt and up-front decision making.

  • Up down buttons for sibling exchange (easy)
  • left right buttons for column-list exchange (easy)
  • maybe a state for ad-hoc movements.

I started because I'm using WeKan at work. It's heavy for what it is and I'd prefer to store in markdown with a small helper to detect kanban layout. That way it's lightweight and doesn't need a formal storage. One person can be in-charge of full-edits to a board. YMMV

Lewiscowles1986 avatar Aug 06 '18 09:08 Lewiscowles1986

All these examples does not work as expected. Waiting for native nested support very much.

molfar avatar Aug 24 '18 16:08 molfar

Me too! How can we help?

Spone avatar Aug 24 '18 16:08 Spone

Any update on this? I tried nesting Sortables, which works, but I'm seeing this error in the console:

screen shot 2018-10-25 at 5 39 37 pm

tlaverdure avatar Oct 25 '18 21:10 tlaverdure

Hi all, same issue here @tlaverdure , did you find any way out of this ? thanks in advance

HectorLS avatar Nov 26 '18 00:11 HectorLS

@HectorLS nope. Just had to do without for now.

tlaverdure avatar Nov 26 '18 01:11 tlaverdure

Hi @travdesjard and all, im having troubles snaping to the nested dropzone, its sorting perfectly between siblings top level, but it gets too much tricky to snap into the nested ones. Any idea why could be ? maybe some css rule 😕 ?

Hope someone can help on this Thanks!

HectorLS avatar Nov 29 '18 11:11 HectorLS

@HectorLS would love to help you out but I think this is out of my wheelhouse 😉

travdesjard avatar Nov 29 '18 13:11 travdesjard

Could we hope for implementing nested sortable feature?

molfar avatar Mar 05 '19 18:03 molfar

I managed to get a proof of concept working for nested sortables by turning on and off the droppable containers using the events. I attached a data attribute that gets the sortable group and then using the sourceContainer I can determine which groups a sortable element belongs to. From there I just unbind the event to the other containers.

Markup looks a bit like this :

<div class="sortable dashboard" data-sortable-group="dashboard">
  <div class="sortable-item">
    dashboard panel A
  </div>
  <div class="sortable-item">
     <div class="sortable" data-sortable-group="tabs">
        <div class="sortable-item">
            TAB 1
        </div>
        <div class="sortable-item">          
            TAB 2
        </div>
      </div>
  </div>
  <div class="sortable-item">
    dashboard panel C
  </div>
  <div class="sortable-item">
    <div class="sortable" data-sortable-group="tabs">
        <div class="sortable-item">
            TAB X
        </div>
        <div class="sortable-item">          
            TAB Y
        </div>
      </div>
  </div>
  <div class="sortable-item">
    dashboard panel E
  </div>
</div>

And the JS does something like this:

   const groupTargetIdentifier = "data-sortable-group";
   this.droppableContainers = document.querySelector('.sortable');

    this.sortable = new Sortable(this.droppableContainers, {
      draggable: ".sortable-item",
      mirror: {
        constrainDimensions: true,
      },
    });

    this.unwantedContainers = null;
    let group;

    this.sortable.on("drag:start", e => {
      group = e.sourceContainer.getAttribute(groupTargetIdentifier);
      this.unwantedContainers = this.droppableContainers.filter(container => container.getAttribute(groupTargetIdentifier) !== group);
      this.unwantedContainers.forEach(target => this.sortable.removeContainer(target));
    });

    this.sortable.on("drag:stop", () =>{
      this.unwantedContainers.forEach(target => this.sortable.addContainer(target));
      this.unwantedContainers = null;
    });

Maybe not the most performant, but my use case I've got 5 dashboard panels that can be rearranged, and a maximum of 9 tabs

stugoo avatar Oct 30 '19 08:10 stugoo

Is there any plan to make this work?

Every few months I check the website to see whether the 'nestable' is finally no longer grayed out. As long as that is not possible, we cannot use this library because a very important function is missing. Too bad.

MichaelBrauner avatar Jan 11 '22 05:01 MichaelBrauner

Still no PR for this?

SlimGee avatar Dec 27 '23 10:12 SlimGee