Vue Final Modal

repository·master·Indexed 21 days ago

https://github.com/vue-final/vue-final-modal

A lightweight (~4.5kB), styleless modal library for Vue 3 and Nuxt 3. It provides core modal functionality including focus trapping, teleportation, and z-index management via the <VueFinalModal> component and useModal() composable.

Tokens
18.6K
Snippets
55
Records
71
Agent score
74%

What's inside vue-final-modal

  1. Overview of Vue Final Modal 4

    master

    Vue Final Modal 4 is a powerful and lightweight modal library designed specifically for Vue 3. It provides a flexible way to manage modals in Vue applications.

    For existing users migrating from older versions, a Migration guide from v3 is available. If you require older versions, documentation is maintained for vue-final-modal@3.x (Vue 3) and vue-final-modal@2.x (Vue 2).

  2. Core concepts of Vue Final Modal

    master

    Vue Final Modal is a collection of Vue components and composables designed to manage modals in Vue.js applications. It focuses on providing core modal functionality while remaining lightweight (~4.5kB) and unstyled.

    Key Abstractions

    • <VueFinalModal> Component: A styleless component that provides essential modal features (like focus trapping and teleportation) but leaves the layout and styling to the developer via slots.
    • useModal() Composable: The primary way to manage modals programmatically.

    Built-in Features

    • SSR Support: Full support for Nuxt 3.
    • Teleportation: Modals are teleported to 'body' by default.
    • Accessibility: Automatically traps keyboard focus within the modal element using focus-trap.
    • Z-Index Management: Provides full control over nested modal z-indices via the zIndexFn prop.
    • Customizable Transitions: Supports custom <Transition> components for both the modal content and the overlay.
    • Styleless Design: The library does not include complex CSS, allowing you to define your own modal styles.
  3. Register the vue-final-modal plugin

    master

    In version 4, you must create a plugin using createVfm() and register it to provide the necessary shared context via provide/inject.

    ### Vue 3
    ```ts [main.ts]
    import { createApp } from 'vue'
    import { createVfm } from 'vue-final-modal'
    import App from './App.vue'
    
    const app = createApp(App)
    
    const vfm = createVfm()
    app.use(vfm).mount('#app')

    Nuxt 3

    import { createVfm } from 'vue-final-modal'
    
    export default defineNuxtPlugin((nuxtApp) => {
      const vfm = createVfm() as any
    
      nuxtApp.vueApp.use(vfm)
    })
  4. Replace $vfm.show with useModal

    master

    The dynamic modal method $vfm.show() has been removed. You must now use the useModal() composable to create dynamic modals. Additionally, the params property is removed in favor of attrs for better TypeScript support.

    Migration Example

    Old (3.x):

    this.$vfm.show({
      component: ModalConfirm,
      bind: {
        name: 'ModalConfirmName'
      },
      on: {
        confirm() {
          this.$vfm.hide('ModalConfirmName')
        },
        opened() {
          console.log('modal opened')
        },
      },
      slots: {
        default: '<p>The content of the modal</p>'
      }
    })

    New (4.0):

    const { open, close } = useModal({
      component: ModalConfirm,
      attrs: {
        title: 'Hello World!',
        onConfirm() {
          close()
        },
        onOpened() {
          console.log('modal opened')
        }
      },
      slots: {
        default: '<p>The content of the modal</p>',
      },
    })
    open()
    const { open, close } = useModal({
      component: ModalConfirm,
      attrs: {
        title: 'Hello World!',
        onConfirm() {
          close()
        },
        onOpened() {
          console.log('modal opened')
        }
      },
      slots: {
        default: '<p>The content of the modal</p>',
      },
    })
    open()
  5. Pass slots to a dynamic modal using `useModal()`

    master

    You can pass content to the modal's slots using three different methods:

    1. Using a String (HTML)

    Pass a string of HTML to the default slot. Warning: Use this only with trusted content to avoid XSS attacks.

    const modalInstance = useModal({
      component: VueFinalModal,
      slots: {
        default: '<p>The content of the modal</p>'
      }
    })

    2. Using a Component

    Pass a Vue component directly to the slot. This component will be rendered without any props or events passed to it.

    import ModalContent from './ModalContent.vue'
    
    const modalInstance = useModal({
      component: VueFinalModal,
      slots: {
        default: ModalContent
      }
    })

    3. Using useModalSlot() for Components with Props and Events

    To pass props and events to a component inside a slot, use the useModalSlot() helper. This provides better TypeScript DX.

    import { VueFinalModal, useModal, useModalSlot } from 'vue-final-modal'
    import ModalContent from './ModalContent.vue'
    
    const modalInstance = useModal({
      component: VueFinalModal,
      slots: {
        default: useModalSlot({
          component: ModalContent,
          attrs: {
            title: 'Hello world!',
            onConfirm() { /* handle confirm */ }
          }
        })
      }
    })
    import { VueFinalModal, useModal, useModalSlot } from 'vue-final-modal'
    import ModalContent from './ModalContent.vue'
    
    const modalInstance = useModal({
      component: VueFinalModal,
      slots: {
        default: useModalSlot({
          component: ModalContent,
          attrs: {
            title: 'Hello world!',
            onConfirm() {  }
          }
        })
      }
    })
  6. Add <ModalsContainer> to your Vue tree

    master

    To use dynamic modals created via useModal(), you must include the <ModalsContainer> component exactly once in your application's component tree.

    ### Vue 3
    ```vue [App.vue]
    <script setup lang="ts">
    import { ModalsContainer } from 'vue-final-modal'
    </script>
    
    <template>
      <div>
        ...
        <ModalsContainer />
      </div>
    </template>

    Nuxt 3

    <script setup lang="ts">
    import { ModalsContainer } from 'vue-final-modal'
    </script>
    
    <template>
      <div>
        <slot />
        <ModalsContainer />
      </div>
    </template>
  7. Nest modals using useModal()

    master

    You can nest modals by calling useModal() multiple times and triggering the .open() method of a second modal from within the lifecycle or event handlers (like onConfirm) of a first modal.

    To ensure all modals render correctly, you must include the <ModalsContainer /> component in your application template. Each call to useModal() creates a unique modal instance that can be controlled independently.

    <script setup lang="ts">
    import { ModalsContainer, useModal } from 'vue-final-modal'
    import MyComponent from './MyComponent.vue'
    
    // Define the first modal
    const modalFirst = useModal({
      component: MyComponent,
      attrs: {
        title: 'First Modal',
        onConfirm() {
          // Open the second modal when the first one confirms
          modalSecond.open()
        },
      },
    })
    
    // Define the second modal
    const modalSecond = useModal({
      component: MyComponent,
      attrs: {
        title: 'Second Modal',
        onConfirm() {
          modalSecond.close()
        },
      },
    })
    
    function openFirst() {
      modalFirst.open()
    }
    </script>
    
    <template>
      <button @click="openFirst">Open Modal</button>
    
      <!-- Required for modals to render -->
      <ModalsContainer />
    </template>