Files
next-tjwater-frontend/src/shared/ui/status.tsx
T
jiang f4318a9b9c feat: refine workbench visuals and map controls
Unify the Agent history extension with the header acrylic surface, preserve the full conversation body, and consolidate shared control and status styling.

Restore map flow and SCADA controller behavior, remove obsolete rendering paths, and extend regression coverage. Button press coverage now releases outside the target so state assertions cannot accidentally toggle the control.
2026-07-28 16:39:36 +08:00

127 lines
2.9 KiB
TypeScript

import type { ComponentProps, ReactNode } from "react";
import { cn } from "@/shared/ui/cn";
export type StatusTone = "neutral" | "info" | "success" | "warning" | "danger";
export type StatusActivity = "static" | "live" | "loading";
const toneClassNames: Record<StatusTone, string> = {
neutral: "status-tone-neutral",
info: "status-tone-info",
success: "status-tone-success",
warning: "status-tone-warning",
danger: "status-tone-danger"
};
type StatusBadgeProps = ComponentProps<"span"> & {
tone: StatusTone;
activity?: StatusActivity;
icon?: ReactNode;
size?: "sm" | "md";
};
export function StatusBadge({
tone,
activity = "static",
icon,
size = "sm",
className,
children,
...props
}: StatusBadgeProps) {
return (
<span
className={cn(
"status-badge inline-flex shrink-0 items-center rounded-full border font-semibold",
size === "sm" ? "min-h-5 gap-1 px-2 py-0.5 text-xs leading-4" : "min-h-6 gap-1.5 px-2.5 py-0.5 text-xs leading-5",
toneClassNames[tone],
className
)}
data-status-tone={tone}
{...props}
>
{icon ? (
<span
className="status-glyph grid shrink-0 place-items-center"
data-activity={activity}
aria-hidden="true"
>
{icon}
</span>
) : null}
{children}
</span>
);
}
type StatusDotProps = Omit<ComponentProps<"span">, "children"> & {
tone: StatusTone;
activity?: StatusActivity;
label?: string;
size?: "sm" | "md";
};
export function StatusDot({
tone,
activity = "static",
label,
size = "sm",
className,
...props
}: StatusDotProps) {
return (
<span
className={cn(
"status-dot inline-block shrink-0 rounded-full",
size === "sm" ? "h-1.5 w-1.5" : "h-2 w-2",
toneClassNames[tone],
className
)}
data-activity={activity}
data-status-tone={tone}
role={label ? "status" : undefined}
aria-label={label}
aria-hidden={label ? undefined : true}
{...props}
/>
);
}
type StatusIconProps = ComponentProps<"span"> & {
tone: StatusTone;
activity?: StatusActivity;
shape?: "rounded" | "circle";
size?: "xs" | "sm" | "md";
};
export function StatusIcon({
tone,
activity = "static",
shape = "rounded",
size = "sm",
className,
children,
...props
}: StatusIconProps) {
return (
<span
className={cn(
"status-icon inline-grid shrink-0 place-items-center border",
shape === "circle" ? "rounded-full" : "rounded-lg",
size === "xs" ? "h-3.5 w-3.5" : size === "sm" ? "h-6 w-6" : "h-7 w-7",
toneClassNames[tone],
className
)}
data-status-tone={tone}
{...props}
>
<span
className="status-glyph grid place-items-center"
data-activity={activity}
aria-hidden="true"
>
{children}
</span>
</span>
);
}