38 lines
1.3 KiB
React
38 lines
1.3 KiB
React
import React from "react";
|
|
|
|
const colors = {
|
|
live: "var(--status-live)",
|
|
ok: "var(--status-ok)",
|
|
warn: "var(--status-warn)",
|
|
error: "var(--status-error)",
|
|
idle: "var(--text-disabled)",
|
|
};
|
|
|
|
/**
|
|
* Status dot with optional pulsing ring — on-air / live indicators.
|
|
*/
|
|
export function StatusDot({ status = "idle", pulse = false, size = 8, label, style = {} }) {
|
|
const c = colors[status] || colors.idle;
|
|
const doPulse = pulse || status === "live";
|
|
return (
|
|
<span style={{ display: "inline-flex", alignItems: "center", gap: "7px", ...style }}>
|
|
<span style={{ position: "relative", width: size, height: size, flex: "0 0 auto" }}>
|
|
{doPulse && (
|
|
<span
|
|
style={{
|
|
position: "absolute",
|
|
inset: 0,
|
|
borderRadius: "50%",
|
|
background: c,
|
|
animation: "ctv-ping 1.6s cubic-bezier(0,0,0.2,1) infinite",
|
|
}}
|
|
/>
|
|
)}
|
|
<span style={{ position: "absolute", inset: 0, borderRadius: "50%", background: c, boxShadow: doPulse ? `0 0 6px ${c}` : "none" }} />
|
|
<style>{`@keyframes ctv-ping{0%{transform:scale(1);opacity:.55}70%,100%{transform:scale(2.4);opacity:0}}`}</style>
|
|
</span>
|
|
{label && <span style={{ font: "var(--text-xs)/1 var(--font-sans)", color: "var(--text-secondary)" }}>{label}</span>}
|
|
</span>
|
|
);
|
|
}
|