Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Select not compatible with opacity transition #440

Open
2 tasks
iyume opened this issue Mar 28, 2024 · 6 comments
Open
2 tasks

[Bug]: Select not compatible with opacity transition #440

iyume opened this issue Mar 28, 2024 · 6 comments
Labels
bug Something isn't working

Comments

@iyume
Copy link

iyume commented Mar 28, 2024

Reproduction

<script setup lang="ts">
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";

const fold = ref(false);

const selected = ref();
</script>

<template>
  <button @click="fold = !fold">Action</button>
  <Transition
    enter-from-class="opacity-0"
    enter-active-class="transition-opacity duration-1000"
    enter-to-class="opacity-100"
    leave-from-class="opacity-100"
    leave-active-class="transition-opacity duration-1000"
    leave-to-class="opacity-0"
  >
    <div v-if="!fold">
      <Select v-model="selected">
        <SelectTrigger class="w-[180px]">
          <SelectValue placeholder="Select a fruit" />
        </SelectTrigger>
        <SelectContent>
          <SelectGroup>
            <SelectLabel>Fruits</SelectLabel>
            <SelectItem value="apple"> Apple </SelectItem>
            <SelectItem value="banana"> Banana </SelectItem>
            <SelectItem value="blueberry"> Blueberry </SelectItem>
            <SelectItem value="grapes"> Grapes </SelectItem>
            <SelectItem value="pineapple"> Pineapple </SelectItem>
          </SelectGroup>
        </SelectContent>
      </Select>
    </div>
  </Transition>
</template>

Describe the bug

Select is not compatible with opacity transition component. The text disappear (not transparent) on entering opacity transition.

动画

System Info

System:
    OS: Linux 5.15 Ubuntu 22.04.3 LTS 22.04.3 LTS (Jammy Jellyfish)
    CPU: (24) x64 AMD Ryzen 9 7900X 12-Core Processor
    Memory: 3.10 GB / 7.37 GB
    Container: Yes
    Shell: 5.1.16 - /bin/bash
  Binaries:
    Node: 20.11.1 - ~/.nvm/versions/node/v20.11.1/bin/node
    npm: 10.2.4 - ~/.nvm/versions/node/v20.11.1/bin/npm
    pnpm: 8.15.4 - ~/.nvm/versions/node/v20.11.1/bin/pnpm
  npmPackages:
    nuxt: ^3.11.1 => 3.11.1 
    radix-vue: ^1.5.3 => 1.5.3 
    shadcn-nuxt: ^0.10.2 => 0.10.2 
    vue: ^3.4.21 => 3.4.21 

Chrome: 123.0.6312.60
Firefox: 124.0.1

Contributes

  • I am willing to submit a PR to fix this issue
  • I am willing to submit a PR with failing tests
@iyume iyume added the bug Something isn't working label Mar 28, 2024
@stripedpurple
Copy link

@iyume That happens because the of the v-if, which causes the component to be unmounted. if you use a v-show instead the text will fade as well . If you want the component to unmount after the transition you can do something like this.

<script setup lang="ts">
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectTrigger,
  SelectValue,
} from "@/shadcn/components/ui/select";

import { ref } from 'vue';

const isMounted = ref(false);
const fold = ref(false);

const selected = ref();
</script>

<template>
  <button @click="fold = !fold">Action</button>
  <Transition enter-from-class="opacity-0" enter-active-class="transition-opacity duration-1000"
    enter-to-class="opacity-100" leave-from-class="opacity-100" leave-active-class="transition-opacity duration-1000"
    leave-to-class="opacity-0" @enter="isMounted = true" @leave="isMounted = false">
    <div v-show="!fold" v-if='!isMounted'>
      <Select v-model="selected">
        <SelectTrigger class="w-[180px]">
          <SelectValue placeholder="Select a fruit" />
        </SelectTrigger>
        <SelectContent>
          <SelectGroup>
            <SelectLabel>Fruits</SelectLabel>
            <SelectItem value="apple"> Apple </SelectItem>
            <SelectItem value="banana"> Banana </SelectItem>
            <SelectItem value="blueberry"> Blueberry </SelectItem>
            <SelectItem value="grapes"> Grapes </SelectItem>
            <SelectItem value="pineapple"> Pineapple </SelectItem>
          </SelectGroup>
        </SelectContent>
      </Select>
    </div>
  </Transition>
</template>

@iyume
Copy link
Author

iyume commented Mar 29, 2024

@stripedpurple I tried your code snippet, but it looks strange.

动画

