Search code examples
vue.jsanimationvuejs2css-grid

How to animate todo moving from one list to another with Vue.js?


I am trying to do this svelte example todo moving animation with Vue.js.

Below you can find what I have done so far. Just click on the todo to see.

new Vue({
  el: "#app",
  data: {
    items: [
    	{ id: 1, name: 'John', done: false },
      { id: 2, name: 'Jane', done: false },
      { id: 3, name: 'Jade', done: true },
      { id: 4, name: 'George', done: true },
    ]
  },
  
  computed: {
  	done () {
    	return this.items.filter(i => i.done)
    },
    
    undone () {
    	return this.items.filter(i => !i.done)
    }
  },
  
  methods: {
  	toggle: function(todo){
    	todo.done = !todo.done
    }
  }
})
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  height: 500px;
  transition: all 0.2s;
}

.todos {
  display: grid;
  grid-template-columns: 1fr 1fr;
}

.todo {
  border: 1px solid #ccc;
}

.todo.undone {
  grid-column: 2 /span 1;
}

.todo.done {
  grid-column: 1 /span 1;
  background: blue;
  color: white;
}

.flip-list-move {
  transition: all 1s ease-in-out;
}

.header-wrapper {
  display: grid;
  grid-auto-flow: column;
  
}

.header, .todo {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  padding: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">


  <div class="header-wrapper">
   <div class="header">
    <span>Name</span>
    <span>Age</span>
    <span>Gender</span>
  </div>
  
    <div class="header">
    <span>Name</span>
    <span>Age</span>
    <span>Gender</span>
  </div>
  </div>

  <transition-group name="flip-list" tag="div" class="todos">

    <div class="todo done" v-for="item of done" :key="item.id" @click="toggle(item)">
      <span>{{item.name}}</span>
      <span>26</span>
      <span>Male</span>
    </div>

    <div class="todo undone" v-for="item of undone" :key="item.id" @click="toggle(item)">
      <span>{{item.name}}</span>
      <span>20</span>
      <span>Male</span>
    </div> 

   
  </transition-group>
</div>

In order to animate the todo move from one list to another, I used CSS grid but I can't find a way to distinguish todos (left and right) without having a grid cell which is empty.

I would appreciate if there is a better way to achieve the example in svelte docs or a way to omit the empty cells.

Even though it seemed easy in the beginning, it's a bit tricky.


Solution

  • Adding grid-row-start to the first undone element doesn't works if there are more than 6 items in array.

    As a solution, I used the index of v-for loop to add to every undone todo the corresponding grid-row-start.

    index starts at 0 so we have to make index + 1

      <div
        class="todo undone"
        v-for="(item, index) of undone"
        :key="item.id"
        :style="{'grid-row': index + 1}" // => HERE we guarantee no gaps are present in undone list`
        @click="toggle(item)"
      >
        <span>{{item.name}}</span>
        <span>20</span>
        <span>Male</span>
      </div>
    

    You can find the working example on this codesandbox