huntabyte / vaul-svelte

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

How do you trigger the modal to open without putting the button in Drawer.Root? #55

Closed khromov closed 5 months ago

khromov commented 5 months ago

Change Type

Addition

Proposed Changes

👋 Based on the example I don't understand how to trigger the dialog to open from other places in my application. Is there some way to trigger the drawer to open programmatically?

I'm trying to make an extendable modal where the content can be injected dynamically, so I need to first set my context to the correct component to put in the modal and then trigger the modal to open, but I don't understand how I can do that with the included Drawer.Trigger component.

shyakadavis commented 5 months ago

Hi;

Not sure if it meets all needs (yours included), but binding the open prop to a state variable seems to work.

<script lang="ts">
    import { Drawer } from "$lib/index.js";

    let open = false;

    function toggle() {
        open = !open;
    }
</script>

<button on:click={toggle}>
        Open Drawer - Outside
</button>

<Drawer.Root bind:open>
...
</Drawer.Root>
huntabyte commented 5 months ago

Hey, @khromov! What @shyakadavis said is likely what you're looking to do! If not, let me know and I'm happy to help!

khromov commented 5 months ago

Thank you, this did solve my issues! First I created two stores to hold the modal open and content states:

import Empty from '$lib/components/modal/Empty.svelte';
import { writable } from 'svelte/store';

export const modalOpen = writable(false);
export const modalContent = writable({
    component: Empty, // default empty component
    props: {}
});

Then bind it to the open prop and make sure the modal renders the dynamic component:

<Drawer.Root bind:open={$modalOpen} shouldScaleBackground>
<Drawer.Portal>
    <Drawer.Overlay class="drawer-overlay" />
    <Drawer.Content class="drawer-content">
        <svelte:component this={$modalContent.component} {...$modalContent.props} />
    </Drawer.Content>
</Drawer.Portal>
</Drawer.Root>

Finally we can open our modal from any component:

<script>
    import { modalContent, modalOpen } from '$lib/stores/modal';
    import Text from '$lib/components/modal/Text.svelte'; 

    const onOpen = () => {
        $modalContent = {
            component: Text, // any Svelte component
            props: {
                text: 'Hello world'
            }
        };
        $modalOpen = true; // sets modal to open
    }
</script>

<button on:click={onOpen}>Open modal</button>

Thanks again!