Ajuste chat #9

Merged
deyvisonwanderley merged 7 commits from ajuste_chat into master 2025-11-05 18:44:43 +00:00
39 changed files with 4170 additions and 3087 deletions
Showing only changes of commit 05244b9207 - Show all commits

View File

@@ -44,6 +44,8 @@
let isDragging = $state(false);
let dragStart = $state({ x: 0, y: 0 });
let isAnimating = $state(false);
let dragThreshold = $state(5); // Distância mínima em pixels para considerar arrastar
let hasMoved = $state(false); // Flag para verificar se houve movimento durante o arrastar
// Tamanho da janela (redimensionável)
const MIN_WIDTH = 300;
@@ -76,6 +78,38 @@
let previousSize = $state({ width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT });
let previousPosition = $state({ x: 0, y: 0 });
// Dimensões da janela (reativo)
let windowDimensions = $state({ width: 0, height: 0 });
// Atualizar dimensões da janela
function updateWindowDimensions() {
if (typeof window !== 'undefined') {
windowDimensions = {
width: window.innerWidth,
height: window.innerHeight
};
}
}
// Inicializar e atualizar dimensões da janela
$effect(() => {
if (typeof window === 'undefined') return;
updateWindowDimensions();
const handleResize = () => {
updateWindowDimensions();
// Ajustar posição quando a janela redimensionar
ajustarPosicao();
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
});
// Salvar tamanho no localStorage
function saveSize() {
if (typeof window !== 'undefined') {
@@ -119,15 +153,19 @@
newWidth = Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, resizeStart.width + deltaX));
}
if (resizeDirection.includes('w')) {
newWidth = Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, resizeStart.width - deltaX));
newX = position.x + (resizeStart.width - newWidth);
const calculatedWidth = Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, resizeStart.width - deltaX));
const widthDelta = resizeStart.width - calculatedWidth;
newWidth = calculatedWidth;
newX = position.x + widthDelta;
}
if (resizeDirection.includes('s')) {
newHeight = Math.max(MIN_HEIGHT, Math.min(MAX_HEIGHT, resizeStart.height + deltaY));
}
if (resizeDirection.includes('n')) {
newHeight = Math.max(MIN_HEIGHT, Math.min(MAX_HEIGHT, resizeStart.height - deltaY));
newY = position.y + (resizeStart.height - newHeight);
const calculatedHeight = Math.max(MIN_HEIGHT, Math.min(MAX_HEIGHT, resizeStart.height - deltaY));
const heightDelta = resizeStart.height - calculatedHeight;
newHeight = calculatedHeight;
newY = position.y + heightDelta;
}
windowSize = { width: newWidth, height: newHeight };
@@ -337,9 +375,12 @@
previousPosition = { ...position };
// Maximizar completamente: usar toda a largura e altura da tela
const winWidth = windowDimensions.width || (typeof window !== 'undefined' ? window.innerWidth : DEFAULT_WIDTH);
const winHeight = windowDimensions.height || (typeof window !== 'undefined' ? window.innerHeight : DEFAULT_HEIGHT);
windowSize = {
width: window.innerWidth,
height: window.innerHeight
width: winWidth,
height: winHeight
};
position = {
x: 0,
@@ -355,6 +396,7 @@
// Funcionalidade de arrastar
function handleMouseDown(e: MouseEvent) {
if (e.button !== 0) return; // Apenas botão esquerdo
hasMoved = false;
isDragging = true;
dragStart = {
x: e.clientX - position.x,
@@ -364,6 +406,20 @@
e.preventDefault();
}
// Handler específico para o botão flutuante (evita conflito com clique)
function handleButtonMouseDown(e: MouseEvent) {
if (e.button !== 0) return;
// Resetar flag de movimento
hasMoved = false;
isDragging = true;
dragStart = {
x: e.clientX - position.x,
y: e.clientY - position.y,
};
document.body.classList.add('dragging');
// Não prevenir default para permitir clique funcionar se não houver movimento
}
function handleMouseMove(e: MouseEvent) {
if (isResizing) {
handleResizeMove(e);
@@ -375,15 +431,26 @@
const newX = e.clientX - dragStart.x;
const newY = e.clientY - dragStart.y;
// Verificar se houve movimento significativo
const deltaX = Math.abs(newX - position.x);
const deltaY = Math.abs(newY - position.y);
if (deltaX > dragThreshold || deltaY > dragThreshold) {
hasMoved = true;
}
// Dimensões do widget
const widgetWidth = isOpen && !isMinimized ? windowSize.width : 72;
const widgetHeight = isOpen && !isMinimized ? windowSize.height : 72;
// Usar dimensões reativas da janela
const winWidth = windowDimensions.width || (typeof window !== 'undefined' ? window.innerWidth : 0);
const winHeight = windowDimensions.height || (typeof window !== 'undefined' ? window.innerHeight : 0);
// Limites da tela com margem de segurança
const minX = -(widgetWidth - 100); // Permitir até 100px visíveis
const maxX = window.innerWidth - 100; // Manter 100px dentro da tela
const maxX = Math.max(0, winWidth - 100); // Manter 100px dentro da tela
const minY = -(widgetHeight - 100);
const maxY = window.innerHeight - 100;
const maxY = Math.max(0, winHeight - 100);
position = {
x: Math.max(minX, Math.min(newX, maxX)),
@@ -391,14 +458,26 @@
};
}
function handleMouseUp() {
function handleMouseUp(e?: MouseEvent) {
const hadMoved = hasMoved;
if (isDragging) {
isDragging = false;
hasMoved = false;
document.body.classList.remove('dragging');
// Se estava arrastando e houve movimento, prevenir clique
if (hadMoved && e) {
e.preventDefault();
e.stopPropagation();
}
// Garantir que está dentro dos limites ao soltar
ajustarPosicao();
}
handleResizeEnd();
return !hadMoved; // Retorna true se não houve movimento (permite clique)
}
function ajustarPosicao() {
@@ -408,22 +487,32 @@
const widgetWidth = isOpen && !isMinimized ? windowSize.width : 72;
const widgetHeight = isOpen && !isMinimized ? windowSize.height : 72;
// Usar dimensões reativas da janela
const winWidth = windowDimensions.width || (typeof window !== 'undefined' ? window.innerWidth : 0);
const winHeight = windowDimensions.height || (typeof window !== 'undefined' ? window.innerHeight : 0);
// Verificar se está fora dos limites
let newX = position.x;
let newY = position.y;
// Ajustar X
if (newX < -(widgetWidth - 100)) {
newX = -(widgetWidth - 100);
} else if (newX > window.innerWidth - 100) {
newX = window.innerWidth - 100;
// Ajustar X - garantir que pelo menos 100px fiquem visíveis
const minX = -(widgetWidth - 100);
const maxX = Math.max(0, winWidth - 100);
if (newX < minX) {
newX = minX;
} else if (newX > maxX) {
newX = maxX;
}
// Ajustar Y
if (newY < -(widgetHeight - 100)) {
newY = -(widgetHeight - 100);
} else if (newY > window.innerHeight - 100) {
newY = window.innerHeight - 100;
// Ajustar Y - garantir que pelo menos 100px fiquem visíveis
const minY = -(widgetHeight - 100);
const maxY = Math.max(0, winHeight - 100);
if (newY < minY) {
newY = minY;
} else if (newY > maxY) {
newY = maxY;
}
position = { x: newX, y: newY };
@@ -433,15 +522,26 @@
}, 300);
}
// Event listeners globais
if (typeof window !== 'undefined') {
// Event listeners globais com cleanup adequado
$effect(() => {
if (typeof window === 'undefined') return;
window.addEventListener('mousemove', handleMouseMove);
window.addEventListener('mouseup', handleMouseUp);
}
return () => {
window.removeEventListener('mousemove', handleMouseMove);
window.removeEventListener('mouseup', handleMouseUp);
};
});
</script>
<!-- Botão flutuante MODERNO E ARRASTÁVEL -->
{#if !isOpen || isMinimized}
{@const winWidth = windowDimensions.width || (typeof window !== 'undefined' ? window.innerWidth : 0)}
{@const winHeight = windowDimensions.height || (typeof window !== 'undefined' ? window.innerHeight : 0)}
{@const bottomPos = position.y === 0 ? '1.5rem' : `${Math.max(0, winHeight - position.y - 72)}px`}
{@const rightPos = position.x === 0 ? '1.5rem' : `${Math.max(0, winWidth - position.x - 72)}px`}
<button
type="button"
class="fixed group relative border-0 backdrop-blur-xl"
@@ -449,8 +549,8 @@
z-index: 99999 !important;
width: 4.5rem;
height: 4.5rem;
bottom: {position.y === 0 ? '1.5rem' : `${window.innerHeight - position.y - 72}px`};
right: {position.x === 0 ? '1.5rem' : `${window.innerWidth - position.x - 72}px`};
bottom: {bottomPos};
right: {rightPos};
position: fixed !important;
background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%);
box-shadow:
@@ -462,8 +562,26 @@
transform: {isDragging ? 'scale(1.05)' : 'scale(1)'};
transition: {isAnimating ? 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)' : 'transform 0.2s, box-shadow 0.3s'};
"
onclick={handleToggle}
onmousedown={handleMouseDown}
onmousedown={handleButtonMouseDown}
onmouseup={(e) => {
const hadMovedBefore = hasMoved;
handleMouseUp(e);
// Se houve movimento, prevenir o clique
if (hadMovedBefore) {
e.preventDefault();
e.stopPropagation();
}
}}
onclick={(e) => {
// Só executar toggle se não houve movimento
if (!hasMoved) {
handleToggle();
} else {
// Prevenir clique se houve movimento
e.preventDefault();
e.stopPropagation();
}
}}
aria-label="Abrir chat"
>
<!-- Anel de brilho rotativo -->
@@ -521,12 +639,16 @@
<!-- Janela do Chat ULTRA MODERNA E ARRASTÁVEL -->
{#if isOpen && !isMinimized}
{@const winWidth = windowDimensions.width || (typeof window !== 'undefined' ? window.innerWidth : 0)}
{@const winHeight = windowDimensions.height || (typeof window !== 'undefined' ? window.innerHeight : 0)}
{@const bottomPos = position.y === 0 ? '1.5rem' : `${Math.max(0, winHeight - position.y - windowSize.height)}px`}
{@const rightPos = position.x === 0 ? '1.5rem' : `${Math.max(0, winWidth - position.x - windowSize.width)}px`}
<div
class="fixed flex flex-col overflow-hidden backdrop-blur-2xl"
style="
z-index: 99999 !important;
bottom: {position.y === 0 ? '1.5rem' : `${window.innerHeight - position.y - windowSize.height}px`};
right: {position.x === 0 ? '1.5rem' : `${window.innerWidth - position.x - windowSize.width}px`};
bottom: {bottomPos};
right: {rightPos};
width: {windowSize.width}px;
height: {windowSize.height}px;
max-width: calc(100vw - 3rem);