Files
Termix/src/ui/hooks/use-status-color-scheme.ts
T

88 lines
3.0 KiB
TypeScript

import { useState, useEffect } from "react";
import type { StatusColorScheme } from "@/types/host-sidebar-preferences";
export type { StatusColorScheme };
const PREFS_KEY = "hostSidebarPreferences";
const PREFS_EVENT = "hostSidebarPreferencesChanged";
/**
* Reads the scheme straight off the sidebar preferences cache that
* useHostSidebarPreferences writes, so the toggle in Customize Sidebar takes
* effect without a reload. Exported for imperative callers that build markup
* outside React (the topology SVG).
*/
export function readStatusColorScheme(): StatusColorScheme {
try {
const raw = localStorage.getItem(PREFS_KEY);
if (!raw) return "accent";
const parsed = JSON.parse(raw) as {
display?: { statusColorScheme?: string };
};
return parsed?.display?.statusColorScheme === "status"
? "status"
: "accent";
} catch {
return "accent";
}
}
export function useStatusColorScheme(): StatusColorScheme {
const [scheme, setScheme] = useState<StatusColorScheme>(
readStatusColorScheme,
);
useEffect(() => {
const handler = () => setScheme(readStatusColorScheme());
handler();
window.addEventListener(PREFS_EVENT, handler);
window.addEventListener("storage", handler);
return () => {
window.removeEventListener(PREFS_EVENT, handler);
window.removeEventListener("storage", handler);
};
}, []);
return scheme;
}
/** Returns Tailwind class names for a status dot/stripe. */
export function getStatusClasses(
status: boolean | "online" | "offline" | "unknown",
scheme: StatusColorScheme,
variant: "dot" | "stripe" | "badge",
loading = false,
): string {
const online = status === true || status === "online";
if (loading || status === "unknown") {
if (scheme === "status") {
if (variant === "dot") return "bg-warning animate-pulse";
if (variant === "stripe") return "bg-warning/40 animate-pulse";
return "border-warning/40 text-warning bg-warning/10 animate-pulse";
}
if (variant === "dot") return "bg-muted-foreground/40 animate-pulse";
if (variant === "stripe") return "bg-muted-foreground/20 animate-pulse";
return "border-border/50 text-muted-foreground/50 bg-muted/20 animate-pulse";
}
if (scheme === "status") {
if (variant === "dot") return online ? "bg-emerald-500" : "bg-red-500";
if (variant === "stripe")
return online ? "bg-emerald-500" : "bg-red-500/40";
// badge
return online
? "border-emerald-500/40 text-emerald-500 bg-emerald-500/10"
: "border-red-500/40 text-red-500 bg-red-500/10";
}
// accent scheme
if (variant === "dot")
return online ? "bg-accent-brand" : "bg-muted-foreground/25";
// Offline keeps a faint neutral line rather than nothing, so the stripe
// column stays visible instead of blending into the background.
if (variant === "stripe")
return online ? "bg-accent-brand" : "bg-muted-foreground/20";
// badge
return online
? "border-accent-brand/40 text-accent-brand bg-accent-brand/10"
: "border-border/50 text-muted-foreground/60 bg-muted/30";
}