huntabyte / vaul-svelte

An unstyled drawer component for Svelte.
https://vaul-svelte.com
MIT License
437 stars 18 forks source link

Controlled usage break animations #52

Open ciaoben opened 7 months ago

ciaoben commented 7 months ago

Describe the bug

I noticed that close animation is broken when using the bind:open prop.

Reproduction

https://stackblitz.com/edit/vaul-svelte-scaled-z2k92v?file=src%2Froutes%2F%2Bpage.svelte

Logs

No response

System Info

Every browser

Severity

annoyance

huntabyte commented 7 months ago

I've spent the past couple of hours trying to come up with a clean solution for this with no luck so far.

The tricky part is we have a few different pieces at play here, one being the underlying Dialog's open state, our true "isOpen" state (which changes once the animation completes), and then also the user's bind:open to the Drawer.Root.

I plan to keep experimenting with a way to work around this but if anyone else wants to take a look I'm open to ideas!

huntabyte commented 7 months ago

If this is something you all desperately need, in the meantime I can expose the closeDrawer() function as a prop so you could do something like:

<script lang="ts">
    let closeDrawer: CloseDrawer;
</script>

<Drawer.Root bind:closeDrawer>

</Drawer.Root>

<button on:click={() => closeDrawer()>Programatic close</button>

Let me know if this would work. I feel I'm getting close to a solution though.

MentalGear commented 7 months ago

Thanks for looking into this. The mentioned workaround is not working for me on the latest Drawer update.

ModalBottomDrawer.svelte:66 Uncaught TypeError: $.get(...) is not a function
    at HTMLButtonElement.click (ModalBottomDrawer.svelte:66:36)
    at HTMLButtonElement.bubble_event (chunk-6IUIVARJ.js?v=ff411993:1472:8)
    at HTMLButtonElement.click (button.svelte:20:17)
    at HTMLButtonElement.bubble_event (chunk-6IUIVARJ.js?v=ff411993:1472:8)
    at HTMLButtonElement.<anonymous> (bits-ui.js?v=ff411993:13667:24)
    at HTMLButtonElement.target_handler (chunk-H4AWZ5LT.js?v=ff411993:1847:22)
Screenshot 2024-02-29 at 14 33 29
julien-blanchon commented 7 months ago

I'm also struggling with that

Wizzel1 commented 4 months ago

If this is something you all desperately need, in the meantime I can expose the closeDrawer() function as a prop so you could do something like:

<script lang="ts">
  let closeDrawer: CloseDrawer;
</script>

<Drawer.Root bind:closeDrawer>

</Drawer.Root>

<button on:click={() => closeDrawer()>Programatic close</button>

Let me know if this would work. I feel I'm getting close to a solution though.

I'd need that too

pixellush commented 4 months ago

I've spent the past couple of hours trying to come up with a clean solution for this with no luck so far.

The tricky part is we have a few different pieces at play here, one being the underlying Dialog's open state, our true "isOpen" state (which changes once the animation completes), and then also the user's bind:open to the Drawer.Root.

I plan to keep experimenting with a way to work around this but if anyone else wants to take a look I'm open to ideas!

Would it not work to replace bind:open with open={$isOpen} in root.svelte? That way the underlying bits-ui Dialog only responds to isOpen changes, our true open/close state. And we still have open exposed as a prop, which already triggers openDrawer & closeDrawer.

Trying this worked for me... mostly:

Just some findings that I hope can help.

shajidhasan commented 1 month ago

Any update on this?

Krulknul commented 1 month ago

I was running into this from shadcdn-svelte, and I couldn't get that workaround to work because I believe the closeDrawer prop isn't exposed there.

Instead I found the following workaround:

<script>
let dialogTrigger;
</script>

<button on:click={() => {dialogTrigger.click()}}>close</button

<Drawer.Root bind:open={dialogOpen}>
    <Drawer.Close>
        <div bind:this={dialogTrigger}></div>
    </Drawer.Close>
</Drawer.Root>
MitchMigala commented 1 month ago

@huntabyte Great work porting this all to Svelte. Keep carrying the Svelte torch man. I had to get back to making money and stop making videos on my Consulting Ninja channel. Super busy with work now, but a quick fix for anyone that just needs this to look like there isn't a bug....

<script lang="ts">
let expanded: boolean = false;
let delayClose: boolean = false;

// Reactive Statement to actually set the drawer closed
$: {
  if (delayClose) {
    setTimeout(() => {
      expanded = false;
      delayClose = false;
    }, 300); // <--- Adjust this also as needed. I feel like this looks good. The overlay disappears just as it is reach bottom
  }
}
</script>

<button on:click={() => expanded = !expanded}>Open Drawer</button>

<Drawer.Root bind:open={expanded}>
  <!-- I like my drawers to close a bit faster than they open so I purposely set duration shorter here -->
  <Drawer.Content class="transition-all duration-200 ease-in-out {delayClose ? '!transform translate-y-full' : ''}">
    <!-- I am performing an api call when a user chooses something inside of a scroll area in the drawer , but you can put this anywhere on the page you need to initiate the the close -->
    <button
      on:click={() => {
        // Inline for simplicity, but this or in other button click functions works of course
        loading = true;
        // Whatever else you need to do wrapped in error handling 
        delayClose = true;
      }}
    >
      I am an option in giant list of options for the user
    </button>
  </Drawer.Content>
</Drawer.Root>
tomekrozalski commented 1 month ago

My workaround is to add fade to Drawer.Content:

<script>
  import { fade } from 'svelte/transition';
  export let drawerOpen = false;
</script>

<button type="button" on:click={() => { drawerOpen = !drawerOpen;}}>Toggle</button>

<Drawer.Root bind:open={drawerOpen} direction="right">
  <Drawer.Overlay />
  <Drawer.Content transition={fade}>
...

My blind idea was that it could force transition when we close drawer. And it works for me.

On the other hand, your workaround is better @Krulknul 🙏

AleksNankiewicz commented 1 month ago

How to make it work in react?