feat: enhance modal positioning and styling across components
- Implemented dynamic positioning for modals in ErrorModal, ComprovantePonto, and RegistroPonto components to ensure they are centered based on the viewport and container dimensions. - Updated modal styles to improve visual consistency, including backdrop opacity adjustments and enhanced animations. - Refactored modal handling logic to include scroll and resize event listeners for better responsiveness during user interactions.
This commit is contained in:
@@ -851,6 +851,94 @@
|
||||
});
|
||||
|
||||
const saldoPositivo = $derived(historicoSaldo ? historicoSaldo.saldoMinutos >= 0 : false);
|
||||
|
||||
// Posicionamento dos modais
|
||||
let modalPosition = $state<{ top: number; left: number } | null>(null);
|
||||
|
||||
// Função para calcular a posição baseada no container central da página
|
||||
function calcularPosicaoModal() {
|
||||
// Procurar pelo elemento container-central
|
||||
const containerCentral = document.getElementById('container-central');
|
||||
|
||||
if (containerCentral) {
|
||||
const rect = containerCentral.getBoundingClientRect();
|
||||
const viewportWidth = window.innerWidth;
|
||||
const viewportHeight = window.innerHeight;
|
||||
|
||||
// Centralizar baseado na largura do container central
|
||||
const containerCenterX = rect.left + (rect.width / 2);
|
||||
const containerTop = rect.top;
|
||||
|
||||
// Posicionar o modal centralizado verticalmente na viewport, mas respeitando o container
|
||||
const top = Math.max(50, Math.min(containerTop + 100, viewportHeight / 2));
|
||||
|
||||
return {
|
||||
top: top,
|
||||
left: containerCenterX
|
||||
};
|
||||
}
|
||||
|
||||
// Se não encontrar, usar posição padrão (centro da tela)
|
||||
return null;
|
||||
}
|
||||
|
||||
// Atualizar posição quando os modais forem abertos ou quando a página rolar
|
||||
$effect(() => {
|
||||
if (mostrandoWebcam || mostrandoModalConfirmacao || aguardandoProcessamento || mostrarModalErro) {
|
||||
// Aguardar um pouco para garantir que o DOM está atualizado
|
||||
setTimeout(() => {
|
||||
modalPosition = calcularPosicaoModal();
|
||||
}, 10);
|
||||
|
||||
// Adicionar listener de scroll para atualizar posição
|
||||
const handleScroll = () => {
|
||||
modalPosition = calcularPosicaoModal();
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', handleScroll, true);
|
||||
window.addEventListener('resize', handleScroll);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll, true);
|
||||
window.removeEventListener('resize', handleScroll);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// Função para obter estilo do modal baseado na posição calculada
|
||||
function getModalStyle() {
|
||||
if (modalPosition) {
|
||||
// Garantir que o modal não saia da viewport
|
||||
const viewportWidth = window.innerWidth;
|
||||
const viewportHeight = window.innerHeight;
|
||||
const modalWidth = 800; // Aproximadamente max-w-2xl ou max-w-3xl
|
||||
const modalHeight = Math.min(viewportHeight * 0.9, 600);
|
||||
|
||||
let left = modalPosition.left;
|
||||
let top = modalPosition.top;
|
||||
|
||||
// Ajustar se o modal sair da viewport à direita
|
||||
if (left + (modalWidth / 2) > viewportWidth) {
|
||||
left = viewportWidth - (modalWidth / 2) - 20;
|
||||
}
|
||||
// Ajustar se o modal sair da viewport à esquerda
|
||||
if (left - (modalWidth / 2) < 20) {
|
||||
left = (modalWidth / 2) + 20;
|
||||
}
|
||||
// Ajustar se o modal sair da viewport abaixo
|
||||
if (top + modalHeight > viewportHeight) {
|
||||
top = viewportHeight - modalHeight - 20;
|
||||
}
|
||||
// Ajustar se o modal sair da viewport acima
|
||||
if (top < 20) {
|
||||
top = 20;
|
||||
}
|
||||
|
||||
return `position: fixed; top: ${top}px; left: ${left}px; transform: translate(-50%, 0); max-width: ${Math.min(modalWidth, viewportWidth - 40)}px;`;
|
||||
}
|
||||
// Se não houver posição calculada, centralizar na tela
|
||||
return 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);';
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="space-y-6">
|
||||
@@ -1330,22 +1418,22 @@
|
||||
<!-- Modal Webcam -->
|
||||
{#if mostrandoWebcam}
|
||||
<div
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4"
|
||||
class="fixed inset-0 z-50 pointer-events-none"
|
||||
style="animation: fadeIn 0.2s ease-out;"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="modal-webcam-title"
|
||||
>
|
||||
<!-- Backdrop -->
|
||||
<!-- Backdrop leve -->
|
||||
<div
|
||||
class="absolute inset-0 bg-black/50 backdrop-blur-sm transition-opacity duration-200"
|
||||
class="absolute inset-0 bg-black/20 transition-opacity duration-200 pointer-events-auto"
|
||||
onclick={handleWebcamCancel}
|
||||
></div>
|
||||
|
||||
<!-- Modal Box -->
|
||||
<div
|
||||
class="relative bg-base-100 rounded-2xl shadow-2xl max-w-2xl w-full max-h-[90vh] overflow-hidden flex flex-col z-10 transform transition-all duration-300"
|
||||
style="animation: slideUp 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);"
|
||||
class="absolute bg-base-100 rounded-2xl shadow-2xl max-w-2xl w-full max-h-[90vh] overflow-hidden flex flex-col z-10 transform transition-all duration-300 pointer-events-auto"
|
||||
style="animation: slideUp 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); {getModalStyle()}"
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<!-- Header fixo -->
|
||||
@@ -1383,19 +1471,19 @@
|
||||
<!-- Modal de Aguardando Processamento -->
|
||||
{#if aguardandoProcessamento}
|
||||
<div
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4"
|
||||
class="fixed inset-0 z-50 pointer-events-none"
|
||||
style="animation: fadeIn 0.2s ease-out;"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="modal-aguardando-title"
|
||||
>
|
||||
<!-- Backdrop -->
|
||||
<div class="absolute inset-0 bg-black/50 backdrop-blur-sm transition-opacity duration-200"></div>
|
||||
<!-- Backdrop leve -->
|
||||
<div class="absolute inset-0 bg-black/20 transition-opacity duration-200 pointer-events-auto"></div>
|
||||
|
||||
<!-- Modal Box -->
|
||||
<div
|
||||
class="relative bg-base-100 rounded-2xl shadow-2xl max-w-md w-full z-10 transform transition-all duration-300 p-8"
|
||||
style="animation: slideUp 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);"
|
||||
class="absolute bg-base-100 rounded-2xl shadow-2xl max-w-md w-full z-10 transform transition-all duration-300 p-8 pointer-events-auto"
|
||||
style="animation: slideUp 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); {getModalStyle()}"
|
||||
>
|
||||
<div class="flex flex-col items-center gap-4 text-center">
|
||||
<span class="loading loading-spinner loading-lg text-primary"></span>
|
||||
@@ -1409,22 +1497,22 @@
|
||||
<!-- Modal de Confirmação -->
|
||||
{#if mostrandoModalConfirmacao && imagemCapturada && dataHoraAtual}
|
||||
<div
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4"
|
||||
class="fixed inset-0 z-50 pointer-events-none"
|
||||
style="animation: fadeIn 0.2s ease-out;"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="modal-confirmacao-title"
|
||||
>
|
||||
<!-- Backdrop -->
|
||||
<!-- Backdrop leve -->
|
||||
<div
|
||||
class="absolute inset-0 bg-black/50 backdrop-blur-sm transition-opacity duration-200"
|
||||
class="absolute inset-0 bg-black/20 transition-opacity duration-200 pointer-events-auto"
|
||||
onclick={cancelarRegistro}
|
||||
></div>
|
||||
|
||||
<!-- Modal Box -->
|
||||
<div
|
||||
class="relative bg-base-100 rounded-2xl shadow-2xl max-w-3xl w-full max-h-[90vh] overflow-hidden flex flex-col z-10 transform transition-all duration-300"
|
||||
style="animation: slideUp 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);"
|
||||
class="absolute bg-base-100 rounded-2xl shadow-2xl max-w-3xl w-full max-h-[90vh] overflow-hidden flex flex-col z-10 transform transition-all duration-300 pointer-events-auto"
|
||||
style="animation: slideUp 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); {getModalStyle()}"
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<!-- Header fixo -->
|
||||
|
||||
Reference in New Issue
Block a user