mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-13 08:23:32 -07:00
143 lines
3.5 KiB
JavaScript
143 lines
3.5 KiB
JavaScript
import { createSeriesHighlight } from "./highlight.js";
|
|
import { createSeriesLoader } from "./loader.js";
|
|
import { renderPlot } from "./plot.js";
|
|
import { createScrubber } from "./scrubber/index.js";
|
|
import { createSvgElement } from "./svg.js";
|
|
import { getViewBoxHeight, VIEWBOX_WIDTH } from "./viewbox.js";
|
|
|
|
/**
|
|
* @param {Object} args
|
|
* @param {SVGSVGElement} args.svg
|
|
* @param {Readout} args.readout
|
|
* @param {HTMLElement} args.menu
|
|
* @param {HTMLElement[]} args.items
|
|
* @param {HTMLElement} args.status
|
|
* @param {Chart} args.chart
|
|
* @param {() => ChartView} args.getView
|
|
* @param {() => ChartScale} args.getScale
|
|
* @param {() => ChartOrder} args.getOrder
|
|
* @param {() => TimeframeValue} args.getTimeframe
|
|
*/
|
|
export function createChartRenderer({
|
|
svg,
|
|
readout,
|
|
menu,
|
|
items,
|
|
status,
|
|
chart,
|
|
getView,
|
|
getScale,
|
|
getOrder,
|
|
getTimeframe,
|
|
}) {
|
|
const group = createSvgElement("g");
|
|
const highlight = createSeriesHighlight(items, menu);
|
|
const loadSeries = createSeriesLoader(chart);
|
|
/** @type {LoadedSeries[]} */
|
|
let loadedSeries = [];
|
|
/** @type {ReturnType<typeof createScrubber> | undefined} */
|
|
let scrubber;
|
|
const resizeObserver = new ResizeObserver(renderCurrent);
|
|
let active = false;
|
|
let loadId = 0;
|
|
|
|
svg.append(group);
|
|
|
|
function clearStatus() {
|
|
status.textContent = "";
|
|
svg.removeAttribute("aria-busy");
|
|
}
|
|
|
|
/** @param {string} message */
|
|
function setStatus(message) {
|
|
status.textContent = message;
|
|
}
|
|
|
|
function renderCurrent() {
|
|
if (!active || !loadedSeries.length) return;
|
|
|
|
const height = getViewBoxHeight(svg);
|
|
|
|
svg.setAttribute("viewBox", `0 0 ${VIEWBOX_WIDTH} ${height}`);
|
|
group.replaceChildren();
|
|
highlight.clearNodes();
|
|
scrubber ??= createScrubber(svg, readout, highlight);
|
|
scrubber.setSeries(
|
|
renderPlot(
|
|
getView(),
|
|
group,
|
|
loadedSeries,
|
|
height,
|
|
highlight,
|
|
getScale(),
|
|
getOrder(),
|
|
),
|
|
height,
|
|
);
|
|
}
|
|
|
|
async function loadCurrent() {
|
|
const id = (loadId += 1);
|
|
const loadingTimer = setTimeout(() => {
|
|
if (id === loadId && active) setStatus("Loading");
|
|
}, 250);
|
|
|
|
setStatus("");
|
|
svg.setAttribute("aria-busy", "true");
|
|
|
|
try {
|
|
const nextSeries = await loadSeries(getTimeframe());
|
|
|
|
if (id !== loadId || !active) return;
|
|
|
|
loadedSeries = nextSeries;
|
|
renderCurrent();
|
|
clearStatus();
|
|
} catch (error) {
|
|
if (id !== loadId) return;
|
|
console.error(error);
|
|
setStatus("Chart unavailable");
|
|
} finally {
|
|
clearTimeout(loadingTimer);
|
|
if (id === loadId) svg.removeAttribute("aria-busy");
|
|
}
|
|
}
|
|
|
|
function resume() {
|
|
if (active) return;
|
|
|
|
active = true;
|
|
resizeObserver.observe(svg);
|
|
void loadCurrent();
|
|
}
|
|
|
|
function suspend() {
|
|
if (!active) return;
|
|
|
|
active = false;
|
|
loadedSeries = [];
|
|
loadId += 1;
|
|
resizeObserver.disconnect();
|
|
group.replaceChildren();
|
|
highlight.clearNodes();
|
|
scrubber?.clear();
|
|
clearStatus();
|
|
}
|
|
|
|
return {
|
|
loadCurrent,
|
|
renderCurrent,
|
|
resume,
|
|
suspend,
|
|
};
|
|
}
|
|
|
|
/** @typedef {import("./index.js").Chart} Chart */
|
|
/** @typedef {import("./index.js").LoadedSeries} LoadedSeries */
|
|
/** @typedef {import("./legend/index.js").Readout} Readout */
|
|
/** @typedef {import("./order.js").ChartOrder} ChartOrder */
|
|
/** @typedef {import("./scale.js").ChartScale} ChartScale */
|
|
/** @typedef {import("./timeframes.js").TimeframeValue} TimeframeValue */
|
|
/** @typedef {import("./views.js").ChartView} ChartView */
|
|
/** @typedef {import("./highlight.js").SeriesHighlight} SeriesHighlight */
|