JS_is_awesome18
JS_is_awesome18

Reputation: 1757

How to use as="template" in Vue 3 HeadlessUI TransitionRoot

I am attempting to set up slot machine-style animation using Vue 3 TailwindCSS and HeadlessUI. So far, I have a basic green square set up to slide in from top and slide out the bottom based on cycles in a for-loop called when clicking the "click to transition" button. The resetIsShowing() function also handles the randomization of a sequential array (1-10). My goal is to display one of the 10 random numbers on the green square on each cycle. To do this, I refactored the main div displaying the square to pass a random number on each cycle:

<div v-for="number in numbers" :key="number.id" class="h-full w-full rounded-md bg-green-500 shadow-lg">
 {{ number }}
</div>

But when I add list property to the above div, then I get the following error:

The current component <TransitionChild /> is rendering a "template".
However we need to passthrough the following props:
  - ref

If I change "template" to "ref" in the TransitionRoot, then I lose the slide down effect that I am trying to achieve. How can I set this up so that numbers appear on the square on each cycle?

Here is the full code:

<template>
    <div class="flex flex-col items-center py-16">
      <div class="h-72 w-72">
        <div class="flex justify-center items-center w-full h-full p-4 text-9xl rounded-md shadow-lg border-solid border-2 border-sky-500">
            <TransitionRoot
              appear
              :show="isShowing"
              as="template"
              enter="transition ease-in-out duration-300 transform"
              enter-from="-translate-y-full opacity-0"
              enter-to="translate-y-0 opacity-100"
              leave="transition ease-in-out duration-300 transform"
              leave-from="translate-y-0 opacity-100"
              leave-to="translate-y-full opacity-0"
            >

              <div v-for="number in numbers" :key="number.id" class="h-full w-full rounded-md bg-green-500 shadow-lg">
                {{ number }}
              </div>
    
            </TransitionRoot>
        </div>
      </div>
      <button
        @click="resetIsShowing"
        class="mt-8 flex transform items-center rounded-full bg-black bg-opacity-20 px-3 py-2 text-sm font-medium text-white transition hover:scale-105 hover:bg-opacity-30 focus:outline-none active:bg-opacity-40"
      >
        <svg viewBox="0 0 20 20" fill="none" class="h-5 w-5 opacity-70">
          <path
            d="M14.9497 14.9498C12.2161 17.6835 7.78392 17.6835 5.05025 14.9498C2.31658 12.2162 2.31658 7.784 5.05025 5.05033C7.78392 2.31666 12.2161 2.31666 14.9497 5.05033C15.5333 5.63385 15.9922 6.29475 16.3266 7M16.9497 2L17 7H16.3266M12 7L16.3266 7"
            stroke="currentColor"
            stroke-width="1.5"
          />
        </svg>
  
        <span class="ml-3">Click to transition</span>
      </button>
    </div>
  </template>
  
  <script setup>
  import { ref } from 'vue'
  import { TransitionRoot } from '@headlessui/vue'
  
  const isShowing = ref(true)
  const numbers = ref([])
  const numberArray = ref(Array.from({length: 10}, (e, i)=> i + 1))

  const sleep = (milliseconds) => {
    return new Promise(resolve => setTimeout(resolve, milliseconds))
  }
  
  async function resetIsShowing() {
    for (let i = 0; i < numberArray.value.length; i++) {
        const index = Math.floor(Math.random() * numberArray.value.length)
        await sleep(800)

        isShowing.value = false
  
        setTimeout(() => {
            isShowing.value = true
        }, 500)

        numbers.value = numberArray.value.filter(r => r === (index + 1))
    }
  }
  </script>

Upvotes: 2

Views: 1303

Answers (1)

JS_is_awesome18
JS_is_awesome18

Reputation: 1757

RESOLVED

<template>
    <div class="flex flex-col items-center py-16">
      <div class="h-72 w-72">
        <div class="flex justify-center items-center w-full h-full p-4 rounded-md shadow-lg border-solid border-2 border-sky-500">
            <TransitionRoot
              appear
              :show="isShowing"
              as="template"
              enter="transition ease-in-out duration-[400ms]"
              enter-from="-translate-y-full opacity-0"
              enter-to="translate-y-0 opacity-100"
              leave="transition ease-in-out duration-[400ms]"
              leave-from="translate-y-0 opacity-100"
              leave-to="translate-y-full opacity-0"
            >

              <div class="h-full w-full rounded-md pt-16">
                <span v-if="numbers > 0" class="text-9xl">
                    {{ numbers }}
                </span>
              </div>
    
            </TransitionRoot>
        </div>
      </div>
      <button
        @click="resetIsShowing"
        class="mt-8 flex transform items-center rounded-full bg-black bg-opacity-20 px-3 py-2 text-sm font-medium text-white transition hover:scale-105 hover:bg-opacity-30 focus:outline-none active:bg-opacity-40"
      >
        <svg viewBox="0 0 20 20" fill="none" class="h-5 w-5 opacity-70">
          <path
            d="M14.9497 14.9498C12.2161 17.6835 7.78392 17.6835 5.05025 14.9498C2.31658 12.2162 2.31658 7.784 5.05025 5.05033C7.78392 2.31666 12.2161 2.31666 14.9497 5.05033C15.5333 5.63385 15.9922 6.29475 16.3266 7M16.9497 2L17 7H16.3266M12 7L16.3266 7"
            stroke="currentColor"
            stroke-width="1.5"
          />
        </svg>
  
        <span class="ml-3">Click to transition</span>
      </button>
    </div>
  </template>
  
  <script setup>
  import { ref, watch } from 'vue'
  import { TransitionRoot } from '@headlessui/vue'
  
  const isShowing = ref(true)
  const numbers = ref([])
  const numberArray = ref(Array.from({length: 10}, (e, i)=> i + 1))

  const sleep = (milliseconds) => {
    return new Promise(resolve => setTimeout(resolve, milliseconds))
  }
  
  async function resetIsShowing() {
    for (let i = 0; i < numberArray.value.length; i++) {

        await sleep(1200)
        
        const index = Math.floor(Math.random() * numberArray.value.length)
        // const index = numberArray.value[i]
        
        numbers.value = numberArray.value.filter(r => r === (index + 1))[0]
        // numbers.value = numberArray.value.filter(r => r === (index))[0]
        
        console.log(numbers.value)
        
        isShowing.value = false
        
        setTimeout(() => {
            isShowing.value = true
        }, 500)
    }
  }
  </script>
  

Upvotes: -1

Related Questions