Files
carmanagement/apps/homepage/src/components/app-shell/MobileNavigation.tsx
T
root d7fb7b7a7b
Build & Deploy / Build & Push Docker Image (push) Failing after 47s
Build & Deploy / Deploy to VPS (push) Has been skipped
Test / API Unit Tests (push) Failing after 5m4s
Test / Marketplace Unit Tests (push) Failing after 4m55s
Test / Admin Unit Tests (push) Successful in 9m37s
Test / Dashboard Unit Tests (push) Successful in 9m37s
Test / API Integration Tests (push) Successful in 9m54s
redesign the homepage
2026-06-26 16:27:21 -04:00

147 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { ActionLink } from '@/components/actions/ActionLink';
import { DemoTrigger } from '@/components/integrations/DemoTrigger';
import type { ShellMessages } from '@/lib/localization/messages';
import { localizedPath, type Locale } from '@/lib/localization/config';
import type { ThemePreference } from '@/lib/theme/config';
import { useEffect, useRef, useState } from 'react';
import { HeaderNavigation } from './HeaderNavigation';
import { LocaleSelector } from './LocaleSelector';
import styles from './SiteHeader.module.css';
import { ThemeSelector } from './ThemeSelector';
function signInUrl(locale: Locale): string {
return localizedPath('sign-in', locale);
}
interface MobileNavigationProps {
locale: Locale;
messages: ShellMessages;
themePreference: ThemePreference;
demoEnabled: boolean;
}
export function MobileNavigation({
locale,
messages,
themePreference,
demoEnabled,
}: MobileNavigationProps) {
const [open, setOpen] = useState(false);
const dialogRef = useRef<HTMLDialogElement>(null);
const triggerRef = useRef<HTMLButtonElement>(null);
const closeRef = useRef<HTMLButtonElement>(null);
useEffect(() => {
const dialog = dialogRef.current;
if (!dialog) return;
if (open && !dialog.open) {
dialog.showModal();
document.body.dataset.navigationOpen = 'true';
closeRef.current?.focus();
} else if (!open && dialog.open) {
dialog.close();
}
return () => {
delete document.body.dataset.navigationOpen;
};
}, [open]);
const close = () => {
dialogRef.current?.close();
setOpen(false);
delete document.body.dataset.navigationOpen;
window.requestAnimationFrame(() => triggerRef.current?.focus());
};
return (
<div className={styles.mobileNavigation}>
<button
ref={triggerRef}
type="button"
className={styles.menuButton}
aria-label={messages.header.menuOpen}
aria-haspopup="dialog"
aria-expanded={open}
onClick={() => setOpen(true)}
>
<span aria-hidden="true" className={styles.menuIcon}>
<span />
<span />
<span />
</span>
</button>
<dialog
ref={dialogRef}
className={styles.drawer}
aria-label={messages.header.navigationLabel}
onClose={() => {
setOpen(false);
delete document.body.dataset.navigationOpen;
}}
onCancel={(event) => {
event.preventDefault();
close();
}}
>
<div className={styles.drawerPanel}>
<div className={styles.drawerHeader}>
<strong>RentalDriveGo</strong>
<button
ref={closeRef}
type="button"
className={styles.closeButton}
aria-label={messages.header.menuClose}
onClick={close}
>
<span aria-hidden="true">×</span>
</button>
</div>
<HeaderNavigation
locale={locale}
label={messages.header.navigationLabel}
labels={messages.header.nav}
mobile
onNavigate={close}
/>
<div
className={styles.drawerControls}
role="group"
aria-label={messages.header.controlsLabel}
>
<LocaleSelector
currentLocale={locale}
label={messages.controls.language}
localeNames={messages.controls.localeNames}
/>
<ThemeSelector
initialPreference={themePreference}
label={messages.controls.theme}
statusTemplate={messages.controls.themeStatus}
optionLabels={messages.controls.themes}
/>
</div>
<div className={styles.drawerActions}>
<ActionLink href={signInUrl(locale)} variant="button-primary">
{messages.header.login}
</ActionLink>
<DemoTrigger
label={messages.header.demo}
source="mobile-header"
fullWidth
disabledReason={demoEnabled ? undefined : messages.header.demoUnavailable}
/>
</div>
</div>
</dialog>
</div>
);
}