refactor: update modal positioning logic across components to ensure consistent placement relative to the card, enhancing user experience
This commit is contained in:
@@ -20,18 +20,21 @@
|
|||||||
|
|
||||||
if (cardRef) {
|
if (cardRef) {
|
||||||
const rect = cardRef.getBoundingClientRect();
|
const rect = cardRef.getBoundingClientRect();
|
||||||
const viewportWidth = window.innerWidth;
|
|
||||||
const viewportHeight = window.innerHeight;
|
const viewportHeight = window.innerHeight;
|
||||||
|
|
||||||
// Posicionar o modal na mesma posição do card de registro
|
// Posicionar o modal na mesma altura Y do card (top do card) - mesma posição do texto "Registrar Ponto"
|
||||||
// Centralizado horizontalmente no card
|
const top = rect.top;
|
||||||
const left = rect.left + (rect.width / 2);
|
|
||||||
// Posicionar abaixo do card com um pequeno espaçamento
|
|
||||||
const top = rect.bottom + 20;
|
|
||||||
|
|
||||||
|
// Garantir que o modal não saia da viewport
|
||||||
|
// Considerar uma altura mínima do modal (aproximadamente 300px)
|
||||||
|
const minTop = 20;
|
||||||
|
const maxTop = viewportHeight - 350; // Deixar espaço para o modal
|
||||||
|
const finalTop = Math.max(minTop, Math.min(top, maxTop));
|
||||||
|
|
||||||
|
// Centralizar horizontalmente
|
||||||
return {
|
return {
|
||||||
top: top,
|
top: finalTop,
|
||||||
left: left
|
left: window.innerWidth / 2
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,37 +78,12 @@
|
|||||||
// Função para obter estilo do modal baseado na posição calculada
|
// Função para obter estilo do modal baseado na posição calculada
|
||||||
function getModalStyle() {
|
function getModalStyle() {
|
||||||
if (modalPosition) {
|
if (modalPosition) {
|
||||||
// Garantir que o modal não saia da viewport
|
// Posicionar na altura do card, centralizado horizontalmente
|
||||||
const viewportWidth = window.innerWidth;
|
// position: fixed já é relativo à viewport, então podemos usar diretamente
|
||||||
const viewportHeight = window.innerHeight;
|
return `position: fixed; top: ${modalPosition.top}px; left: 50%; transform: translateX(-50%); width: 100%; max-width: 700px;`;
|
||||||
const modalWidth = 700; // Aproximadamente max-w-2xl
|
|
||||||
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 - 20) {
|
|
||||||
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 - 20) {
|
|
||||||
top = viewportHeight - modalHeight - 20;
|
|
||||||
}
|
|
||||||
// Ajustar se o modal sair da viewport acima
|
|
||||||
if (top < 20) {
|
|
||||||
top = 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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%); width: 100%; max-width: 700px;';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verificar se details contém instruções ou apenas detalhes técnicos
|
// Verificar se details contém instruções ou apenas detalhes técnicos
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
|
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
|
||||||
import { formatarDataHoraCompleta, getTipoRegistroLabel } from '$lib/utils/ponto';
|
import { formatarDataHoraCompleta, getTipoRegistroLabel } from '$lib/utils/ponto';
|
||||||
import logoGovPE from '$lib/assets/logo_governo_PE.png';
|
import logoGovPE from '$lib/assets/logo_governo_PE.png';
|
||||||
import { onMount } from 'svelte';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
registroId: Id<'registrosPonto'>;
|
registroId: Id<'registrosPonto'>;
|
||||||
@@ -28,18 +27,21 @@
|
|||||||
|
|
||||||
if (cardRef) {
|
if (cardRef) {
|
||||||
const rect = cardRef.getBoundingClientRect();
|
const rect = cardRef.getBoundingClientRect();
|
||||||
const viewportWidth = window.innerWidth;
|
|
||||||
const viewportHeight = window.innerHeight;
|
const viewportHeight = window.innerHeight;
|
||||||
|
|
||||||
// Posicionar o modal na mesma posição do card de registro
|
// Posicionar o modal na mesma altura Y do card (top do card) - mesma posição do texto "Registrar Ponto"
|
||||||
// Centralizado horizontalmente no card
|
const top = rect.top;
|
||||||
const left = rect.left + (rect.width / 2);
|
|
||||||
// Posicionar abaixo do card com um pequeno espaçamento
|
|
||||||
const top = rect.bottom + 20;
|
|
||||||
|
|
||||||
|
// Garantir que o modal não saia da viewport
|
||||||
|
// Considerar uma altura mínima do modal (aproximadamente 300px)
|
||||||
|
const minTop = 20;
|
||||||
|
const maxTop = viewportHeight - 350; // Deixar espaço para o modal
|
||||||
|
const finalTop = Math.max(minTop, Math.min(top, maxTop));
|
||||||
|
|
||||||
|
// Centralizar horizontalmente
|
||||||
return {
|
return {
|
||||||
top: top,
|
top: finalTop,
|
||||||
left: left
|
left: window.innerWidth / 2
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,18 +49,26 @@
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
// Atualizar posição quando o modal for aberto (quando registroQuery tiver dados)
|
||||||
|
$effect(() => {
|
||||||
|
if (registroQuery?.data) {
|
||||||
// Usar requestAnimationFrame para garantir que o DOM está completamente renderizado
|
// Usar requestAnimationFrame para garantir que o DOM está completamente renderizado
|
||||||
const updatePosition = () => {
|
const updatePosition = () => {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
const pos = calcularPosicaoModal();
|
const pos = calcularPosicaoModal();
|
||||||
if (pos) {
|
if (pos) {
|
||||||
modalPosition = pos;
|
modalPosition = pos;
|
||||||
|
} else {
|
||||||
|
// Fallback para centralização
|
||||||
|
modalPosition = {
|
||||||
|
top: window.innerHeight / 2,
|
||||||
|
left: window.innerWidth / 2
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Aguardar um pouco mais para garantir que o DOM está atualizado
|
// Aguardar um pouco para garantir que o DOM está atualizado
|
||||||
setTimeout(updatePosition, 50);
|
setTimeout(updatePosition, 50);
|
||||||
|
|
||||||
// Adicionar listener de scroll para atualizar posição
|
// Adicionar listener de scroll para atualizar posição
|
||||||
@@ -73,42 +83,21 @@
|
|||||||
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;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Função para obter estilo do modal baseado na posição calculada
|
// Função para obter estilo do modal baseado na posição calculada
|
||||||
function getModalStyle() {
|
function getModalStyle() {
|
||||||
if (modalPosition) {
|
if (modalPosition) {
|
||||||
// Garantir que o modal não saia da viewport
|
// Posicionar na altura do card, centralizado horizontalmente
|
||||||
const viewportWidth = window.innerWidth;
|
// position: fixed já é relativo à viewport, então podemos usar diretamente
|
||||||
const viewportHeight = window.innerHeight;
|
return `position: fixed; top: ${modalPosition.top}px; left: 50%; transform: translateX(-50%); width: 100%; max-width: 700px;`;
|
||||||
const modalWidth = 700; // Aproximadamente max-w-2xl
|
|
||||||
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 - 20) {
|
|
||||||
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 - 20) {
|
|
||||||
top = viewportHeight - modalHeight - 20;
|
|
||||||
}
|
|
||||||
// Ajustar se o modal sair da viewport acima
|
|
||||||
if (top < 20) {
|
|
||||||
top = 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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%); width: 100%; max-width: 700px;';
|
||||||
}
|
}
|
||||||
|
|
||||||
async function gerarPDF() {
|
async function gerarPDF() {
|
||||||
|
|||||||
@@ -877,12 +877,85 @@
|
|||||||
|
|
||||||
const saldoPositivo = $derived(historicoSaldo ? historicoSaldo.saldoMinutos >= 0 : false);
|
const saldoPositivo = $derived(historicoSaldo ? historicoSaldo.saldoMinutos >= 0 : false);
|
||||||
|
|
||||||
// Posicionamento dos modais
|
// Posicionamento dos modais baseado no texto "Registrar Ponto"
|
||||||
// Posicionamento dos modais
|
let modalPosition = $state<{ top: number; left: number } | null>(null);
|
||||||
// Removido modalPosition pois agora será centralizado via CSS fixo
|
|
||||||
|
|
||||||
// Função para obter estilo do modal (centralizado)
|
// Função para calcular a posição do modal baseada no card de registro de ponto
|
||||||
|
function calcularPosicaoModal() {
|
||||||
|
const cardRef = document.getElementById('card-registro-ponto-ref');
|
||||||
|
|
||||||
|
if (cardRef) {
|
||||||
|
const rect = cardRef.getBoundingClientRect();
|
||||||
|
const viewportHeight = window.innerHeight;
|
||||||
|
|
||||||
|
// Posicionar o modal na mesma altura Y do card (top do card)
|
||||||
|
// getBoundingClientRect() já retorna posição relativa à viewport quando usado com position: fixed
|
||||||
|
const top = rect.top;
|
||||||
|
|
||||||
|
// Garantir que o modal não saia da viewport
|
||||||
|
// Considerar uma altura mínima do modal (aproximadamente 300px)
|
||||||
|
const minTop = 20;
|
||||||
|
const maxTop = viewportHeight - 350; // Deixar espaço para o modal
|
||||||
|
const finalTop = Math.max(minTop, Math.min(top, maxTop));
|
||||||
|
|
||||||
|
return {
|
||||||
|
top: finalTop,
|
||||||
|
left: window.innerWidth / 2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se não encontrar, usar posição padrão (centro da tela)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Atualizar posição quando os modais forem abertos
|
||||||
|
$effect(() => {
|
||||||
|
if (mostrandoWebcam || mostrandoTransicao || aguardandoProcessamento || mostrandoModalConfirmacao) {
|
||||||
|
// Usar requestAnimationFrame para garantir que o DOM está completamente renderizado
|
||||||
|
const updatePosition = () => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const pos = calcularPosicaoModal();
|
||||||
|
if (pos) {
|
||||||
|
modalPosition = pos;
|
||||||
|
} else {
|
||||||
|
// Fallback para centralização
|
||||||
|
modalPosition = {
|
||||||
|
top: window.innerHeight / 2,
|
||||||
|
left: window.innerWidth / 2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Aguardar um pouco para garantir que o DOM está atualizado
|
||||||
|
setTimeout(updatePosition, 50);
|
||||||
|
|
||||||
|
// Adicionar listener de scroll para atualizar posição
|
||||||
|
const handleScroll = () => {
|
||||||
|
updatePosition();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('scroll', handleScroll, true);
|
||||||
|
window.addEventListener('resize', handleScroll);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('scroll', handleScroll, true);
|
||||||
|
window.removeEventListener('resize', handleScroll);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Limpar posição quando o modal for fechado
|
||||||
|
modalPosition = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Função para obter estilo do modal
|
||||||
function getModalStyle() {
|
function getModalStyle() {
|
||||||
|
if (modalPosition) {
|
||||||
|
// Posicionar na altura do card, centralizado horizontalmente
|
||||||
|
// position: fixed já é relativo à viewport, então podemos usar diretamente
|
||||||
|
return `position: fixed; top: ${modalPosition.top}px; left: 50%; transform: translateX(-50%); width: 100%; max-width: 800px;`;
|
||||||
|
}
|
||||||
|
// Fallback para centralização padrão
|
||||||
return 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 100%; max-width: 800px;';
|
return 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 100%; max-width: 800px;';
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user