refactor: update modal positioning logic in ErrorModal, ComprovantePonto, and RegistroPonto components

- Refactored modal positioning to align with the synchronized clock element, enhancing user experience by ensuring modals are positioned below the clock with appropriate spacing.
- Replaced timeout-based DOM updates with requestAnimationFrame for improved rendering performance and responsiveness.
- Added logic to clear modal position when modals are closed, ensuring a clean state for future interactions.
This commit is contained in:
2025-11-23 16:28:48 -03:00
parent a3eab60fcd
commit 1089a4fdab
3 changed files with 91 additions and 61 deletions

View File

@@ -13,26 +13,25 @@
let modalPosition = $state<{ top: number; left: number } | null>(null); let modalPosition = $state<{ top: number; left: number } | null>(null);
// Função para calcular a posição baseada no container central da página // Função para calcular a posição baseada no relógio sincronizado
function calcularPosicaoModal() { function calcularPosicaoModal() {
// Procurar pelo elemento container-central // Procurar pelo elemento do relógio sincronizado
const containerCentral = document.getElementById('container-central'); const relogioRef = document.getElementById('relogio-sincronizado-ref');
if (containerCentral) { if (relogioRef) {
const rect = containerCentral.getBoundingClientRect(); const rect = relogioRef.getBoundingClientRect();
const viewportWidth = window.innerWidth; const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight; const viewportHeight = window.innerHeight;
// Centralizar baseado na largura do container central // Posicionar o modal na mesma posição do relógio sincronizado
const containerCenterX = rect.left + (rect.width / 2); // Centralizado horizontalmente no card do relógio
const containerTop = rect.top; const left = rect.left + (rect.width / 2);
// Posicionar abaixo do card do relógio com um pequeno espaçamento
// Posicionar o modal centralizado verticalmente na viewport, mas respeitando o container const top = rect.bottom + 20;
const top = Math.max(50, Math.min(containerTop + 100, viewportHeight / 2));
return { return {
top: top, top: top,
left: containerCenterX left: left
}; };
} }
@@ -42,14 +41,22 @@
$effect(() => { $effect(() => {
if (open) { if (open) {
// Aguardar um pouco para garantir que o DOM está atualizado // Usar requestAnimationFrame para garantir que o DOM está completamente renderizado
setTimeout(() => { const updatePosition = () => {
modalPosition = calcularPosicaoModal(); requestAnimationFrame(() => {
}, 10); const pos = calcularPosicaoModal();
if (pos) {
modalPosition = pos;
}
});
};
// Aguardar um pouco mais para garantir que o DOM está atualizado
setTimeout(updatePosition, 50);
// Adicionar listener de scroll para atualizar posição // Adicionar listener de scroll para atualizar posição
const handleScroll = () => { const handleScroll = () => {
modalPosition = calcularPosicaoModal(); updatePosition();
}; };
window.addEventListener('scroll', handleScroll, true); window.addEventListener('scroll', handleScroll, true);
@@ -59,6 +66,9 @@
window.removeEventListener('scroll', handleScroll, true); window.removeEventListener('scroll', handleScroll, true);
window.removeEventListener('resize', handleScroll); window.removeEventListener('resize', handleScroll);
}; };
} else {
// Limpar posição quando o modal for fechado
modalPosition = null;
} }
}); });
@@ -75,7 +85,7 @@
let top = modalPosition.top; let top = modalPosition.top;
// Ajustar se o modal sair da viewport à direita // Ajustar se o modal sair da viewport à direita
if (left + (modalWidth / 2) > viewportWidth) { if (left + (modalWidth / 2) > viewportWidth - 20) {
left = viewportWidth - (modalWidth / 2) - 20; left = viewportWidth - (modalWidth / 2) - 20;
} }
// Ajustar se o modal sair da viewport à esquerda // Ajustar se o modal sair da viewport à esquerda
@@ -83,7 +93,7 @@
left = (modalWidth / 2) + 20; left = (modalWidth / 2) + 20;
} }
// Ajustar se o modal sair da viewport abaixo // Ajustar se o modal sair da viewport abaixo
if (top + modalHeight > viewportHeight) { if (top + modalHeight > viewportHeight - 20) {
top = viewportHeight - modalHeight - 20; top = viewportHeight - modalHeight - 20;
} }
// Ajustar se o modal sair da viewport acima // Ajustar se o modal sair da viewport acima
@@ -91,7 +101,8 @@
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;`; // Usar transform para centralizar horizontalmente baseado no left calculado
return `position: fixed; top: ${top}px; left: ${left}px; transform: translateX(-50%); max-width: ${Math.min(modalWidth, viewportWidth - 40)}px;`;
} }
// Se não houver posição calculada, centralizar na tela // Se não houver posição calculada, centralizar na tela
return 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);'; return 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);';

View File

@@ -21,26 +21,25 @@
let gerando = $state(false); let gerando = $state(false);
let modalPosition = $state<{ top: number; left: number } | null>(null); let modalPosition = $state<{ top: number; left: number } | null>(null);
// Função para calcular a posição baseada no container central da página // Função para calcular a posição baseada no relógio sincronizado
function calcularPosicaoModal() { function calcularPosicaoModal() {
// Procurar pelo elemento container-central // Procurar pelo elemento do relógio sincronizado
const containerCentral = document.getElementById('container-central'); const relogioRef = document.getElementById('relogio-sincronizado-ref');
if (containerCentral) { if (relogioRef) {
const rect = containerCentral.getBoundingClientRect(); const rect = relogioRef.getBoundingClientRect();
const viewportWidth = window.innerWidth; const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight; const viewportHeight = window.innerHeight;
// Centralizar baseado na largura do container central // Posicionar o modal na mesma posição do relógio sincronizado
const containerCenterX = rect.left + (rect.width / 2); // Centralizado horizontalmente no card do relógio
const containerTop = rect.top; const left = rect.left + (rect.width / 2);
// Posicionar abaixo do card do relógio com um pequeno espaçamento
// Posicionar o modal centralizado verticalmente na viewport, mas respeitando o container const top = rect.bottom + 20;
const top = Math.max(50, Math.min(containerTop + 100, viewportHeight / 2));
return { return {
top: top, top: top,
left: containerCenterX left: left
}; };
} }
@@ -49,14 +48,22 @@
} }
onMount(() => { onMount(() => {
// Aguardar um pouco para garantir que o DOM está atualizado // Usar requestAnimationFrame para garantir que o DOM está completamente renderizado
setTimeout(() => { const updatePosition = () => {
modalPosition = calcularPosicaoModal(); requestAnimationFrame(() => {
}, 10); const pos = calcularPosicaoModal();
if (pos) {
modalPosition = pos;
}
});
};
// Aguardar um pouco mais para garantir que o DOM está atualizado
setTimeout(updatePosition, 50);
// Adicionar listener de scroll para atualizar posição // Adicionar listener de scroll para atualizar posição
const handleScroll = () => { const handleScroll = () => {
modalPosition = calcularPosicaoModal(); updatePosition();
}; };
window.addEventListener('scroll', handleScroll, true); window.addEventListener('scroll', handleScroll, true);
@@ -81,7 +88,7 @@
let top = modalPosition.top; let top = modalPosition.top;
// Ajustar se o modal sair da viewport à direita // Ajustar se o modal sair da viewport à direita
if (left + (modalWidth / 2) > viewportWidth) { if (left + (modalWidth / 2) > viewportWidth - 20) {
left = viewportWidth - (modalWidth / 2) - 20; left = viewportWidth - (modalWidth / 2) - 20;
} }
// Ajustar se o modal sair da viewport à esquerda // Ajustar se o modal sair da viewport à esquerda
@@ -89,7 +96,7 @@
left = (modalWidth / 2) + 20; left = (modalWidth / 2) + 20;
} }
// Ajustar se o modal sair da viewport abaixo // Ajustar se o modal sair da viewport abaixo
if (top + modalHeight > viewportHeight) { if (top + modalHeight > viewportHeight - 20) {
top = viewportHeight - modalHeight - 20; top = viewportHeight - modalHeight - 20;
} }
// Ajustar se o modal sair da viewport acima // Ajustar se o modal sair da viewport acima
@@ -97,7 +104,8 @@
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;`; // Usar transform para centralizar horizontalmente baseado no left calculado
return `position: fixed; top: ${top}px; left: ${left}px; transform: translateX(-50%); max-width: ${Math.min(modalWidth, viewportWidth - 40)}px;`;
} }
// Se não houver posição calculada, centralizar na tela // Se não houver posição calculada, centralizar na tela
return 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);'; return 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);';

