Phase 4 of turning the game into the company landing page. - Only the menu button, a compact objective chip and the touch controls stay on screen; speed shows only while driving. - The objective chip shows the next goal in one line, its distance and an arrow that turns towards it (replaces the compass strip); tapping it opens the mission list. - One floating menu holds map, missions, status, products, real-world, camera, stable mode, sound, graphics, minimap, fullscreen, help, restart and back to the start screen. It closes on a second tap, a tap outside, Escape, or after an action. - Missions, status and help open as sheets; street label, rank bar, status panel, toolbar and compass are gone. The minimap is optional and off by default. - Share the icon set and sheet dialog between start screen and HUD. - Layout checked for phone portrait, landscape and safe areas. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
48 lines
1.5 KiB
React
48 lines
1.5 KiB
React
import { useEffect, useRef } from 'react';
|
|
import { Icon } from './Icon.jsx';
|
|
|
|
/** @typedef {{id: string, open: boolean, title: string, closeLabel: string, onClose: function,
|
|
* children?: import('react').ReactNode}} SheetProps */
|
|
/**
|
|
* Modal panel built on the native dialog (focus trap and Escape for free). Styled by the
|
|
* `.info-sheet` rules: a centred card on wide screens, a bottom sheet on phones.
|
|
* @param {SheetProps} props - Visibility, title, close label and callback, and content.
|
|
* @returns {import('react').ReactElement} Dialog element.
|
|
*/
|
|
export function Sheet({ id, open, title, closeLabel, onClose, children }) {
|
|
const dialog = useRef(null);
|
|
|
|
// Keep the native dialog in step with `open`; closing by Escape reports back through onClose.
|
|
useEffect(() => {
|
|
const element = dialog.current;
|
|
if (!element) return;
|
|
if (open && !element.open) element.showModal();
|
|
if (!open && element.open) element.close();
|
|
}, [open]);
|
|
|
|
return (
|
|
<dialog
|
|
ref={dialog}
|
|
id={id}
|
|
aria-labelledby={`${id}-title`}
|
|
onClose={onClose}
|
|
className="info-sheet"
|
|
>
|
|
<header className="mb-4 flex items-center justify-between gap-4">
|
|
<h2 id={`${id}-title`} className="text-lg font-bold">
|
|
{title}
|
|
</h2>
|
|
<button
|
|
type="button"
|
|
className="start-icon-button"
|
|
aria-label={closeLabel}
|
|
onClick={onClose}
|
|
>
|
|
<Icon name="close" className="h-5 w-5" />
|
|
</button>
|
|
</header>
|
|
{open && children}
|
|
</dialog>
|
|
);
|
|
}
|