redesign the homepage
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
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
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
'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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user