visua-vue/src/components/modal/VModal.vue
2025-07-28 00:58:31 +02:00

138 lines
3.3 KiB
Vue

<script setup lang="ts">
import Dialog from 'primevue/dialog';
import type IVModal from './IVModal.type';
import { useId, computed, watch, ref } from 'vue';
import VButtonGroup from '../button/VButtonGroup.vue';
import VButton from '../button/VButton.vue';
import styles from '@visua/typography.module.css';
const props = withDefaults(defineProps<IVModal>(), {
modalId: () => useId(),
actions: () => [],
icon: undefined,
size: 'md',
closeButtonLabel: 'Fermer',
closeButtonTitle: 'Fermer la fenêtre modale',
title: undefined,
visible: false,
isAlert: false,
breakpoints: undefined,
modal: true,
dismissableMask: false,
blockScroll: true,
position: 'center',
maximizable: false,
draggable: true,
showHeader: true,
closeOnEscape: true,
})
const role = computed(() => {
return props.isAlert ? 'alertdialog' : 'dialog'
})
const modalSize = computed(() => {
switch (props.size) {
case 'lg':
return `width: 49.5rem;`
case 'md':
return `width: 36.75rem;`
case 'sm':
return `width: 24rem;`
default:
return `width: 36.75rem;`
}
})
const emit = defineEmits([
'update:visible',
'hide',
'after-hide',
'show',
'maximize',
'unmaximize',
'dragstart',
'dragend'
]);
const localVisible = ref(props.visible);
watch(() => props.visible, (newVal) => {
if(localVisible.value !== newVal){
localVisible.value = newVal;
}
})
watch(localVisible, (newVal) => {
if(props.visible !== newVal){
emit('update:visible', newVal);
}
});
</script>
<template>
<Dialog
:header="props.title"
id="modal"
:modal="props.modal"
:dismissable-mask="props.dismissableMask"
:role="role"
:aria-model="true"
:aria-labelledby="`modal-${props.modalId}-dialog`"
ref="modal"
:style="modalSize"
:breakpoints="props.breakpoints"
:block-scroll="props.blockScroll"
:position="props.position"
:maximizable="props.maximizable"
:draggable="props.draggable"
:show-header="props.showHeader"
:close-on-escape="props.closeOnEscape"
v-model:visible="localVisible"
@update:visible="emit('update:visible', $event)"
@after-hide="emit('after-hide', $event)"
@dragend="emit('dragend', $event)"
@dragstart="emit('dragstart', $event)"
@hide="emit('hide', $event)"
@maximize="emit('maximize', $event)"
@unmaximize="emit('unmaximize', $event)"
@show="emit('show', $event)"
>
<template #header>
<slot name="header" v-if="props.icon !== undefined">
<span :class="[styles['titles-H6-XXS']]">
<i :class="props.icon"></i>
{{ props.title }}
</span>
</slot>
</template>
<slot name="content"/>
<template #footer>
<slot name="footer">
<VButtonGroup
v-if="props.actions?.length"
inline-layout-when="always"
align="right"
:buttons="props.actions"
size="sm"
title="groupe de boutons"
/>
</slot>
</template>
<template #closebutton="{closeCallback}">
<VButton
tertiary
noOutline
size="sm"
icon="ri-close-line"
iconRight
@click="closeCallback"
:label="closeButtonLabel"
:title="closeButtonTitle"
ref="closeBtn"
aria-controls="modal-1"
type="button"
/>
</template>
</Dialog>
</template>