An open API service indexing awesome lists of open source software.

https://github.com/NoelDeMartin/vue-modals

Vue Modals done right
https://github.com/NoelDeMartin/vue-modals

vue

Last synced: about 1 month ago
JSON representation

Vue Modals done right

Awesome Lists containing this project

README

          

# Vue Modals ![CI](https://github.com/NoelDeMartin/vue-modals/actions/workflows/ci.yml/badge.svg)

The missing library to do Vue Modals right:

- ๐Ÿช Open modals from anywhere (even outside of Vue components!).
- ๐Ÿ“ค Return promises from modals for seamless async workflows.
- ๐Ÿงผ Clean and simple API.
- ๐Ÿง™ Full TypeScript support.
- ๐Ÿ“ฆ Tiny library with zero dependencies.

To learn more, read this blog post: [The Problems With Modals, and How to Solve Them](https://noeldemartin.com/blog/the-problems-with-modals-and-how-to-solve-them).

## Installation

First, install it using your favorite package manager:

```sh
pnpm add @noeldemartin/vue-modals
```

Then, place a `` somewhere within your app (wherever you want to render modals).

Finally, open modals like this:

```js
import { showModal } from '@noeldemartin/vue-modals';
import MyModal from './MyModal.vue';

const { answer } = await showModal(MyModal, { question: 'How many golf balls fit into a Boeing 747?' });
```

## Usage

The second argument in the `showModal` function will be passed as component properties, and modals can be closed by emitting a `close` event (the payload of which will be returned in a promise). You can take advantage of Vue's TypeScript features to type both:

```vue


Close

defineEmits<{ close: [{ answer: string }] }>();
defineProps<{ question: string }>();

```

```ts
const { answer } = await showModal(MyModal, { question: 'How many golf balls fit into a Boeing 747?' });
// ๐Ÿ‘† answer will be typed as `string | undefined` (in case the modal is dismissed)
// ๐Ÿ‘† showModal's second argument will be typed as `{ question: string }`
```

### Modal responses

Given that modals can also be dismissed, the payload from the `close` event won't be directly returned in the promise. Instead, it will be merged into an object with a `dismissed` boolean:

```ts
defineEmits<{ close: [{ answer: string }] }>();
// ๐Ÿ‘† showModal will return `Promise<{ dismissed: false; answer: string } | { dismissed: true; answer?: undefined }>`

defineEmits<{ close: [string] }>();
// ๐Ÿ‘† showModal will return `Promise<{ dismissed: false; response: string } | { dismissed: true; response?: undefined }>`

defineEmits<{ somethingElse: [] }>();
// ๐Ÿ‘† showModal will return `Promise<{ dismissed: boolean }>`
```

### Customizing modals

If you want to have an overlay that sits behind your modals, you can use the `overlay` slot in the ``. You can also style the modals container passing class or style attributes, and use `` to implement animations:

```html





```

By default, modals will be rendered one after another in the DOM. However, some libraries may require to nest them (such as [Reka UI](https://reka-ui.com/docs/components/dialog#nested-dialog)). In that case, you can use the `nested` attribute to indicate that the rendering of child modals will happen in each modal component, rather than the root:

```vue

```

Modals don't need to use any special components, so they can be simple divs:

```html


My modal content



```

However, if you also want to configure some animations, you can use the `` component and pass the same attributes used in the `` component:

```html

My modal content

```

Finally, if you want to create your own modal wrapper, you can use `useModal()`. This will expose a couple of utilities you can use to work with the modal. By default, modals are removed from the DOM when closed, but you can pass the `{ removeOnClose: false }` option to disable it. If you do, make sure to call `remove()` to remove it yourself. Combined with `visible`, and using the native ``, you can achieve the same result as the `` component to customize it on your own:

```vue





import { useModal } from '@noeldemartin/vue-modals/composition';

const { visible, close, remove } = useModal({ removeOnClose: false });

```

You can also use `child` if you want to render nested modals using the ``:

```vue





import { useModal } from '@noeldemartin/vue-modals/composition';

const { child, visible, close, remove } = useModal({ removeOnClose: false });

```

However, keep in mind that if you're going to work with modals this way, you'll need to implement all the accessibility functionality on your own (focus trapping, keyboard events, etc.).

Instead, you'll probably want to integrate with an existing component library.

### Third-party integrations

In order to use this with component libraries, you'll need to follow similar techniques from the ones described in the previous section.

There are some built-in integrations, but feel free to look at the [src/integrations/](./src/integrations/) folder to learn more.

#### PrimeVue

You can use the `` component as usual, but import `` from `@noeldemartin/vue-modals/primevue` instead. Attributes will be passed down to PrimeVue's native [``](https://primevue.org/dialog/#api.dialog.props) component, such as `header`, `footer`, `maximizable`, etc.

```vue


My modal content


import Button from 'primevue/button';
import { Modal } from '@noeldemartin/vue-modals/primevue';

```

#### Shadcn

Following Shadcn's philosophy, this library doesn't export any code to integrate with the library. Instead, you'll want to copy & paste these into your project:

`src/components/ui/modal/Modal.vue`

```vue





import { Dialog, DialogContent } from '@/components/ui/dialog';
import { useModal, ModalComponent } from '@noeldemartin/vue-modals';

const { child, ...modal } = useModal({ removeOnClose: false });

function close() {
modal.close();

setTimeout(() => modal.remove(), 1000);
}

```

`src/components/ui/modal/ModalsPortal.vue`

```vue

import { ModalsPortal } from '@noeldemartin/vue-modals';

```

`src/components/ui/modal/index.ts`

```ts
export { default as Modal } from './Modal.vue';
export { default as ModalsPortal } from './ModalsPortal.vue';
```

Once that is set up, you should be able to create modals like this:

```vue



My Awesome Modal

Modal content goes here


import { Modal } from '@/components/ui/modal';
import { DialogHeader, DialogTitle } from '@/components/ui/dialog';

```