JSFiddle - React, Tailwind, and code Playground

changing transitions on transition-group based on state of list

by Travis Almand

HTML

<p>Pick the cities in order you would wish to visit.</p>

<div id="app">
  <div class="container">
    <ul class="list-back">
      <li v-for="(city, index) in cities" :key="city" @click="chooseCity(index)"></li>
    </ul>
    <transition-group :name="currentListTransition" tag="ul" class="list">
      <li v-for="(item, index) in selectedItems" :key="item">{{ item }}</li>
    </transition-group>
  </div>
  <div class="container">
    <ul class="list">
      <li class="city" v-for="(city, index) in cities" :key="city" @click="chooseCity(index)">{{ city }}</li>
    </ul>
  </div>
  <div class="panel">
    <button class="clear" @click="clearSelection">clear</button>
  </div>
</div>

SCSS

#app {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  height: 100vh;
  justify-content: center;
  width: 100vw;
}

.container {
  height: 150px;
  margin: 20px;
  position: relative;
  width: 150px;
}

.list,
.list-back {
  align-items: center;
  bottom: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  left: 0;
  margin: auto;
  position: absolute;
  right: 0;
  top: 0;
  
  li {
    align-items: center;
    display: flex;
    flex-shrink: 0;
    height: 24px;
    justify-content: center;
    margin-bottom: 4px;
    width: 100%;
  }
}
.list-back li {
  background-color: #E0E0E0;
  border-radius: 24px;
}

.city {
  background-color: #E0E0E0;
  border-radius: 24px;
  cursor: pointer;
  transition: 0.5s;
  
  &:hover {
    background-color: #307B21;
    color: #F5F5F5;
  }
  &.selected {
    opacity: 0.5;
    pointer-events: none;
  }
}
.panel {
  display: flex;
  justify-content: center;
  width: 100%;
  
  .clear {
    background-color: #C11826;
    border: {
      radius: 8px;
      style: none;
    }
    color: #F5F5F5;
    cursor: pointer;
    padding: 8px 16px;
  }
}


.top-enter-active, .top-leave-active { transition: 0.5s; }
.top-enter, .top-leave-to { opacity: 0; transform: translate3d(0, -40px, 0); }
.top-move { opacity: 0.5; transition: 0.5s; }

.left-enter-active, .left-leave-active { transition: 0.5s; }
.left-enter, .left-leave-to { opacity: 0; transform: translate3d(-40px, 0, 0); }
.left-move { opacity: 0.5; transition: 0.5s; }

.right-enter-active, .right-leave-active { transition: 0.5s; }
.right-enter, .right-leave-to { opacity: 0; transform: translate3d(40px, 0, 0); }
.right-move { opacity: 0.5; transition: 0.5s; }

.bottom-enter-active, .bottom-leave-active { transition: 0.5s; }
.bottom-enter, .bottom-leave-to { opacity: 0; transform: translate3d(0, 30px, 0); }
.bottom-move { opacity: 0.5; transition: 0.5s; }

Vue

new Vue({
  el: "#app",
  data: {
    selectedItems: [],
    cities: ['Chicago', 'New York', 'Seattle', 'Miami', 'Boston'],
    currentListTransition: 'top'
  },
  methods: {
  	chooseCity: function (index) {
    let selectedLength = this.selectedItems.length;
    let citiesLength = this.cities.length;
    let clt = this.currentListTransition;
    
      if (selectedLength === 0) {
        clt = 'top';
      } else if (selectedLength > 0 && selectedLength < citiesLength - 1) {
      	clt = clt === 'top' || clt === 'left' ? 'right' : 'left';
      } else if (selectedLength === citiesLength - 1) {
        clt = 'bottom';
      }
    
    	this.currentListTransition = clt;
    	this.selectedItems.push(this.cities[index]);
      document.querySelector(`.city:nth-child(${index + 1})`).classList.add('selected');
    },
    clearSelection: function () {
    	this.currentListTransition = 'right';
    	this.selectedItems = [];
      document.querySelectorAll('.city.selected').forEach(element => {
      	element.classList.remove('selected');
      });
    }
  }
})