View File

@@ -855,26 +855,25 @@
// Posicionamento dos modais // Posicionamento dos modais
let modalPosition = $state<{ top: number; left: number } | null>(null); let modalPosition = $state<{ top: number; left: number } | null>(null);
// Função para calcular a posição baseada no container central da página // Função para calcular a posição baseada no relógio sincronizado
function calcularPosicaoModal() { function calcularPosicaoModal() {
// Procurar pelo elemento container-central // Procurar pelo elemento do relógio sincronizado
const containerCentral = document.getElementById('container-central'); const relogioRef = document.getElementById('relogio-sincronizado-ref');
if (containerCentral) { if (relogioRef) {
const rect = containerCentral.getBoundingClientRect(); const rect = relogioRef.getBoundingClientRect();
const viewportWidth = window.innerWidth; const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight; const viewportHeight = window.innerHeight;
// Centralizar baseado na largura do container central // Posicionar o modal na mesma posição do relógio sincronizado
const containerCenterX = rect.left + (rect.width / 2); // Centralizado horizontalmente no card do relógio
const containerTop = rect.top; const left = rect.left + (rect.width / 2);
// Posicionar abaixo do card do relógio com um pequeno espaçamento
// Posicionar o modal centralizado verticalmente na viewport, mas respeitando o container const top = rect.bottom + 20;
const top = Math.max(50, Math.min(containerTop + 100, viewportHeight / 2));
return { return {
top: top, top: top,
left: containerCenterX left: left
}; };
} }
@@ -885,14 +884,22 @@
// Atualizar posição quando os modais forem abertos ou quando a página rolar // Atualizar posição quando os modais forem abertos ou quando a página rolar
$effect(() => { $effect(() => {
if (mostrandoWebcam || mostrandoModalConfirmacao || aguardandoProcessamento || mostrarModalErro) { if (mostrandoWebcam || mostrandoModalConfirmacao || aguardandoProcessamento || mostrarModalErro) {
// Aguardar um pouco para garantir que o DOM está atualizado // Usar requestAnimationFrame para garantir que o DOM está completamente renderizado
setTimeout(() => { const updatePosition = () => {
modalPosition = calcularPosicaoModal(); requestAnimationFrame(() => {
}, 10); const pos = calcularPosicaoModal();
if (pos) {
modalPosition = pos;
}
});
};
// Aguardar um pouco mais para garantir que o DOM está atualizado
setTimeout(updatePosition, 50);
// Adicionar listener de scroll para atualizar posição // Adicionar listener de scroll para atualizar posição
const handleScroll = () => { const handleScroll = () => {
modalPosition = calcularPosicaoModal(); updatePosition();
}; };
window.addEventListener('scroll', handleScroll, true); window.addEventListener('scroll', handleScroll, true);
@@ -902,6 +909,9 @@
window.removeEventListener('scroll', handleScroll, true); window.removeEventListener('scroll', handleScroll, true);
window.removeEventListener('resize', handleScroll); window.removeEventListener('resize', handleScroll);
}; };
} else {
// Limpar posição quando os modais forem fechados
modalPosition = null;
} }
}); });
@@ -918,7 +928,7 @@
let top = modalPosition.top; let top = modalPosition.top;
// Ajustar se o modal sair da viewport à direita // Ajustar se o modal sair da viewport à direita
if (left + (modalWidth / 2) > viewportWidth) { if (left + (modalWidth / 2) > viewportWidth - 20) {
left = viewportWidth - (modalWidth / 2) - 20; left = viewportWidth - (modalWidth / 2) - 20;
} }
// Ajustar se o modal sair da viewport à esquerda // Ajustar se o modal sair da viewport à esquerda
@@ -926,7 +936,7 @@
left = (modalWidth / 2) + 20; left = (modalWidth / 2) + 20;
} }
// Ajustar se o modal sair da viewport abaixo // Ajustar se o modal sair da viewport abaixo
if (top + modalHeight > viewportHeight) { if (top + modalHeight > viewportHeight - 20) {
top = viewportHeight - modalHeight - 20; top = viewportHeight - modalHeight - 20;
} }
// Ajustar se o modal sair da viewport acima // Ajustar se o modal sair da viewport acima
@@ -934,7 +944,8 @@
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;`; // Usar transform para centralizar horizontalmente baseado no left calculado
return `position: fixed; top: ${top}px; left: ${left}px; transform: translateX(-50%); max-width: ${Math.min(modalWidth, viewportWidth - 40)}px;`;
} }
// Se não houver posição calculada, centralizar na tela // Se não houver posição calculada, centralizar na tela
return 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);'; return 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);';
@@ -1009,7 +1020,7 @@
<!-- Relógio Sincronizado --> <!-- Relógio Sincronizado -->
<div class="mb-5 flex justify-center"> <div class="mb-5 flex justify-center">
<div class="card bg-gradient-to-br from-primary/10 to-primary/5 border-2 border-primary/20 shadow-lg rounded-2xl p-5 w-full max-w-sm"> <div id="relogio-sincronizado-ref" class="card bg-gradient-to-br from-primary/10 to-primary/5 border-2 border-primary/20 shadow-lg rounded-2xl p-5 w-full max-w-sm">
<RelogioSincronizado /> <RelogioSincronizado />
</div> </div>
</div> </div>