diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx index 4ed82a59..1a80efa6 100644 --- a/frontend/app/layout.tsx +++ b/frontend/app/layout.tsx @@ -1,5 +1,6 @@ import type { Metadata } from "next"; import { Analytics } from "@vercel/analytics/react"; +import { SpeedInsights } from "@vercel/speed-insights/next"; import "./globals.css"; export const metadata: Metadata = { @@ -28,6 +29,7 @@ export default function RootLayout({ {children} + ); diff --git a/frontend/components/dashboard/DetailPanel.tsx b/frontend/components/dashboard/DetailPanel.tsx index fe9b42ab..a18067eb 100644 --- a/frontend/components/dashboard/DetailPanel.tsx +++ b/frontend/components/dashboard/DetailPanel.tsx @@ -2,6 +2,7 @@ import { ChartConfiguration } from "chart.js/auto"; import clsx from "clsx"; +import { useEffect, useRef } from "react"; import { ForecastTable } from "@/components/dashboard/PanelSections"; import { useChart } from "@/hooks/useChart"; import { useDashboardStore } from "@/hooks/useDashboardStore"; @@ -127,6 +128,7 @@ export function DetailPanel() { const store = useDashboardStore(); const { locale, t } = useI18n(); const detail = store.selectedDetail; + const panelRef = useRef(null); const isOverlayOpen = Boolean(store.futureModalDate) || store.historyState.isOpen || @@ -139,18 +141,49 @@ export function DetailPanel() { !isOverlayOpen; const profileStats = detail ? getCityProfileStats(detail, locale) : []; const scenery = getCityScenery(detail?.name); + const blurActiveElement = () => { + if (typeof document === "undefined") return; + const active = document.activeElement; + if (active instanceof HTMLElement) { + active.blur(); + } + }; + + useEffect(() => { + const panel = panelRef.current; + if (!panel) return; + + if (!isVisible) { + panel.setAttribute("inert", ""); + if ( + typeof document !== "undefined" && + panel.contains(document.activeElement) + ) { + const active = document.activeElement; + if (active instanceof HTMLElement) { + active.blur(); + } + } + return; + } + + panel.removeAttribute("inert"); + }, [isVisible]); return (