svelte-sortablejs icon indicating copy to clipboard operation
svelte-sortablejs copied to clipboard

Sortable shows new added item not at the end but in between

Open git-no opened this issue 3 years ago • 4 comments

Thank you very much for the great work and Svelte implementation.

Issue

Resorting by drag and drop does not work correct after an additional item is added. The item the user drops at position A, sortable puts it to position A-1.

Situation

  • There is a data array = [1,2,3]
  • User resorts to [2,3,1]
  • An additional item is added to the array at last position [2,3,1,4]
  • But Sortable shows [2,3,4,1]

Goal

The goal ist to save the sorted data (not html) to database. Therefor a array of data is used.

Steps to Reproduce

  1. Create a svelte page and add the code below.
  2. Run Svelte
  3. Drag Item 1 below Item 3
  4. Add a item to the array by clicking on the Add button.
<script>
	import { SortableList } from '@jhubbardsf/svelte-sortablejs';

	let data = ['Item 1', 'Item 2', 'Item 3'];
	let sortableListData = [...data];

	$: {
		console.log(`data: ${data}`);
	}

	const addItem = () => {
		const item = `Item ${data.length + 1}`;
		data = [...data, item];
		sortableListData = [...data];
	};

	const move = (arr, from, to) => {
		const input = [...arr];
		let numberOfDeletedElm = 1;

		const elm = input.splice(from, numberOfDeletedElm)[0];
		numberOfDeletedElm = 0;
		input.splice(to, numberOfDeletedElm, elm);
		data = [...input];
	};

	const handleEnd = (evt) => {
		move(data, evt.oldIndex, evt.newIndex);
		// data = [...data];
	};
</script>

