Full-screen map with draggable, collapsible floating HUD windows
- MapLibre map is now a fixed, full-viewport background layer (z-0) - New FloatingWindow: drag by title bar (pointer capture, viewport-clamped, position persisted to localStorage), collapsible to title bar, bring-to-front z-index; semi-transparent slate window with amber/cyan accents - Game screen re-laid out as floating windows over the map: slim top header (scenario, turn + date, player nation chip, Idle/Simulating status, Focus map collapse-all), dossier (top-left), event feed (right), Your move deck (bottom-center), legend (bottom-left), tiny zoom control (bottom-right) - MapLibre default nav controls removed; custom floating zoom-in/out control - Panels gain an inWindow mode (no card chrome, same testids) - server: honor HISTORY_API_PORT as PORT fallback (dev proxy/e2e agreement) - css: keep maplibre canvas base color and Radix portals above HUD windows
This commit is contained in:
@@ -31,6 +31,12 @@ const EVENT_ICONS: Record<EventType, LucideIcon> = {
|
||||
interface Props {
|
||||
events: EventEntry[];
|
||||
nations: Record<string, Nation>;
|
||||
/**
|
||||
* Render without the outer chrome and the built-in header (the surrounding
|
||||
* floating window provides the border, the "Event feed" title, and the count
|
||||
* badge). Same content, same testids.
|
||||
*/
|
||||
inWindow?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,26 +44,34 @@ interface Props {
|
||||
* icon, a color-coded outcome badge, a nation-colored accent edge, and the turn
|
||||
* + date it resolved on. New events slide in and flash briefly at the top.
|
||||
*/
|
||||
export default function EventFeed({ events, nations }: Props) {
|
||||
export default function EventFeed({ events, nations, inWindow = false }: Props) {
|
||||
const latestFirst = [...events].reverse();
|
||||
|
||||
const header = inWindow ? null : (
|
||||
<div className="flex items-center justify-between border-b px-4 py-2.5">
|
||||
<div className="flex items-center gap-2">
|
||||
<Radar className="size-4 text-primary" />
|
||||
<h2 className="text-sm font-semibold tracking-wide">Event feed</h2>
|
||||
</div>
|
||||
<Badge variant="secondary" className="text-xs tabular-nums">
|
||||
{events.length}
|
||||
</Badge>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex h-full min-h-0 flex-col rounded-xl border bg-card"
|
||||
className={
|
||||
inWindow
|
||||
? 'flex h-full min-h-0 flex-col'
|
||||
: 'flex h-full min-h-0 flex-col rounded-xl border bg-card'
|
||||
}
|
||||
data-testid="event-feed-panel"
|
||||
>
|
||||
<div className="flex items-center justify-between border-b px-4 py-2.5">
|
||||
<div className="flex items-center gap-2">
|
||||
<Radar className="size-4 text-primary" />
|
||||
<h2 className="text-sm font-semibold tracking-wide">Event feed</h2>
|
||||
</div>
|
||||
<Badge variant="secondary" className="text-xs tabular-nums">
|
||||
{events.length}
|
||||
</Badge>
|
||||
</div>
|
||||
{header}
|
||||
|
||||
<ScrollArea className="mx-3 mb-3 min-h-0 flex-1">
|
||||
<div className="space-y-2 pr-1" data-testid="event-feed">
|
||||
<ScrollArea className={inWindow ? 'min-h-0 flex-1' : 'mx-3 mb-3 min-h-0 flex-1'}>
|
||||
<div className="space-y-2 p-3 pr-1" data-testid="event-feed">
|
||||
{latestFirst.length === 0 && (
|
||||
<div className="mt-3 rounded-md border border-dashed p-4 text-center text-sm text-muted-foreground">
|
||||
No events yet. Take an action, send a message, or jump forward to move the world.
|
||||
|
||||
Reference in New Issue
Block a user