Files
2024-09-01 18:54:23 +05:00

189 lines
4.6 KiB
Vue

<template>
<Modal
:show="show"
@close-via-escape="handlePreventModalAbandonmentOnClose"
role="dialog"
:size="action.modalSize"
:modal-style="action.modalStyle"
:use-focus-trap="usesFocusTrap"
>
<form
ref="theForm"
autocomplete="off"
@change="onUpdateFormStatus"
@submit.prevent.stop="$emit('confirm')"
:data-form-unique-id="formUniqueId"
class="bg-white dark:bg-gray-800"
:class="{
'rounded-lg shadow-lg overflow-hidden space-y-6':
action.modalStyle === 'window',
'flex flex-col justify-between h-full':
action.modalStyle === 'fullscreen',
}"
>
<div
class="space-y-6"
:class="{
'overflow-hidden overflow-y-auto': action.modalStyle === 'fullscreen',
}"
>
<ModalHeader v-text="action.name" />
<!-- Confirmation Text -->
<p
v-if="action.confirmText"
class="px-8"
:class="{ 'text-red-500': action.destructive }"
>
{{ action.confirmText }}
</p>
<!-- Action Fields -->
<div v-if="action.fields.length > 0">
<div
class="action"
v-for="field in action.fields"
:key="field.attribute"
>
<component
:is="'form-' + field.component"
:errors="errors"
:resource-name="resourceName"
:field="field"
:show-help-text="true"
:form-unique-id="formUniqueId"
:mode="
action.modalStyle === 'fullscreen'
? 'action-fullscreen'
: 'action-modal'
"
:sync-endpoint="syncEndpoint"
@field-changed="onUpdateFieldStatus"
/>
</div>
</div>
</div>
<ModalFooter>
<div class="flex items-center ml-auto">
<CancelButton
component="button"
type="button"
dusk="cancel-action-button"
class="ml-auto mr-3"
@click="$emit('close')"
>
{{ action.cancelButtonText }}
</CancelButton>
<Button
type="submit"
ref="runButton"
dusk="confirm-action-button"
:loading="working"
variant="solid"
:state="action.destructive ? 'danger' : 'default'"
>
{{ action.confirmButtonText }}
</Button>
</div>
</ModalFooter>
</form>
</Modal>
</template>
<script>
import { PreventsModalAbandonment } from '@/mixins'
import isObject from 'lodash/isObject'
import { uid } from 'uid/single'
import { Button } from 'laravel-nova-ui'
export default {
components: {
Button,
},
emits: ['confirm', 'close'],
mixins: [PreventsModalAbandonment],
props: {
action: { type: Object, required: true },
endpoint: { type: String, required: false },
errors: { type: Object, required: true },
resourceName: { type: String, required: true },
selectedResources: { type: [Array, String], required: true },
show: { type: Boolean, default: false },
working: Boolean,
},
data: () => ({
loading: true,
formUniqueId: uid(),
}),
created() {
document.addEventListener('keydown', this.handleKeydown)
},
mounted() {
this.loading = false
},
beforeUnmount() {
document.removeEventListener('keydown', this.handleKeydown)
},
methods: {
/**
* Prevent accidental abandonment only if form was changed.
*/
onUpdateFormStatus() {
this.updateModalStatus()
},
onUpdateFieldStatus() {
this.onUpdateFormStatus()
},
handlePreventModalAbandonmentOnClose(event) {
this.handlePreventModalAbandonment(
() => {
this.$emit('close')
},
() => {
event.stopPropagation()
}
)
},
},
computed: {
syncEndpoint() {
let searchParams = new URLSearchParams({ action: this.action.uriKey })
if (this.selectedResources === 'all') {
searchParams.append('resources', 'all')
} else {
this.selectedResources.forEach(resource => {
searchParams.append(
'resources[]',
isObject(resource) ? resource.id.value : resource
)
})
}
return (
(this.endpoint || `/nova-api/${this.resourceName}/action`) +
'?' +
searchParams.toString()
)
},
usesFocusTrap() {
return this.loading === false && this.action.fields.length > 0
},
},
}
</script>