// drawer.jsx — detail view as a side drawer.
function StockDrawer({ ticker, onClose, onRemove }) {
const [quote, setQuote] = React.useState(null);
const [range, setRange] = React.useState("1D");
const [confirmingRemove, setConfirmingRemove] = React.useState(false);
React.useEffect(() => {
if (!ticker) return undefined;
let unsub;
let cancelled = false;
fetchQuote(ticker).then((q) => {
if (cancelled) return;
setQuote({ ...q });
unsub = subscribeToQuoteUpdates(ticker, (next) => setQuote({ ...next }));
});
return () => {
cancelled = true;
if (unsub) unsub();
};
}, [ticker]);
React.useEffect(() => {
if (!ticker) return undefined;
const onKey = (e) => { if (e.key === "Escape") onClose(); };
window.addEventListener("keydown", onKey);
document.body.style.overflow = "hidden";
return () => {
window.removeEventListener("keydown", onKey);
document.body.style.overflow = "";
};
}, [ticker, onClose]);
// A fresh ticker — or a close/reopen — always starts out of the confirm state.
React.useEffect(() => { setConfirmingRemove(false); }, [ticker]);
if (!ticker) return null;
const d = quote ? resolveDisplayQuote(quote) : null;
const direction = d
? d.displayChange > 0 ? "up" : d.displayChange < 0 ? "down" : "flat"
: "flat";
const sessionKey = d ? d.sessionLabel.toLowerCase().replace(/\s/g, "-") : "";
return (
<>
>
);
}
function Stat({ label, value }) {
return (
);
}
// Deterministic sparkline so each ticker/range pair stays stable across re-renders
function MiniChart({ seed, direction, points = 48 }) {
const path = React.useMemo(() => {
const W = 1000, H = 220, pad = 8;
let s = 0;
for (let i = 0; i < seed.length; i++) s = (s * 31 + seed.charCodeAt(i)) | 0;
const rnd = () => {
s = (s * 1664525 + 1013904223) | 0;
return ((s >>> 0) % 10000) / 10000;
};
const ys = [];
let v = 0.5;
for (let i = 0; i < points; i++) {
v += (rnd() - 0.5) * 0.08;
v = Math.max(0.1, Math.min(0.9, v));
ys.push(v);
}
// Bias the endpoint to match direction so the chart visually agrees with the price move
const bias = direction === "up" ? -0.18 : direction === "down" ? 0.18 : 0;
ys[0] = Math.max(0.1, Math.min(0.9, ys[0] + bias));
const stepX = (W - pad * 2) / (points - 1);
const toY = (n) => pad + (1 - n) * (H - pad * 2);
let line = "M " + pad + " " + toY(ys[0]);
for (let i = 1; i < points; i++) {
line += " L " + (pad + i * stepX) + " " + toY(ys[i]);
}
const fill = line +
" L " + (W - pad) + " " + (H - pad) +
" L " + pad + " " + (H - pad) + " Z";
return { line, fill, last: ys[ys.length - 1], lastX: W - pad };
}, [seed, points, direction]);
return (
);
}
window.StockDrawer = StockDrawer;