Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
git-no opened this issue Jun 15, 2022 · 4 comments
Open

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

git-no opened this issue Jun 15, 2022 · 4 comments

Comments

@git-no
Copy link

git-no commented Jun 15, 2022

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.

@jhubbardsf
Copy link
Owner

jhubbardsf commented Jun 18, 2022

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

@git-no
Copy link
Author

git-no commented Jun 18, 2022

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
Copy link
Author

git-no commented Jun 22, 2022

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

@jhubbardsf
Copy link
Owner

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>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants