Improve animation

This commit is contained in:
2025-09-04 21:25:38 +03:00
Unverified
parent 68ac96c1e4
commit b6542328de
3 changed files with 59 additions and 16 deletions
+8
View File
@@ -420,6 +420,14 @@
min-width: 160px; min-width: 160px;
z-index: 1000; z-index: 1000;
&.entering {
animation: fadeInDown 0.2s ease forwards;
}
&.closing {
animation: fadeOutUp 0.2s ease forwards;
}
.context-menu-item { .context-menu-item {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -6,12 +6,10 @@ import type { UserProfile } from "../../../core/types";
import { UserProfileDialog } from "./UserProfileDialog"; import { UserProfileDialog } from "./UserProfileDialog";
import { MessageContextMenu, type ContextMenuState } from "./MessageContextMenu"; import { MessageContextMenu, type ContextMenuState } from "./MessageContextMenu";
import { fetchUserProfile } from "../../api/profileApi"; import { fetchUserProfile } from "../../api/profileApi";
import { useState } from "react"; import { useState, useEffect } from "react";
import { delay } from "../../../utils/utils"; import { delay } from "../../../utils/utils";
import { request } from "../../../websocket"; import { request } from "../../../websocket";
export function ChatMessages() { export function ChatMessages() {
const { messages } = useChat(); const { messages } = useChat();
const { user } = useAppState(); const { user } = useAppState();
@@ -26,6 +24,44 @@ export function ChatMessages() {
position: { x: 0, y: 0 } position: { x: 0, y: 0 }
}); });
// Effect to handle clicks outside the context menu
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (contextMenu.isOpen) {
// Check if the click is on a context menu element
const target = event.target as Element;
if (!target.closest('.context-menu')) {
handleContextMenuClose();
}
}
};
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape' && contextMenu.isOpen) {
handleContextMenuClose();
}
};
const handleWindowBlur = () => {
// Close context menu when browser window loses focus
if (contextMenu.isOpen) {
handleContextMenuClose();
}
};
// Add event listeners
document.addEventListener('mousedown', handleClickOutside);
document.addEventListener('keydown', handleKeyDown);
window.addEventListener('blur', handleWindowBlur);
// Cleanup
return () => {
document.removeEventListener('mousedown', handleClickOutside);
document.removeEventListener('keydown', handleKeyDown);
window.removeEventListener('blur', handleWindowBlur);
};
}, [contextMenu.isOpen]);
const handleProfileClick = async (username: string) => { const handleProfileClick = async (username: string) => {
if (!user.authToken) return; if (!user.authToken) return;
@@ -120,19 +156,9 @@ export function ChatMessages() {
} }
}; };
// Close context menu when clicking outside
const handleClickOutside = (e: React.MouseEvent) => {
if (contextMenu.isOpen) {
// Only close if clicking on the chat messages container, not on the context menu itself
if (e.target === e.currentTarget) {
handleContextMenuClose();
}
}
};
return ( return (
<> <>
<div className="chat-messages" id="chat-messages" onClick={handleClickOutside}> <div className="chat-messages" id="chat-messages">
{messages.map((message) => ( {messages.map((message) => (
<Message <Message
key={message.id} key={message.id}
@@ -33,6 +33,7 @@ export function MessageContextMenu({
// Internal state for dialogs // Internal state for dialogs
const [editDialogOpen, setEditDialogOpen] = useState(false); const [editDialogOpen, setEditDialogOpen] = useState(false);
const [replyDialogOpen, setReplyDialogOpen] = useState(false); const [replyDialogOpen, setReplyDialogOpen] = useState(false);
const [isClosing, setIsClosing] = useState(false);
const handleAction = (action: string) => { const handleAction = (action: string) => {
console.log("Context menu action triggered:", action); console.log("Context menu action triggered:", action);
@@ -48,12 +49,20 @@ export function MessageContextMenu({
case "delete": case "delete":
if (isAuthor) { if (isAuthor) {
onDelete(message); onDelete(message);
onClose(); handleClose();
} }
break; break;
} }
}; };
const handleClose = () => {
setIsClosing(true);
// Wait for animation to complete before calling onClose
setTimeout(() => {
onClose();
}, 200); // Match the animation duration from _animations.scss
};
const handleEditSave = (messageId: number, newContent: string) => { const handleEditSave = (messageId: number, newContent: string) => {
// Create a temporary message object with the updated content // Create a temporary message object with the updated content
const updatedMessage = { ...message, content: newContent }; const updatedMessage = { ...message, content: newContent };
@@ -71,7 +80,7 @@ export function MessageContextMenu({
return ( return (
<> <>
<div <div
className="context-menu" className={`context-menu ${isClosing ? 'closing' : 'entering'}`}
style={{ style={{
position: "fixed", position: "fixed",
display: "block", display: "block",