Skip to content

Instantly share code, notes, and snippets.

@blackfyre
Last active June 12, 2024 11:54
Show Gist options
  • Save blackfyre/ccd2a207948912594b5ac6cf67a11ef8 to your computer and use it in GitHub Desktop.
Save blackfyre/ccd2a207948912594b5ac6cf67a11ef8 to your computer and use it in GitHub Desktop.
Modals in Laravel Nova Tools
<template>
<modal @modal-close="handleClose">
<form
@submit.prevent="handleConfirm"
slot-scope="props"
class="bg-white rounded-lg shadow-lg overflow-hidden"
style="width: 460px"
>
<slot :uppercaseMode="uppercaseMode" :mode="mode">
<div class="p-8">
<heading :level="2" class="mb-6">{{ __('General Modal') }}</heading>
<p class="text-80 leading-normal">{{__('General modal contents')}}</p>
</div>
</slot>
<div class="bg-30 px-6 py-3 flex">
<div class="ml-auto">
<button type="button" data-testid="cancel-button" dusk="cancel-general-button" @click.prevent="handleClose" class="btn text-80 font-normal h-9 px-3 mr-3 btn-link">{{__('Cancel')}}</button>
<button id="confirm-delete-button" ref="confirmButton" data-testid="confirm-button" type="submit" class="btn btn-default btn-danger">{{ __(uppercaseMode) }}</button>
</div>
</div>
</form>
</modal>
</template>
<script>
export default {
name: "GeneralModal",
methods: {
handleClose() {
this.$emit('close')
},
handleConfirm() {
this.$emit('confirm')
},
},
/**
* Mount the component.
*/
mounted() {
this.$refs.confirmButton.focus()
},
}
</script>
<style scoped>
</style>
<template>
<div>
<button @click="openModal">{{__('Open Modal')}}</button>
<portal to="modals">
<transition name="fade">
<general-modal
v-if="modalOpen"
@confirm="confirmModal"
@close="closeModal"
/>
</transition>
</portal>
</div>
</template>
<script>
import GeneralModal from './parts/modals/GeneralModal.vue';
export default {
props: ["resourceName", "resourceId", "field"],
data() {
return {
modalOpen: false
}
},
components: {
GeneralModal
},
mounted() {
},
methods: {
openModal() {
this.modalOpen = true;
},
confirmModal() {
this.modalOpen = false;
},
closeModal() {
this.modalOpen = false;
}
}
};
</script>
@blackfyre
Copy link
Author

blackfyre commented Mar 17, 2020

@henryavila This isn't for a general Vue.js application! This snippet expects to be in an environment provided by Laravel Nova.
If you're interested in a general use modal component for Vue.js, I strongly suggest you check out the examples section in the docs: https://vuejs.org/v2/examples/modal.html

@henryavila
Copy link

Thanks for quick response @blackfyre. In fact I'm using Laravel Nova. I'm new in VueJS and automatically the way Laravel Nova use it. I'm a backend (php) developer starting with the frontend with Laravel Nova.

If you could give some direction, I will be so gald.
For example. I create a Card in Laravel Nova. And whant this card to display a Modal. In this case, the Tool.vue in your gist represent my Card vue file? Then where do I put the GeneralModal.vue (from your gist) file?

@blackfyre
Copy link
Author

@henryavila
Thanks for the background, this puts things into perspective 😄
Haven't developed a Card before, but the match between the Card & Tool vue files seems reasonable.
As for placing the GeneralModal.vue file is totally up to you! At some point, you'll have to reference it in your card vue file.

@finoghentov
Copy link

finoghentov commented Mar 27, 2020

Hello everyone, today i face one problem that I can't use improted component inside this <modal> tag, maybe I do something wrong, I wanted to use vuedraggable to make some functionality inside my project, but i couldn't because <draggable> tag is not initialize like draggable component. After I tried to make my own component just with test content like "Hello world" and import that component and use it inside <modal> tag, the same result. Laravel Nova

@bamatic
Copy link

bamatic commented Mar 28, 2020

If i need two modals in my tool.vue, i need to make two but this does not work I mean if i have two components GeneralModal and TableModal both using difference betwen componentes is only the contenu in the Nova modal's slot, how could I have two modals ?

@provydon
Copy link

thanks for this mehn

@bmoex
Copy link

bmoex commented Feb 22, 2023

Thank you for your sharing; I fixed it for Nova 4 like this:
Similar to your implementation but changed to match laravel/nova/resources/js/components/Modals/DeleteResourceModal.vue

GeneralModal.vue

<template>
    <Modal @modal-close="handleClose"
           :show="show"
           role="alertdialog"
           size="sm">
        <form
            @submit.prevent="handleConfirm"
            class="mx-auto bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden"
        >
            <slot/>
            <ModalFooter>
                <div class="ml-auto">
                    <LinkButton
                        type="button"
                        data-testid="cancel-button"
                        dusk="cancel-delete-button"
                        @click.prevent="handleClose"
                        class="mr-3"
                    >
                        {{ __('Cancel') }}
                    </LinkButton>

                    <LoadingButton
                        ref="confirmButton"
                        dusk="confirm-delete-button"
                        :processing="working"
                        :disabled="working"
                        component="DangerButton"
                        type="submit"
                    >
                        {{ confirmButtonText }}
                    </LoadingButton>
                </div>
            </ModalFooter>
        </form>
    </Modal>
</template>

<script setup>
import {ref, watchEffect} from 'vue'

const props = defineProps({
    confirmButtonText: {
        type: String,
        default: 'Delete'
    }
})
const emit = defineEmits(['close', 'confirm'])

const confirmButton = ref(null)
watchEffect(() => {
    // Only focus when mounted (e.g. if hidden through :show)
    if (confirmButton.value) {
        confirmButton.value.focus()
    }
})

const handleClose = () => {
    emit('close')
};

const handleConfirm = () => {
    emit('confirm')
};
</script>

Tool.vue

...
        <GeneralModal
            :show="modalOpen"
            confirmButtonText="Delete"
            @close="closeModal"
            @confirm="confirmModal">
            <ModalHeader>Delete resource</ModalHeader>
            <ModalContent>
                <p class="leading-normal">
                    Are you sure you want to delete the resource?
                </p>
            </ModalContent>
        </GeneralModal>
...

@BobbyBorisov
Copy link

@bmoex thanks for the snippet. any idea how to trigger the auto close on click away functionality? I can see that Nova 4 native modals have it

@blackfyre
Copy link
Author

@bmoex thanks for the snippet. any idea how to trigger the auto close on click away functionality? I can see that Nova 4 native modals have it

You really can't do that without modifying the nova code as it usually involves a click listener on the backdrop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment