In Svelte, I have a component which is used to display items in two different lists. When those items are moved from one list to the other, they use a transition to animate in or out.
However, I also have a way to filter what is displayed on the screen. Displaying a new set of items would use the same component, but with different data. In this case, I don't want the transition animation to occur. I assumed that adding the local
modifier would do the trick, but it seems that Svelte isn't dropping the parent element to the list, but instead reusing it and adding the new data in the existing list DOM element.
I've tried to reproduce what I'm seeing in the sample code below.
Wanted Behavior:
- Clicking on a TODO will toggle the TODO from one list to the other.
- Clicking "Switch Categories" will switch which TODOs are listed, without animating the
<li>
s of the TODOs that are added or removed.
Actual Behavior:
- Happens as expected.
- The todos that are switched in do so with the animation.
How can I change my example so that I get the effect that I want?
App.svelte:
<script>
import Todos from './Todos.svelte';
let todos = [
{ id: 1, category: 'personal', name: 'Walk dog', done: false },
{ id: 2, category: 'personal', name: 'Take out trash', done: false },
{ id: 3, category: 'work', name: 'Make login page functional', done: false },
{ id: 4, category: 'work', name: 'Make login page elegant', done: false }
];
let currentCategory = 'personal';
const toggleCategory = () => {
currentCategory = currentCategory == 'personal' ? 'work' : 'personal';
}
const toggleTodo = id => {
todos = todos.map(todo => {
if (todo.id === id) {
return { ...todo, done: !todo.done }
}
return todo;
});
}
$: categoryTodos = todos.filter(x => x.category == currentCategory);
</script>
<button on:click={toggleCategory}>Switch Categories</button>
<Todos todos={categoryTodos} {toggleTodo}>
</Todos>
Todos.svelte:
<script>
import { slide } from 'svelte/transition';
export let todos;
export let toggleTodo;
$: complete = todos.filter(t => t.done);
$: incomplete = todos.filter(t => !t.done);
</script>
<h1>Incomplete</h1>
<ul>
{#each incomplete as {id, name} (id)}
<li transition:slide|local on:click={() => toggleTodo(id)}>{name}</li>
{/each}
</ul>
<h1>Complete</h1>
<ul>
{#each complete as {id, name} (id)}
<li transition:slide|local on:click={() => toggleTodo(id)}>{name}</li>
{/each}
</ul>