Search code examples
javascriptdrag-and-dropsvelte

Add and delete items in drag and drop list


I have to rework a program (javascript & html). So I thought of using Tauri and Sevlte to give it a stand-alone version in a newer look. Taking the first steps is never easy, especially if you are more of a C programmer than C++ or anything else :-(

Here is my problem:

I create a drag&drop zone with draggable elements: using 'svelte-dnd-action'.

in my main file "+page.svelte" I create an array holding the entries:

import SortableList from ./SortableList.svelte
 
let sensors = [ 
    {id: 0, title: 'USB1'},
    {id: 1, title: 'USB2'},
    {id: 2, title: 'USB3'},
    {id: 3, title: 'File: abc.txt'},
    {id: 4, title: 'File: 123.txt'},        
];

then I call <SortableList list = {sensors}/>

Inside "SortableList.svelte" I loop over all list elements and call "ListElement.svelte" to display the element. (I know, I could do this directly here...)

<section use:dndzone={{items: list, dropFromOthersDisabled}}
    on:consider={handleSort}
    on:finalize={handleSort}>    
    {#each list as item (item.id) }
        <div animate:flip={{ duration: flipDurationMs }}>
        <ListElement count = {item.id} id = {item.title}/>
    </div>
    {/each}
</section>

"ListElement.svelte" then creates the list-item to be displayed:

<div class="ListElement">
    <div style="display: inline-block;">
        <h1 class ={id}>{id}</h1>
    </div>
    <div style="display: inline-block;">
        <div >
            <p class ="{id}, IDtag" style="margin: 0px">{@html ListBoxString}:{count}</p>
        </div>
        <div >
            <button class={id} on:click={ListBoxClick}>{id}: Clicked {count} {count === 1 ? 'time' : 'times'}</button>
        </div>
    </div>
</div>

All of this works fine...... until I start working with the list. I have a button on that adds a new element to the array "sensors" This I have in "+page.svelte":

function buttonClick(){
    let i=sensors.length;
        sensors = [...sensors,{id:i, title:'great'}];
}

It works :-), but if I manually sorted the list by drag&drop, adding an element restores the original order! :-(

I would also like to add a "delete" button to each list element. But my guess: this would end in a similar problem. Having browsed for this "resorting issue" for hours I decided to ask the community.

What I tried:

  • renumbering the id values in "SortableList.svelte" I use the function "on:finalize..."... ugly animation, adding another element resorts the list.
  • using "on:drop" I get an "A11y: <section> with drop handler must have an ARIA role" error.

Well, I am sure there is a simple and elegant way to cover this problem.

Many thanks to all reading my question and maybe answering it.


Solution

  • You are just missing a binding, the changes from within SortableList (the reordering) is not propagating back out.

    <SortableList bind:list={sensors} />
    

    REPL

    (If ListElement changes properties, you may need additional bindings threre. You probably should not be changing the id if that is used for identification.)


    Side note: If SortableList is supposed to be generic, it should be using a slot:

    <section use:dndzone={{ items: list, dropFromOthersDisabled }}
        on:consider={handleSort}
        on:finalize={handleSort}>    
        {#each list as item (item.id)}
            <div animate:flip={{ duration: flipDurationMs }}>
                <slot {item} />
            </div>
        {/each}
    </section>
    
    <SortableList bind:list={sensors}>
        <svelte:fragment let:item>
            <ListElement count={item.id} id={item.title} />
        </svelte:fragment>
    </SortableList>
    

    REPL

    SortableList now only has to assume that the items have an id for keying.