Horizon / src /components /TopicSelector.jsx
NKessler's picture
Upload 6270 files
4083225 verified
Raw
History Blame Contribute Delete
4.09 kB
import { useEffect, useRef, useState } from "react";
import { X } from "lucide-react";
import "./TopicSelector.css";
import { DEFAULT_NEWS_TIMEFRAME, NEWS_TIMEFRAMES } from "../constants/timeframes.js";
const SUGGESTIONS = [
"Russia–Ukraine war",
"Israel–Gaza conflict",
"Sahel coups",
"South China Sea disputes",
"Climate negotiations at COP",
"Migration across the Mediterranean",
];
export default function TopicSelector({ initialQuery, onSubmit, onCancel }) {
const [topic, setTopic] = useState(initialQuery?.topic ?? "");
const [newsTimeframe, setNewsTimeframe] = useState(
initialQuery?.newsTimeframe ?? DEFAULT_NEWS_TIMEFRAME
);
const inputRef = useRef(null);
useEffect(() => {
inputRef.current?.focus();
}, []);
useEffect(() => {
setTopic(initialQuery?.topic ?? "");
setNewsTimeframe(initialQuery?.newsTimeframe ?? DEFAULT_NEWS_TIMEFRAME);
}, [initialQuery]);
useEffect(() => {
function onKey(e) {
if (e.key === "Escape" && onCancel) onCancel();
}
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, [onCancel]);
const canSubmit = topic.trim().length > 0;
function submit(e) {
e?.preventDefault();
if (!canSubmit) return;
onSubmit({
topic: topic.trim(),
newsTimeframe,
});
}
return (
<div className="modal" role="dialog" aria-modal="true" aria-label="Define a topic">
<div className="modal__backdrop" onClick={onCancel} />
<form className="modal__panel" onSubmit={submit}>
<header className="modal__header">
<div>
<div className="label">Define a topic</div>
<h2 className="modal__title">Open an investigation</h2>
</div>
{onCancel && (
<button
type="button"
className="ghost modal__close"
aria-label="Close"
onClick={onCancel}
>
<X size={16} strokeWidth={1.5} />
</button>
)}
</header>
<div className="modal__body">
<label className="field">
<span className="label">Topic / query</span>
<input
ref={inputRef}
type="text"
value={topic}
onChange={(e) => setTopic(e.target.value)}
placeholder="e.g. Russia–Ukraine war, Gaza ceasefire negotiations…"
/>
</label>
<label className="field">
<span className="label">News timeframe</span>
<select
value={newsTimeframe}
onChange={(e) => setNewsTimeframe(e.target.value)}
>
{NEWS_TIMEFRAMES.map((item) => (
<option key={item.value} value={item.value}>
{item.label}
</option>
))}
</select>
</label>
<div className="suggestions">
<div className="label">Suggested investigations</div>
<div className="suggestions__chips">
{SUGGESTIONS.map((s) => (
<button
key={s}
type="button"
className="chip"
onClick={() => setTopic(s)}
>
{s}
</button>
))}
</div>
</div>
</div>
<footer className="modal__footer">
<p className="modal__hint">
FrameVis will compile coverage from international wires, national
press, and partisan outlets — then surface where their narratives
diverge.
</p>
<div className="modal__actions">
{onCancel && (
<button type="button" onClick={onCancel}>
Cancel
</button>
)}
<button type="submit" className="primary" disabled={!canSubmit}>
Open investigation
</button>
</div>
</footer>
</form>
</div>
);
}