Modal

BpModal is a modal component that allows for easy access throughout templates and other components. Uses Portal-Vue to always send content to the end of body. Will also lazyload any scripts provided.

For lazyloading to work, HTML must be an encoded URI. Twig’s escape filter can be used to achieve this.

Vue Component

BpModal

Props

  • block - the block class (defaults to ‘accordion’)
  • openButtonElement - the open button element (defaults to ‘openButton’)
  • overlayElement - the overlay element (defaults to ‘overlay’)
  • containerElement - the container element (defaults to ‘container’)
  • loadingSpinnerElement - the loading spinner element (defaults to ‘loadingSpinner’)
  • contentElement - the content element (defaults to ‘content’)
  • closeButtonElement - the close button element (defaults to ‘closeButton’)
  • wrapperElement - the wrapper element (defaults to ‘wrapper’)
  • closeButtonIconElement - the close button icon element (defaults to ‘closeButtonIcon’)

Slots

BpModal also provides some named slots that can be used to override certain elements

  • open-button - To provide a custom button for opening the modal (openModal method is passed through the open-button slot prop)
  • close-button - To provide a custom button for closing the modal (closeModal method is passed through the close-button slot prop)
  • loading-spinner - the provide a custom spinner (loading data attribute is passed through the loading slot prop)

    Parameters

None.

<bp-modal>
    <template #open-button="{ open }">
        <button class="button" @click="open">Watch Video</button>
    </template>
    &lt;script src=&quot;//fast.wistia.com/embed/medias/j38ihh83m5.jsonp&quot; async&gt;&lt;/script&gt;
    &lt;script src=&quot;//fast.wistia.com/assets/external/E-v1.js&quot; async&gt;&lt;/script&gt;
    &lt;div class=&quot;wistia_embed wistia_async_j38ihh83m5&quot; style=&quot;height:349px;width:620px&quot;&gt;&amp;nbsp;&lt;/div&gt;
</bp-modal>
  • Content:
    <template>
        <slot
            name="open-button"
            :open="open"
        />
        <teleport to="body">
            <div
                v-if="isOpen"
                :class="block"
            >
                <div
                    :class="`${block}__${overlayElement}`"
                >
                    <div
                        ref="modalWrapper"
                        :class="`${block}__${wrapperElement}`"
                    >
                        <div
                            :class="`${block}__${containerElement}`"
                        >
                            <div
                                ref="modalContent"
                                :class="`${block}__${contentElement}`"
                                tabindex="0"
                            >
                                <slot />
                            </div>
                            <slot
                                v-if="loading"
                                name="loading-spinner"
                                :loading="loading"
                            >
                                <img
                                    :class="`${block}__${loadingSpinnerElement}`"
                                    src="/resources/media/spinner.gif"
                                    alt=""
                                >
                            </slot>
                        </div>
                        <slot
                            name="close-button"
                            :close="close"
                        >
                            <button
                                :class="`${block}__${closeButtonElement}`"
                                @click="close"
                            >
                                Close Modal
                                <svg
                                    :class="`${block}__${closeButtonIconElement}`"
                                    viewBox="0 0 24 24"
                                    stroke="currentColor"
                                    fill="none"
                                >
                                    <line
                                        x1="18"
                                        y1="6"
                                        x2="6"
                                        y2="18"
                                    />
                                    <line
                                        x1="6"
                                        y1="6"
                                        x2="18"
                                        y2="18"
                                    />
                                </svg>
                            </button>
                        </slot>
                    </div>
                </div>
            </div>
        </teleport>
    </template>
    
    <script setup>
    import { nextTick, ref, watch } from 'vue'
    import useOpenable from '@resources/js/components/UseOpenable.vue'
    import useLazyEmbed from '@resources/js/components/UseLazyEmbed.js'
    
    defineProps({
        block: {
            type: String,
            default: 'modal',
        },
    
        overlayElement: {
            type: String,
            default: 'overlay',
        },
    
        containerElement: {
            type: String,
            default: 'container',
        },
    
        loadingSpinnerElement: {
            type: String,
            default: 'loadingSpinner',
        },
    
        contentElement: {
            type: String,
            default: 'content',
        },
    
        closeButtonElement: {
            type: String,
            default: 'closeButton',
        },
    
        wrapperElement: {
            type: String,
            default: 'wrapper',
        },
    
        closeButtonIconElement: {
            type: String,
            default: 'closeButtonIcon',
        },
    })
    
    const loading = ref(false)
    const modalContent = ref(null)
    const modalWrapper = ref(null)
    
    const { isOpen, open, close } = useOpenable(modalWrapper)
    const { loadEmbed } = useLazyEmbed(modalContent)
    
    watch(isOpen, async isOpen => {
        if (isOpen) {
            loading.value = true
            lockScroll()
            await nextTick(loadEmbed)
        } else {
            unlockScroll()
        }
    
        loading.value = false
    })
    
    const lockScroll = () => {
        document.documentElement.classList.add('-lock')
    }
    
    const unlockScroll = () => {
        document.documentElement.classList.remove('-lock')
    }
    </script>
    
  • URL: /components/raw/modal/BpModal.vue
  • Filesystem Path: resources/styles/organisms/modal/BpModal.vue
  • Size: 4.1 KB
  • Content:
    @use "/resources/styles/common" as *;
    @use "/resources/styles/config";
    
    .modal {
        $b: &;
    
        &__overlay {
            display: flex;
            align-items: center;
            justify-content: center;
            position: fixed;
            top: 0;
            right: 0;
            bottom: 0;
            left: 0;
            background-color: rgba(config.$black, .5);
        }
    
        &__wrapper {
            position: relative;
            max-width: 60rem;
            background-color: config.$white;
            padding: config.$padding;
            max-height: 100vh;
        }
    
        &__loadingSpinner {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }
    
        &__closeButton {
            background: none;
            border: none;
            padding: 0;
            position: absolute;
            top: 0.2rem;
            right: 0.5rem;
            font-size: 0;
        }
    
        &__closeButtonIcon {
            width: 1rem;
        }
    }
    
    // Scroll Lock
    html.-lock,
    html.-lock body {
        position: relative;
        overflow: hidden;
        touch-action: none;
        -ms-touch-action: none;
    }
    
  • URL: /components/raw/modal/_index.scss
  • Filesystem Path: resources/styles/organisms/modal/_index.scss
  • Size: 1.1 KB