diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/config.ts b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/config.ts index 8c0d2244..d6fcfb30 100644 --- a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/config.ts +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/config.ts @@ -69,6 +69,7 @@ export const ROUTES = { export const STORAGE_KEYS = { AUTH: 'auth-storage', UI: 'ui-storage', + PRESENTATION_MODE: 'monitor:presentation-mode', } as const export const QUERY_CONFIG = { diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/BottomTicker.tsx b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/BottomTicker.tsx index 7d914a7e..471c96eb 100644 --- a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/BottomTicker.tsx +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/BottomTicker.tsx @@ -2,14 +2,17 @@ // BottomTicker.tsx import { useTicker } from '@/stores/ticker' +import { useUIStore } from '@/stores/ui' import styles from './BottomTicker.module.scss' const MS_PER_SECOND = 1000 const SECONDS_PER_MINUTE = 60 const SECONDS_PER_HOUR = 3600 -export function BottomTicker(): React.ReactElement { +export function BottomTicker(): React.ReactElement | null { const items = useTicker((s) => s.items) + const isPresentation = useUIStore((s) => s.presentationMode) + if (isPresentation) return null return (
{items.length > 0 && ( diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/Dashboard.module.scss b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/Dashboard.module.scss index 1bf97f83..f9b301f6 100644 --- a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/Dashboard.module.scss +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/Dashboard.module.scss @@ -41,3 +41,17 @@ overflow-y: auto; border-left: 1px solid var(--fg-4); } + +.root.presentation { + .grid { + grid-template-columns: 280px 1fr 280px; + } + + .left { + border-right: 0; + } + + .right { + border-left: 0; + } +} diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/Dashboard.tsx b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/Dashboard.tsx index 5a28da01..45621d2b 100644 --- a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/Dashboard.tsx +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/Dashboard.tsx @@ -1,14 +1,23 @@ // ©AngelaMos | 2026 // Dashboard.tsx +import { useUIStore } from '@/stores/ui' import { AlertBanner } from './AlertBanner' import { BottomTicker } from './BottomTicker' import styles from './Dashboard.module.scss' +import { presentationMode } from './presentationMode' import { TopStrip } from './TopStrip' export function Dashboard(): React.ReactElement { + presentationMode.useGlobalShortcut() + const isPresentation = useUIStore((s) => s.presentationMode) + + const rootClass = isPresentation + ? `${styles.root} ${styles.presentation}` + : styles.root + return ( -
+
diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/TopStrip.tsx b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/TopStrip.tsx index eab6f1d5..c31f9862 100644 --- a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/TopStrip.tsx +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/TopStrip.tsx @@ -11,10 +11,11 @@ import styles from './TopStrip.module.scss' const CLOCK_TICK_MS = 1000 -export function TopStrip(): React.ReactElement { +export function TopStrip(): React.ReactElement | null { const [now, setNow] = useState(() => new Date()) const isAuthenticated = useAuthStore((s) => s.isAuthenticated) const openAbout = useUIStore((s) => s.openAbout) + const isPresentation = useUIStore((s) => s.presentationMode) const navigate = useNavigate() useEffect(() => { @@ -22,6 +23,8 @@ export function TopStrip(): React.ReactElement { return () => clearInterval(id) }, []) + if (isPresentation) return null + return (
diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/presentationMode.ts b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/presentationMode.ts new file mode 100644 index 00000000..a2687360 --- /dev/null +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/presentationMode.ts @@ -0,0 +1,52 @@ +// ©AngelaMos | 2026 +// presentationMode.ts + +import { useEffect } from 'react' +import { STORAGE_KEYS } from '@/config' +import { useUIStore } from '@/stores/ui' + +function readInitial(): boolean { + return localStorage.getItem(STORAGE_KEYS.PRESENTATION_MODE) === '1' +} + +function persist(on: boolean): void { + if (on) { + localStorage.setItem(STORAGE_KEYS.PRESENTATION_MODE, '1') + } else { + localStorage.removeItem(STORAGE_KEYS.PRESENTATION_MODE) + } +} + +function isTypingInForm(target: EventTarget | null): boolean { + if (!(target instanceof HTMLElement)) return false + if (target.isContentEditable) return true + return ( + target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement + ) +} + +export const presentationMode = { + readInitial, + useGlobalShortcut(): void { + const setMode = useUIStore((s) => s.setPresentationMode) + + useEffect(() => { + setMode(readInitial()) + + function onKey(e: KeyboardEvent): void { + if (e.key === 'f' || e.key === 'F') { + if (isTypingInForm(e.target)) return + const next = !useUIStore.getState().presentationMode + setMode(next) + persist(next) + } else if (e.key === 'Escape') { + setMode(false) + persist(false) + } + } + + document.addEventListener('keydown', onKey) + return () => document.removeEventListener('keydown', onKey) + }, [setMode]) + }, +}