laravelvuejs2laravel-nova

How do you use confirm dialogues in a custom Laravel Nova tool?


Is it possible to use the built in Laravel Nova confirm dialogue in your own tool? All I would like to use is interact with it how Nova does itself.

The docs are quite light on the JS topic, as the only built in UI you seem to be able to work with is the toasted plugin: https://nova.laravel.com/docs/1.0/customization/frontend.html#javascript

Built in Laravel Nova confirm dialogue


Solution

  • You can use <modal> component whenever you want.

    Here is how it work internally in Nova:

    <template>
        <modal
            data-testid="confirm-action-modal"
            tabindex="-1"
            role="dialog"
            @modal-close="handleClose"
            class-whitelist="flatpickr-calendar"
        >
            <form
                autocomplete="off"
                @keydown="handleKeydown"
                @submit.prevent.stop="handleConfirm"
                class="bg-white rounded-lg shadow-lg overflow-hidden"
                :class="{
                    'w-action-fields': action.fields.length > 0,
                    'w-action': action.fields.length == 0,
                }"
            >
                <div>
                    <heading :level="2" class="border-b border-40 py-8 px-8">{{ action.name }}</heading>
    
                    <p v-if="action.fields.length == 0" class="text-80 px-8 my-8">
                        {{ __('Are you sure you want to run this action?') }}
                    </p>
    
                    <div v-else>
                        <!-- Validation Errors -->
                        <validation-errors :errors="errors" />
    
                        <!-- Action Fields -->
                        <div class="action" v-for="field in action.fields" :key="field.attribute">
                            <component
                                :is="'form-' + field.component"
                                :errors="errors"
                                :resource-name="resourceName"
                                :field="field"
                            />
                        </div>
                    </div>
                </div>
    
                <div class="bg-30 px-6 py-3 flex">
                    <div class="flex items-center ml-auto">
                        <button
                            dusk="cancel-action-button"
                            type="button"
                            @click.prevent="handleClose"
                            class="btn text-80 font-normal h-9 px-3 mr-3 btn-link"
                        >
                            {{ __('Cancel') }}
                        </button>
    
                        <button
                            ref="runButton"
                            dusk="confirm-action-button"
                            :disabled="working"
                            type="submit"
                            class="btn btn-default"
                            :class="{
                                'btn-primary': !action.destructive,
                                'btn-danger': action.destructive,
                            }"
                        >
                            <loader v-if="working" width="30"></loader>
                            <span v-else>{{ __('Run Action') }}</span>
                        </button>
                    </div>
                </div>
            </form>
        </modal>
    </template>
    
    <script>
    export default {
        props: {
            working: Boolean,
            resourceName: { type: String, required: true },
            action: { type: Object, required: true },
            selectedResources: { type: [Array, String], required: true },
            errors: { type: Object, required: true },
        },
        /**
         * Mount the component.
         */
        mounted() {
            // If the modal has inputs, let's highlight the first one, otherwise
            // let's highlight the submit button
            if (document.querySelectorAll('.modal input').length) {
                document.querySelectorAll('.modal input')[0].focus()
            } else {
                this.$refs.runButton.focus()
            }
        },
        methods: {
            /**
             * Stop propogation of input events unless it's for an escape or enter keypress
             */
            handleKeydown(e) {
                if (['Escape', 'Enter'].indexOf(e.key) !== -1) {
                    return
                }
                e.stopPropagation()
            },
            /**
             * Execute the selected action.
             */
            handleConfirm() {
                this.$emit('confirm')
            },
            /**
             * Close the modal.
             */
            handleClose() {
                this.$emit('close')
            },
        },
    }
    </script>
    

    Here is simplified example:

    <modal>
        <form
                autocomplete="off"
                class="bg-white rounded-lg shadow-lg overflow-hidden"
        >
            <div>
                <heading :level="2" class="border-b border-40 py-8 px-8">test</heading>
                test
            </div>
            <div class="bg-30 px-6 py-3 flex">
                <div class="flex items-center ml-auto">
                    <button
                            type="button"
                            class="btn text-80 font-normal h-9 px-3 mr-3 btn-link"
                    >
                        {{ __('Cancel') }}
                    </button>
    
                    <button
                            ref="runButton"
                            type="submit"
                            class="btn-danger"
                    >
                        <span>{{ __('Run Action') }}</span>
                    </button>
                </div>
            </div>
        </form>
    </modal>