Search code examples
vue.jsvuejs3vuetify.jscss-transitionsvuetifyjs3

vuetify2 to vuetify3 transition-group no longer working


So I'm currently working on my transition from Vue2 Vuetify2 to Vue3 Vuetify3 and it's been quite the challenge honestly. It seems my code needs to rely less and less on Vuetify since things like the v-treeview are broken beyond repair and I've had to recode everything.

Currently trying to simply make a v-list with v-list-items where each item going up and down would have a transition, and yet it doesn't seem possible. For some reason the transition seems to only work when moving down and never when going up and also never works with the last item regardless. Here is the attempt at a simple piece of code to just try and make the transition happen correctly, trying to follow Vuejs list-transition example as closely as possible but with a v-list instead (which you can easily try on VPlay):

<template>
  <v-container>
    <v-list>
      <transition-group name="list" tag="div" class="container">
        <v-list-item
          v-for="(item, index) in items"
          :key="item.id"
          :title="item.title"
          :subtitle="'Position: ' + (index + 1)"
          class="item"
        >
          <template v-slot:prepend>
            <v-btn icon @click="moveItemUp(index)" :disabled="index === 0">
              <v-icon>mdi-arrow-up</v-icon>
            </v-btn>
            <v-btn
              icon
              @click="moveItemDown(index)"
              :disabled="index === items.length - 1"
            >
              <v-icon>mdi-arrow-down</v-icon>
            </v-btn>
          </template>
        </v-list-item>
      </transition-group>
    </v-list>
  </v-container>
</template>

<script setup>
  import { ref } from 'vue'

  const items = ref([
    { id: 1, title: 'Item 1' },
    { id: 2, title: 'Item 2' },
    { id: 3, title: 'Item 3' },
    { id: 4, title: 'Item 4' },
    { id: 5, title: 'Item 5' },
  ])

  const moveItemUp = index => {
    if (index > 0) {
      const item = items.value.splice(index, 1)[0]
      items.value.splice(index - 1, 0, item)
    }
  }

  const moveItemDown = index => {
    if (index < items.value.length - 1) {
      const item = items.value.splice(index, 1)[0]
      items.value.splice(index + 1, 0, item)
    }
  }
</script>

<style scoped>
  .container {
    position: relative;
    padding: 0;
    list-style-type: none;
  }

  .item {
    width: 100%;
    height: 80px;
    background-color: #f3f3f3;
    border: 1px solid #666;
    box-sizing: border-box;
  }

  .list-move,
  .list-enter-active,
  .list-leave-active {
    transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1);
  }
  .list-enter-from,
  .list-leave-to {
    opacity: 0;
    transform: scaleY(0.01) translate(30px, 0);
  }
  .list-leave-active {
    position: absolute;
  }
</style>

Do I again need to just opt out of a vuetify component again or is there something I have yet to understand? I have seen this question which seemed promising but it's unfortunately not the same issue.


Solution

  • There seems to be problems with the VList in Vuetify 3. First off, @MoritzRingler pointed out that the problem seems to only happen in Chrome and not Firefox, so it does seem to be a browser-specific issue.

    Furthermore, there seems to be an easy workaround to this issue as point out by @yoduh, where you can simply remove v-list entirely and the issue suddenly disappears.

    This is clearly a bug though so I will go and open an issue directly on the Vuetify github page. Issue opened on github: https://github.com/vuetifyjs/vuetify/issues/20472

    For those interested, the simplest way to fix the issue is by changing v-list to div in the provided code above, so:

    <template>
      <v-container>
        <div>
          <transition-group name="list" tag="div" class="container">
            <v-list-item
              v-for="(item, index) in items"
              :key="item.id"
              :title="item.title"
              :subtitle="'Position: ' + (index + 1)"
              class="item"
            >
              <template v-slot:prepend>
                <v-btn icon @click="moveItemUp(index)" :disabled="index === 0">
                  <v-icon>mdi-arrow-up</v-icon>
                </v-btn>
                <v-btn
                  icon
                  @click="moveItemDown(index)"
                  :disabled="index === items.length - 1"
                >
                  <v-icon>mdi-arrow-down</v-icon>
                </v-btn>
              </template>
            </v-list-item>
          </transition-group>
        </div>
      </v-container>
    </template>
    
    <script setup>
      import { ref } from 'vue'
    
      const items = ref([
        { id: 1, title: 'Item 1' },
        { id: 2, title: 'Item 2' },
        { id: 3, title: 'Item 3' },
        { id: 4, title: 'Item 4' },
        { id: 5, title: 'Item 5' },
      ])
    
      const moveItemUp = index => {
        if (index > 0) {
          const item = items.value.splice(index, 1)[0]
          items.value.splice(index - 1, 0, item)
        }
      }
    
      const moveItemDown = index => {
        if (index < items.value.length - 1) {
          const item = items.value.splice(index, 1)[0]
          items.value.splice(index + 1, 0, item)
        }
      }
    </script>
    
    <style scoped>
      .container {
        position: relative;
        padding: 0;
        list-style-type: none;
      }
    
      .item {
        width: 100%;
        height: 80px;
        background-color: #f3f3f3;
        border: 1px solid #666;
        box-sizing: border-box;
      }
    
      .list-move,
      .list-enter-active,
      .list-leave-active {
        transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1);
      }
      .list-enter-from,
      .list-leave-to {
        opacity: 0;
        transform: scaleY(0.01) translate(30px, 0);
      }
      .list-leave-active {
        position: absolute;
      }
    </style>