Sortable icon indicating copy to clipboard operation
Sortable copied to clipboard

Help with creating a lit based element

Open joezappie opened this issue 2 years ago • 1 comments

Since LIT is not a currently supported framework, I'm trying to figure out how to turn this into a lit component myself. I've looked at the sortable libraries for other frameworks to get ideas but haven't gotten it working well yet. Since LIT doesn't allow anything else to modify the DOM its managing, it needs to stop sortable from moving stuff around, and instead internally manage its list of items.

My current approach is to use onMove and always return false so it doesn't modify the HTML directly. I then want to manually update the items array to reflect the dragged change and trigger a lit update so it all stays in sync.

import { LitElement, html, nothing } from 'lit';
import { customElement, property, query } from 'lit/decorators.js';
import { map } from 'lit/directives/map.js';
import Sortable from 'sortablejs';

@customElement('lit-sortable')
export class LitSortable extends LitElement {
  @property({ type: Array }) items;
  @property({ type: Object }) options;
  @property({}) itemTemplate = (value) => html`${value}`;

  willUpdate(changedProperties) {
    super.willUpdate(changedProperties);

    if (changedProperties.has('options')) {
      this.sortable?.destroy();

      this.options.onMove = (evt) => {
        const children = [...this.querySelectorAll(`:scope > ${this.options.draggable || '*'}`)];
        const oldIndex = children.indexOf(evt.dragged);
        const newIndex = children.indexOf(evt.related);

        const el = this.items[oldIndex];
        this.items.splice(oldIndex, 1);
        this.items.splice(newIndex, 0, el);
        this.requestUpdate();

        return false;
      };

      this.sortable = Sortable.create(this, this.options);
    }
  }

  render() {
    console.log(this.items);
    return map(this.items, this.itemTemplate);
  }

  createRenderRoot() {
    return this;
  }
}

This kind of works... but causes the item I'm dragging and the one I'm hovering to infinitely flip while dragging. I'm curious if you have any suggestions on ways to make the library work without being in control of the DOM itself.

I think my issue is that Lit reuses the elements, so after reording the list, technically Lit never moves the "dragged" element, only updates the changed text. I'd need a way to tell sortablejs that the "dragged" element reference has changed.

joezappie avatar May 17 '23 01:05 joezappie

Hi!

I've spent quite some time on getting Sortable to work with Lit and shared the best approach I've found so far here: https://github.com/SortableJS/Sortable/issues/2089#issuecomment-1902094024 hope this helps =D

enkelmedia avatar Jan 20 '24 13:01 enkelmedia

Hello, @joezappie Did you find any solution for this? I am also having same issue.

PirAt39 avatar Jul 18 '24 12:07 PirAt39

@PirAt39 Unfortunately no, I ended up just making my own bare necessity sorting code for my problem at the time. As a general solution, i think enkelmedia's is your best bet to hook into sortable's callbacks.

joezappie avatar Jul 18 '24 13:07 joezappie

@joezappie I found this solution to work very well for me. You can find the full comment here.

jdcoldsmith avatar Jul 19 '24 17:07 jdcoldsmith

@jdcoldsmith That looks like a great solution and will definately use it next time I need a sortable list.

Only reason I didnt use enkelmedias solution was by the time he posted I had already just made a minimal drag and drop element myself. Definately prefer using sortablejs as its such a more robust library so thanks for posting your solution!

I'm going to close this issue.

joezappie avatar Jul 19 '24 17:07 joezappie

You're welcome! To be clear I didn't create that solution but I found it here

jdcoldsmith avatar Jul 19 '24 17:07 jdcoldsmith