Draggable: Nested Sortables?

Created on 23 Jan 2018  路  21Comments  路  Source: Shopify/draggable

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!

question

Most helpful comment

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

All 21 comments

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

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

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).

Nesting is already possible with the current Sortable:

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

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.

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.

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

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

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?

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

@fdietze what @beefchimi just said.

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

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

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

Me too! How can we help?

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

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

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

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 would love to help you out but I think this is out of my wheelhouse 馃槈

Could we hope for implementing nested sortable feature?

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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

manju-reddys picture manju-reddys  路  5Comments

philippkuehn picture philippkuehn  路  4Comments

890f2151c2be69c51db72017546d00fd picture 890f2151c2be69c51db72017546d00fd  路  4Comments

pie6k picture pie6k  路  5Comments

decademoon picture decademoon  路  4Comments