Initial Watermaps import
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
import { Anchor, Bell, CloudSun, Route as RouteIcon, type LucideIcon } from "lucide-react";
|
||||
|
||||
export const NAVIGATION_TOOL_ORDER = [
|
||||
"anchor",
|
||||
"conditions",
|
||||
"upcoming",
|
||||
"route"
|
||||
] as const;
|
||||
|
||||
export type NavigationToolId = (typeof NAVIGATION_TOOL_ORDER)[number];
|
||||
export type ActiveTool = NavigationToolId | null;
|
||||
export type NavigationToolStatus = "idle" | "active" | "caution" | "alarm" | "stale";
|
||||
|
||||
type NavigationToolDefinition = {
|
||||
id: NavigationToolId;
|
||||
label: string;
|
||||
Icon: LucideIcon;
|
||||
};
|
||||
|
||||
const NAVIGATION_TOOLS: readonly NavigationToolDefinition[] = [
|
||||
{ id: "anchor", label: "Ankerwache", Icon: Anchor },
|
||||
{ id: "conditions", label: "Wetter und Tide", Icon: CloudSun },
|
||||
{ id: "upcoming", label: "Als Nächstes", Icon: Bell },
|
||||
{ id: "route", label: "Route", Icon: RouteIcon }
|
||||
];
|
||||
|
||||
export type NavigationToolRailProps = {
|
||||
activeTool: ActiveTool;
|
||||
onSelect: (tool: NavigationToolId) => void;
|
||||
statuses?: Partial<Record<NavigationToolId, NavigationToolStatus>>;
|
||||
badges?: Partial<Record<NavigationToolId, number | string | null>>;
|
||||
disabledTools?: Partial<Record<NavigationToolId, boolean>>;
|
||||
workspaceId?: string;
|
||||
className?: string;
|
||||
ariaLabel?: string;
|
||||
};
|
||||
|
||||
export function NavigationToolRail({
|
||||
activeTool,
|
||||
onSelect,
|
||||
statuses = {},
|
||||
badges = {},
|
||||
disabledTools = {},
|
||||
workspaceId = "navigation-workspace",
|
||||
className,
|
||||
ariaLabel = "Kartenwerkzeuge"
|
||||
}: NavigationToolRailProps) {
|
||||
return (
|
||||
<nav className={classNames("navigation-tool-rail", className)} aria-label={ariaLabel}>
|
||||
{NAVIGATION_TOOLS.map(({ id, label, Icon }) => {
|
||||
const active = activeTool === id;
|
||||
const status = statuses[id] ?? "idle";
|
||||
const badge = formatBadge(badges[id]);
|
||||
const accessibleLabel = toolAriaLabel(label, status, badge, active);
|
||||
|
||||
return (
|
||||
<button
|
||||
key={id}
|
||||
className="navigation-tool-rail-button"
|
||||
type="button"
|
||||
disabled={Boolean(disabledTools[id])}
|
||||
data-tool={id}
|
||||
data-status={status}
|
||||
data-active={active}
|
||||
aria-controls={workspaceId}
|
||||
aria-expanded={active}
|
||||
aria-pressed={active}
|
||||
aria-label={accessibleLabel}
|
||||
title={accessibleLabel}
|
||||
onClick={() => onSelect(id)}
|
||||
>
|
||||
<Icon size={21} aria-hidden="true" />
|
||||
<span className="navigation-tool-rail-label" aria-hidden="true">
|
||||
{label}
|
||||
</span>
|
||||
{badge && (
|
||||
<span className="navigation-tool-rail-badge" aria-hidden="true">
|
||||
{badge}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
export function navigationToolLabel(tool: NavigationToolId) {
|
||||
return NAVIGATION_TOOLS.find((definition) => definition.id === tool)?.label ?? tool;
|
||||
}
|
||||
|
||||
function toolAriaLabel(
|
||||
label: string,
|
||||
status: NavigationToolStatus,
|
||||
badge: string | null,
|
||||
active: boolean
|
||||
) {
|
||||
const parts = [label, statusLabel(status)];
|
||||
if (badge) {
|
||||
parts.push(`${badge} Hinweise`);
|
||||
}
|
||||
parts.push(active ? "geöffnet" : "öffnen");
|
||||
return parts.join(", ");
|
||||
}
|
||||
|
||||
function statusLabel(status: NavigationToolStatus) {
|
||||
switch (status) {
|
||||
case "active":
|
||||
return "aktiv";
|
||||
case "caution":
|
||||
return "Warnung";
|
||||
case "alarm":
|
||||
return "Alarm";
|
||||
case "stale":
|
||||
return "Daten veraltet";
|
||||
default:
|
||||
return "bereit";
|
||||
}
|
||||
}
|
||||
|
||||
function formatBadge(value: number | string | null | undefined) {
|
||||
if (typeof value === "number") {
|
||||
if (!Number.isFinite(value) || value <= 0) {
|
||||
return null;
|
||||
}
|
||||
return value > 99 ? "99+" : String(Math.floor(value));
|
||||
}
|
||||
const normalized = value?.trim();
|
||||
return normalized || null;
|
||||
}
|
||||
|
||||
function classNames(...values: Array<string | null | undefined | false>) {
|
||||
return values.filter(Boolean).join(" ");
|
||||
}
|
||||
Reference in New Issue
Block a user