1import type { EnvironmentConnectionPhase } from "@t3tools/client-runtime/connection";
2
3import { cn } from "~/lib/utils";
4import { Tooltip, TooltipPopup, TooltipTrigger } from "~/components/ui/tooltip";
5
6/** Canonical connection-phase → dot color mapping shared by every status dot. */
7export function connectionPhaseDotClassName(phase: EnvironmentConnectionPhase): string {
8 switch (phase) {
9 case "connected":
10 return "bg-success";
11 case "connecting":
12 case "reconnecting":
13 return "bg-warning";
14 case "error":
15 return "bg-destructive";
16 default:
17 return "bg-muted-foreground/40";
18 }
19}
20
21/** Ping halo for transitional phases; null renders no ping. */
22export function connectionPhasePingClassName(phase: EnvironmentConnectionPhase): string | null {
23 return phase === "connecting" || phase === "reconnecting" ? "bg-warning/60 duration-2000" : null;
24}
25
26type ConnectionStatusDotProps = {
27 tooltipText?: string | null;
28 dotClassName: string;
29 pingClassName?: string | null;
30};
31
32export function ConnectionStatusDot({
33 tooltipText,
34 dotClassName,
35 pingClassName,
36}: ConnectionStatusDotProps) {
37 const dotContent = (
38 <>
39 {pingClassName ? (
40 <span
41 className={cn(
42 "absolute inline-flex h-full w-full animate-status-ping rounded-full",
43 pingClassName,
44 )}
45 />
46 ) : null}
47 <span className={cn("relative inline-flex size-2 rounded-full", dotClassName)} />
48 </>
49 );
50
51 if (!tooltipText) {
52 return (
53 <span className="relative flex size-3 shrink-0 items-center justify-center">
54 {dotContent}
55 </span>
56 );
57 }
58
59 const dot = (
60 <button
61 type="button"
62 aria-label={tooltipText}
63 className="relative flex size-3 shrink-0 cursor-help items-center justify-center rounded-full outline-hidden"
64 >
65 {dotContent}
66 </button>
67 );
68
69 return (
70 <Tooltip>
71 <TooltipTrigger render={dot} />
72 <TooltipPopup side="top" className="whitespace-pre-wrap">
73 {tooltipText}
74 </TooltipPopup>
75 </Tooltip>
76 );
77}