Add undo to a SortableJS list with nested list(s)

30 views Asked by At

I have a single HTML list which may contain zero or more nested lists. A nested list is never be more than one level down ie nested lists cannot contain itself nest a list. Users can add/remove nested lists.

When the page loads I loop through the top level of the root list with this:

const sortoptions = {
    group: {
      name: 'items',
      pull: function (to, from, dragEl) {
        return !dragEl.matches('[data-nested^="nested-"]') || !to.el.matches('[data-nested^="nested-"] > ol');
      }
    },
    ghostClass: 'ghost',
    store: {
      get: function (sortable) {
        let order = localStorage.getItem(sortable.options.group.name);
        return order ? order.split('|') : [];
      },
      set: function (sortable) {
        let order = sortable.toArray(),
            n = order.join('|'),
            o = localStorage.getItem(sortable.options.group.name);
           
        if (n != o) { // changed
          localStorage.setItem(sortable.options.group.name, order.join('|'));
        }
      }
    },
    animation: 150,
    fallbackOnBody: true,
    swapThreshold: 0.65
  };

Sortable.create(document.querySelector('#root'), sortoptions);

And through nested lists with code like this:

let num = 1;
Sortable.create(document.querySelector('[data-nested="nested-' + num + '"]'), sortoptions);

The resulting list behaves as expected (for which I'm quite pleased):

  • root items can be reordered
  • items from a nested list can be moved to root or to another nested list
  • nested lists can be reordered
  • root items can be moved into nested lists

The issue I'm trying to resolve is how to add an undo capability to a list that contains nested lists.

Each time I try something the list is sortable but undo never seems to work.

Each call to Sortable.create creates a new instance and so I've no real idea how to know which items (parent and children) to store to localStorage that I'll later need for undo.

Do I just create an array of root and then and concatenate every nested list to it? But then how would I know if a nested lists new position?


I've made some progress. Before each call to "create" I add a "name" attribute

sortoptions.name = 'foo";
Sortable.create(document.querySelector(...

then in store I can refer to the specific Sortable using "name" eg:

store: {
  get: function (sortable) {
    const inst = sortable.options.group.name + '-' + sortable?.options?.name;

Also, for some unknown reason I needed to manually add a data attribute to each draggable item. If I left it up to Sortable the attribute values changed each time I dragged an item to a new location. My next step is dealing with sub-lists.

0

There are 0 answers