Promise.then(异步)
queueFlush @ chunk-SV7XBIS2.js?v=84e1f9c6:1775
queueJob @ chunk-SV7XBIS2.js?v=84e1f9c6:1769
(匿名) @ chunk-SV7XBIS2.js?v=84e1f9c6:7580
resetScheduling @ chunk-SV7XBIS2.js?v=84e1f9c6:513
triggerEffects @ chunk-SV7XBIS2.js?v=84e1f9c6:557
triggerRefValue @ chunk-SV7XBIS2.js?v=84e1f9c6:1317
set value @ chunk-SV7XBIS2.js?v=84e1f9c6:1362
set @ chunk-SV7XBIS2.js?v=84e1f9c6:1380
_createElementVNode.onClick._cache.<computed>._cache.<computed> @ app.vue:21
callWithErrorHandling @ chunk-SV7XBIS2.js?v=84e1f9c6:1660
callWithAsyncErrorHandling @ chunk-SV7XBIS2.js?v=84e1f9c6:1667
invoker @ chunk-SV7XBIS2.js?v=84e1f9c6:10287

Uncaught (in promise) Maximum recursive updates exceeded in component <BaseTransition>. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.

@lilijh
Copy link

lilijh commented Mar 29, 2024

@iyume ,perhaps this issus is caused by radix-vue,I tried to combine your code with the code for the Select component in the radix-vue official doc,but I made a lot of simplifications,then encountered the same problem as you.
here is my code:

<script setup lang="ts">
import { ref } from 'vue'
import {
  SelectContent,
  SelectItem,
  SelectItemText,
  SelectRoot,
  SelectTrigger,
  SelectValue,
} from 'radix-vue'
const selected = ref()
const fold = ref(false)
</script>

<template>
  <button @click="fold = !fold">Action</button>
  <Transition>
    <div v-if="!fold">
      <SelectRoot v-model="selected">
        <SelectTrigger>
          <SelectValue placeholder="Select a fruit..." />
        </SelectTrigger>
        <SelectContent>
          <SelectItem value="Apple">
            <SelectItemText> Apple </SelectItemText>
          </SelectItem>
        </SelectContent>
      </SelectRoot>
    </div>
  </Transition>
</template>

<style>
.v-enter-active,
.v-leave-active {
  transition: opacity 1s ease;
}
.v-enter-from,
.v-leave-to {
  opacity: 0;
}
</style>
29.03.2024_11.13.40_REC.mp4

by the way, it can work normally with only v-show

@iyume
Copy link
Author

iyume commented Mar 29, 2024

Thanks to your solutions! Yes, it can only work with v-show. I'm curious about why Transition cannot "snapshot" the Select's DOM. 🤔

@lilijh
Copy link

lilijh commented Mar 30, 2024

Thanks to your solutions! Yes, it can only work with v-show. I'm curious about why Transition cannot "snapshot" the Select's DOM. 🤔

“I’m not sure either, but I guess it might be due to the way the source code is written in radix-vue? This is part of the code for SelectItemText.vue:

<template>
  <Primitive :id="itemContext.textId" :ref="forwardRef" v-bind="{ ...props, ...$attrs }">
    <slot />
  </Primitive>

  <!-- Portal the select item text into the trigger value node -->
  <Teleport
    v-if="itemContext.isSelected.value && rootContext.valueElement.value && !rootContext.valueElementHasChildren.value" :to="rootContext.valueElement.value"
  >
    <slot />
  </Teleport>
</template>

and i've written a piece of code here to mimic it:

<script setup lang="ts">
import { ref } from 'vue'

const isDisplay = ref(true)
const root = ref(null)
</script>

<template>
  <div>
    <button @click="isDisplay = !isDisplay">Show</button>
    <Transition>
      <div ref="root" v-if="isDisplay">
        <p>hello</p>
      </div>
    </Transition>
    <Teleport v-if="root" :to="root"> if </Teleport>
  </div>
</template>

<style>
.v-enter-active,
.v-leave-active {
  transition: opacity 2s ease;
}

.v-enter-from,
.v-leave-to {
  opacity: 0;
}
</style>

here is the running effect:

vif.mp4


you can also replace <div ref="root" v-if="isDisplay"> to <div ref="root" v-show="isDisplay"> and then get such an effect:

vshow.mp4

here are some potentially relevant issues

@iyume
Copy link
Author

iyume commented Mar 30, 2024

Thanks to your work! I point out the related issues here:
vuejs/core#5836
vuejs/core#6548

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants