feat: avoid removing notification when mouse hover

This commit is contained in:
2026-07-22 18:23:09 +02:00
parent 9e3f3803a6
commit 5cee5a2047
3 changed files with 33 additions and 7 deletions

View File

@ -3,15 +3,22 @@ import type { Notification } from '../types/types';
type Props = { type Props = {
notifications: Notification[]; notifications: Notification[];
onRemove: (id: number) => void; onRemove: (id: number) => void;
onHover: (id: number, hovering: boolean) => void;
}; };
export default function Notifications({ notifications, onRemove }: Props) { export default function Notifications({
notifications,
onRemove,
onHover,
}: Props) {
return ( return (
<div className="notifications"> <div className="notifications">
{notifications.map((n) => ( {notifications.map((n) => (
<div <div
key={n.id} key={n.id}
className={`notification ${n.type} ${n.removing ? 'notification-out' : ''}`} className={`notification ${n.type} ${n.removing ? 'notification-out' : ''}`}
onMouseEnter={() => onHover(n.id, true)}
onMouseLeave={() => onHover(n.id, false)}
> >
<span className="notification-message" title={n.message}> <span className="notification-message" title={n.message}>
{n.message} {n.message}

View File

@ -16,7 +16,7 @@ function App() {
const [dsps, setDsps] = useState<DSP[]>([]); const [dsps, setDsps] = useState<DSP[]>([]);
const [selected, setSelected] = useState<number | null>(null); const [selected, setSelected] = useState<number | null>(null);
const [page, setPage] = useState<AppPage>('home'); const [page, setPage] = useState<AppPage>('home');
const { notifications, notify, removeNotification } = useNotifications(); const { notifications, notify, removeNotification, hoverNotification } = useNotifications();
async function addDSP(dsp: Omit<DSP, 'id' | 'status'>) { async function addDSP(dsp: Omit<DSP, 'id' | 'status'>) {
try { try {
@ -126,7 +126,7 @@ function App() {
return ( return (
<div className="app"> <div className="app">
<Notifications notifications={notifications} onRemove={removeNotification} /> <Notifications notifications={notifications} onRemove={removeNotification} onHover={hoverNotification}/>
<TopBar <TopBar
dsps={dsps} dsps={dsps}

View File

@ -1,8 +1,9 @@
import { useState, useCallback } from 'react'; import { useState, useCallback, useRef } from 'react';
import { NotificationType, Notification } from '../types/types'; import { NotificationType, Notification } from '../types/types';
export function useNotifications() { export function useNotifications() {
const [notifications, setNotifications] = useState<Notification[]>([]); const [notifications, setNotifications] = useState<Notification[]>([]);
const hovered = useRef<Set<number>>(new Set());
const notify = useCallback( const notify = useCallback(
(message: string, type: NotificationType = 'error', dspName?: string) => { (message: string, type: NotificationType = 'error', dspName?: string) => {
@ -19,24 +20,42 @@ export function useNotifications() {
}, },
]); ]);
setTimeout(() => { const removeLater = () => {
if (hovered.current.has(id)) {
setTimeout(removeLater, 1000);
return;
}
setNotifications((prev) => prev.filter((n) => n.id !== id)); setNotifications((prev) => prev.filter((n) => n.id !== id));
}, 5000); };
setTimeout(removeLater, 5000);
}, },
[] []
); );
const removeNotification = useCallback((id: number) => { const removeNotification = useCallback((id: number) => {
setNotifications((prev) => prev.map((n) => (n.id === id ? { ...n, removing: true } : n))); setNotifications((prev) =>
prev.map((n) => (n.id === id ? { ...n, removing: true } : n))
);
setTimeout(() => { setTimeout(() => {
setNotifications((prev) => prev.filter((n) => n.id !== id)); setNotifications((prev) => prev.filter((n) => n.id !== id));
}, 250); }, 250);
}, []); }, []);
const hoverNotification = useCallback((id: number, hovering: boolean) => {
if (hovering) {
hovered.current.add(id);
} else {
hovered.current.delete(id);
}
}, []);
return { return {
notifications, notifications,
notify, notify,
removeNotification, removeNotification,
hoverNotification,
}; };
} }