<div class="margin-auto ">
	<button class="button" on:click={addItem}>Add</button>

	<SortableList class="list-group col" animation={150} ghostClass="bg-info" onEnd={handleEnd}>
		{#each sortableListData as item}
			<div class="list-group-item">
				{item}
			</div>
		{/each}
	</SortableList>
</div>

<style>
	.margin-auto {
		margin: auto;
	}
	.list-group.col {
		background-color: blue;
		color: white;
	}
	.list-group-item {
		background-color: gray;
		color: white;
		margin: 20px 0;
		padding: 8px 8px;
	}
	.button {
		padding: 8px;
		background-color: green;
		color: white;
	}
</style>

Thank you in advanced for your response.

git-no avatar Jun 15 '22 20:06 git-no

Thank you for the detailed PR! I'll take a closer look at it this weekend and hopefully get a fix put in.

jhubbardsf avatar Jun 18 '22 00:06 jhubbardsf

I created two better examples.

Resort the item array manually in setValue

Issues

  • Drag and after drop does not show the correct order on the screen. Reason in setValue the item array is sorted again and the item list is rendered again by Svelte
  • Adding an item (via button) is correct in the array but not on screen
<script>
	import { SortableList } from '@jhubbardsf/svelte-sortablejs';
	import { writable } from 'svelte/store';

	const items = writable([]);
	$items = ['Item 0', 'Item 1', 'Item 2'];

	let currentIndices = [...$items.keys()];

	// Issues: new items (addItem) should be added at the end, there are added on screen first)
	$: {
		console.log(`currentIndices: ${currentIndices}`);
	}

	function setupStore() {
		return {
			get: getValue,
			set: setValue
		};
	}

	function getValue(sortable) {
		const order = currentIndices.map((i) => i.toString());
		return order || [];
	}

	function setValue(sortable) {
		currentIndices = sortable.toArray().map((i) => parseInt(i));
		$items = currentIndices.map((n) => $items[n]);
	}

	const addItem = () => {
		const item = `Item ${$items.length}`;
		$items = [...$items, item];
		currentIndices = [...currentIndices, currentIndices.length];
		$items = currentIndices.map((n) => $items[n]);
	};
</script>

<div class="margin-auto ">
	<button class="button" on:click={addItem}>Add</button>

	<SortableList class="list-group col" animation={150} ghostClass="bg-info" store={setupStore()}>
		{#each $items as item, i (item)}
			<div class="list-group-item" data-id={currentIndices[i]}>
				{`${item} - dataID: ${currentIndices[i]}`}
			</div>
		{/each}
	</SortableList>
	<div>
		indices order: {currentIndices}
	</div>
	<div>
		items order in array: {$items}
	</div>
</div>

<style>
	.margin-auto {
		margin: auto;
	}
	.list-group.col {
		background-color: blue;
		color: white;
	}
	.list-group-item {
		background-color: gray;
		color: white;
		margin: 20px 0;
		padding: 8px 8px;
	}
	.button {
		padding: 8px;
		background-color: green;
		color: white;
	}
</style>

Without resort the item array manually in setValue

Issues

  • Drag and drop works fine
  • Adding an item (via button) is correct in the array but not on screen
<script>
	import { SortableList } from '@jhubbardsf/svelte-sortablejs';
	import { writable } from 'svelte/store';

	const items = writable([]);
	$items = ['Item 0', 'Item 1', 'Item 2'];

	let currentIndices = [...$items.keys()];

	// Issues: new items (addItem) should be added at the end, there are added on screen first)
	$: {
		console.log(`currentIndices: ${currentIndices}`);
	}

	function setupStore() {
		return {
			get: getValue,
			set: setValue
		};
	}

	function getValue(sortable) {
		const order = currentIndices.map((i) => i.toString());
		return order || [];
	}

	function setValue(sortable) {
		currentIndices = sortable.toArray().map((i) => parseInt(i));
		// $items = currentIndices.map((n) => $items[n]);
	}

	const addItem = () => {
		const item = `Item ${$items.length}`;
		$items = [...$items, item];
		currentIndices = [...currentIndices, currentIndices.length];
		// $items = currentIndices.map((n) => $items[n]);
	};
</script>

<div class="margin-auto ">
	<button class="button" on:click={addItem}>Add</button>

	<SortableList class="list-group col" animation={150} ghostClass="bg-info" store={setupStore()}>
		{#each $items as item, i (item}
			<div class="list-group-item" data-id={i}>
				{`${item} - dataID: ${i}`}
			</div>
		{/each}
	</SortableList>
	<div>
		indices order: {currentIndices}
	</div>
	<div>
		items order in array: {$items}
	</div>
</div>

<style>
	.margin-auto {
		margin: auto;
	}
	.list-group.col {
		background-color: blue;
		color: white;
	}
	.list-group-item {
		background-color: gray;
		color: white;
		margin: 20px 0;
		padding: 8px 8px;
	}
	.button {
		padding: 8px;
		background-color: green;
		color: white;
	}
</style>

git-no avatar Jun 18 '22 04:06 git-no

I modified the code example by adding keys to Svelte each blocks as described keyed-each-blocks

git-no avatar Jun 22 '22 03:06 git-no

Hey, not sure if you're still having issues with this. Sorry. I just looped over your first example and it was fixed by doing. I think the main issue with Svelte is them not having the unique key indexes like you mentioned.

<script>
	import { SortableList } from '@jhubbardsf/svelte-sortablejs';

	let data = [{id: 0, val: 'Item 1'}, {id: 1, val: 'Item 2'}, {id: 2, val: 'Item 3'}];
	// let sortableListData = [...data];

	$: {
		console.log(`data: ${data}`);
	}

	const addItem = () => {
		const item = `Item ${data.length + 1}`;
		data = [...data, {id: data.length, val: item}];
		// sortableListData = [...data];
	};

	const move = (arr, from, to) => {
		const input = [...arr];
		let numberOfDeletedElm = 1;

		const elm = input.splice(from, numberOfDeletedElm)[0];
		numberOfDeletedElm = 0;
		input.splice(to, numberOfDeletedElm, elm);
		data = [...input];
	};

	const handleEnd = (evt) => {
		move(data, evt.oldIndex, evt.newIndex);
		// data = [...data];
	};
</script>

<svelte:head>
	<title>Home</title>
	<meta name="description" content="Svelte demo app" />
</svelte:head>

<section>
	<h1>Sortable Test</h1>

	<div class="margin-auto ">
		<button class="button" on:click={addItem}>Add</button>
	
		<SortableList class="list-group col" animation={150} ghostClass="bg-info" onEnd={handleEnd}>
			{#each data as item (item.id)}
				<div class="list-group-item">
					{item.val}
				</div>
			{/each}
		</SortableList>
	</div>
</section>

<style>
	.margin-auto {
		margin: auto;
	}
	.list-group.col {
		background-color: blue;
		color: white;
	}
	.list-group-item {
		background-color: gray;
		color: white;
		margin: 20px 0;
		padding: 8px 8px;
	}
	.button {
		padding: 8px;
		background-color: green;
		color: white;
	}
</style>

jhubbardsf avatar Sep 25 '22 17:09 jhubbardsf