Files
nademitex_user/src/components/customers/MeetingReportOverlay.vue
T

190 lines
4.0 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import { onBeforeUnmount, watch } from 'vue'
import MeetingReportForm from '@/components/customers/MeetingReportForm.vue'
const props = defineProps<{
open: boolean
customerId: string
}>()
const emit = defineEmits<{
close: []
success: []
}>()
function close() {
emit('close')
}
function onSuccess() {
emit('success')
}
function onKeydown(event: KeyboardEvent) {
if (event.key === 'Escape' && props.open) close()
}
watch(
() => props.open,
(isOpen) => {
document.body.style.overflow = isOpen ? 'hidden' : ''
if (isOpen) {
window.addEventListener('keydown', onKeydown)
} else {
window.removeEventListener('keydown', onKeydown)
}
},
{ immediate: true },
)
onBeforeUnmount(() => {
document.body.style.overflow = ''
window.removeEventListener('keydown', onKeydown)
})
</script>
<template>
<Teleport to="body">
<Transition name="meeting-report-sheet">
<div
v-if="open"
class="meeting-report-overlay"
role="dialog"
aria-modal="true"
aria-labelledby="meeting-report-title"
@click.self="close"
>
<div class="meeting-report-sheet">
<div class="meeting-report-sheet__handle" aria-hidden="true" />
<header class="meeting-report-sheet__head">
<h2 id="meeting-report-title">ثبت گزارش ملاقات</h2>
<button
type="button"
class="meeting-report-sheet__close"
aria-label="بستن"
@click="close"
>
×
</button>
</header>
<MeetingReportForm
v-if="customerId"
:key="customerId"
:customer-id="customerId"
@success="onSuccess"
/>
</div>
</div>
</Transition>
</Teleport>
</template>
<style scoped>
.meeting-report-overlay {
position: fixed;
inset: 0;
z-index: 200;
background: rgba(0, 0, 0, 0.45);
display: flex;
align-items: flex-end;
justify-content: center;
padding: 0;
}
.meeting-report-sheet {
width: 100%;
max-width: 560px;
max-height: min(94dvh, 760px);
overflow-y: auto;
background: var(--color-bg);
color: var(--color-text);
border-radius: 20px 20px 0 0;
padding: 10px 16px calc(16px + env(safe-area-inset-bottom, 0px));
box-shadow: var(--shadow-md);
display: flex;
flex-direction: column;
gap: 16px;
}
.meeting-report-sheet__handle {
width: 40px;
height: 4px;
border-radius: 999px;
background: var(--color-border);
margin: 4px auto 0;
}
.meeting-report-sheet__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.meeting-report-sheet__head h2 {
margin: 0;
font-size: 1.0625rem;
font-weight: 700;
}
.meeting-report-sheet__close {
width: 36px;
height: 36px;
border: none;
border-radius: 999px;
background: var(--color-surface);
color: var(--color-text-muted);
font-size: 1.5rem;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
padding: 0;
}
.meeting-report-sheet-enter-active,
.meeting-report-sheet-leave-active {
transition: opacity 0.2s ease;
}
.meeting-report-sheet-enter-active .meeting-report-sheet,
.meeting-report-sheet-leave-active .meeting-report-sheet {
transition: transform 0.22s ease;
}
.meeting-report-sheet-enter-from,
.meeting-report-sheet-leave-to {
opacity: 0;
}
.meeting-report-sheet-enter-from .meeting-report-sheet,
.meeting-report-sheet-leave-to .meeting-report-sheet {
transform: translateY(100%);
}
@media (min-width: 768px) {
.meeting-report-overlay {
align-items: center;
padding: 24px;
}
.meeting-report-sheet {
border-radius: 16px;
max-width: 520px;
max-height: min(88dvh, 720px);
padding: 20px 20px 24px;
background: var(--color-surface);
}
.meeting-report-sheet__handle {
display: none;
}
.meeting-report-sheet-enter-from .meeting-report-sheet,
.meeting-report-sheet-leave-to .meeting-report-sheet {
transform: translateY(16px) scale(0.98);
}
}
</style>