Horizon / src /components /TopBar.jsx
NKessler's picture
Upload 6270 files
4083225 verified
Raw
History Blame Contribute Delete
3.04 kB
import { Menu, PanelLeftClose, Pencil } from "lucide-react";
import "./TopBar.css";
import { getNewsTimeframeLabel } from "../constants/timeframes.js";
function fmt(n) {
if (n == null) return "—";
if (n >= 1000) return new Intl.NumberFormat("en-US").format(n);
return String(n);
}
function fmtTime(iso) {
if (!iso) return "—";
const d = new Date(iso);
return d.toLocaleString("en-GB", {
year: "numeric",
month: "short",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
});
}
function divLabel(d) {
if (d == null) return { text: "—", tone: "" };
if (d > 0.7) return { text: "high", tone: "danger" };
if (d > 0.45) return { text: "moderate", tone: "warn" };
return { text: "low", tone: "neutral" };
}
export default function TopBar({
query,
meta,
metaLoading,
menuOpen,
onToggleMenu,
onEditTopic,
}) {
const div = divLabel(meta?.divergenceIndex);
return (
<div className="topbar">
<button
className="topbar__menu-btn"
onClick={onToggleMenu}
aria-label="Toggle visualization menu"
title="Toggle visualization menu"
>
{menuOpen ? <PanelLeftClose size={15} /> : <Menu size={15} />}
<span>views</span>
</button>
<div className="topbar__topic">
<div className="label">Topic</div>
<button
className="topbar__topic-button"
onClick={onEditTopic}
title="Change topic"
>
<span className="topbar__topic-block">
<span className="topbar__topic-text">
{query.topic || "Untitled"}
</span>
<span className="topbar__topic-meta">
{getNewsTimeframeLabel(query.newsTimeframe)}
</span>
</span>
<Pencil size={14} strokeWidth={1.5} className="topbar__topic-icon" />
</button>
</div>
<div className="topbar__metrics" aria-busy={metaLoading || undefined}>
<Metric label="Articles" value={fmt(meta?.totalArticles)} />
<Metric label="Sources" value={fmt(meta?.totalSources)} />
<Metric label="Countries" value={fmt(meta?.countriesCovered)} />
<Metric label="Languages" value={fmt(meta?.languagesCovered)} />
<Metric
label="Divergence"
value={
meta?.divergenceIndex != null
? meta.divergenceIndex.toFixed(2)
: "—"
}
accent={div.tone}
suffix={div.text}
/>
<Metric
label="Updated"
value={fmtTime(meta?.lastUpdated)}
tight
/>
</div>
</div>
);
}
function Metric({ label, value, accent = "", suffix, tight = false }) {
return (
<div className={`metric ${tight ? "metric--tight" : ""}`}>
<div className="label metric__label">{label}</div>
<div className={`metric__value metric__value--${accent}`}>
{value}
{suffix && <span className="metric__suffix">{suffix}</span>}
</div>
</div>
);
}