mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-11 23:43:32 -07:00
website: redesign part 27
This commit is contained in:
@@ -5,7 +5,8 @@ import { createLineSeries } from "../line/series.js";
|
||||
|
||||
/**
|
||||
* @param {number} height
|
||||
* @param {{ date: Date, value: number, x: number, y: number }[]} points
|
||||
* @param {ChartPoint[]} points
|
||||
* @returns {StackedPoint[]}
|
||||
*/
|
||||
function createAreaPoints(height, points) {
|
||||
return points.map((point) => ({
|
||||
@@ -16,21 +17,16 @@ function createAreaPoints(height, points) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SVGGElement} group
|
||||
* @param {LoadedSeries[]} loadedSeries
|
||||
* @param {number} height
|
||||
* @param {SeriesHighlight} highlight
|
||||
* @param {import("../scale.js").ChartScale} scale
|
||||
* @param {import("../order.js").ChartOrder} order
|
||||
* @param {PlotContext} context
|
||||
*/
|
||||
export function renderAreaPlot(
|
||||
export function renderAreaPlot({
|
||||
group,
|
||||
loadedSeries,
|
||||
height,
|
||||
highlight,
|
||||
scale,
|
||||
order,
|
||||
) {
|
||||
}) {
|
||||
const plottedSeries = createLineSeries(loadedSeries, height, scale);
|
||||
const indexes = createOrderedIndexes(plottedSeries.length, order);
|
||||
|
||||
@@ -57,6 +53,3 @@ export function renderAreaPlot(
|
||||
|
||||
return plottedSeries;
|
||||
}
|
||||
|
||||
/** @typedef {import("../highlight.js").SeriesHighlight} SeriesHighlight */
|
||||
/** @typedef {import("../index.js").LoadedSeries} LoadedSeries */
|
||||
|
||||
@@ -4,13 +4,13 @@ import { createStackedSeries } from "../stacked/series.js";
|
||||
import { clamp } from "../math.js";
|
||||
import { appendSeriesPath } from "../series-path.js";
|
||||
|
||||
/** @param {{ x: number, y0: number, y1: number }[]} points */
|
||||
/** @param {StackedPoint[]} points */
|
||||
function getBarWidth(points) {
|
||||
return points.length > 1 ? (VIEWBOX_WIDTH / (points.length - 1)) * 0.8 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ x: number, y0: number, y1: number }[]} points
|
||||
* @param {StackedPoint[]} points
|
||||
* @param {number} width
|
||||
*/
|
||||
function createBarPathData(points, width) {
|
||||
@@ -31,21 +31,16 @@ function createBarPathData(points, width) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SVGGElement} group
|
||||
* @param {LoadedSeries[]} loadedSeries
|
||||
* @param {number} height
|
||||
* @param {SeriesHighlight} highlight
|
||||
* @param {import("../scale.js").ChartScale} scale
|
||||
* @param {import("../order.js").ChartOrder} order
|
||||
* @param {PlotContext} context
|
||||
*/
|
||||
export function renderBarPlot(
|
||||
export function renderBarPlot({
|
||||
group,
|
||||
loadedSeries,
|
||||
height,
|
||||
highlight,
|
||||
scale,
|
||||
order,
|
||||
) {
|
||||
}) {
|
||||
const { lineIndexes, plottedSeries, stackIndexes } = createStackedSeries(
|
||||
loadedSeries,
|
||||
height,
|
||||
@@ -79,6 +74,3 @@ export function renderBarPlot(
|
||||
|
||||
return plottedSeries;
|
||||
}
|
||||
|
||||
/** @typedef {import("../highlight.js").SeriesHighlight} SeriesHighlight */
|
||||
/** @typedef {import("../index.js").LoadedSeries} LoadedSeries */
|
||||
|
||||
@@ -5,7 +5,7 @@ import { createLineSeries } from "../line/series.js";
|
||||
|
||||
const radius = 1;
|
||||
|
||||
/** @param {{ x: number, y: number }[]} points */
|
||||
/** @param {ChartPoint[]} points */
|
||||
function createDotsPathData(points) {
|
||||
return points
|
||||
.map(
|
||||
@@ -18,21 +18,16 @@ function createDotsPathData(points) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SVGGElement} group
|
||||
* @param {LoadedSeries[]} loadedSeries
|
||||
* @param {number} height
|
||||
* @param {SeriesHighlight} highlight
|
||||
* @param {import("../scale.js").ChartScale} scale
|
||||
* @param {import("../order.js").ChartOrder} order
|
||||
* @param {PlotContext} context
|
||||
*/
|
||||
export function renderDotsPlot(
|
||||
export function renderDotsPlot({
|
||||
group,
|
||||
loadedSeries,
|
||||
height,
|
||||
highlight,
|
||||
scale,
|
||||
order,
|
||||
) {
|
||||
}) {
|
||||
const plottedSeries = createLineSeries(loadedSeries, height, scale);
|
||||
const indexes = createOrderedIndexes(plottedSeries.length, order);
|
||||
|
||||
@@ -50,6 +45,3 @@ export function renderDotsPlot(
|
||||
|
||||
return plottedSeries;
|
||||
}
|
||||
|
||||
/** @typedef {import("../highlight.js").SeriesHighlight} SeriesHighlight */
|
||||
/** @typedef {import("../index.js").LoadedSeries} LoadedSeries */
|
||||
|
||||
@@ -150,11 +150,3 @@ function clearElementState(element) {
|
||||
}
|
||||
|
||||
/** @typedef {(SVGPathElement | SVGCircleElement)[]} SeriesNode */
|
||||
|
||||
/**
|
||||
* @typedef {Object} SeriesHighlight
|
||||
* @property {(node: SVGPathElement | SVGCircleElement, index: number) => void} addNode
|
||||
* @property {() => void} clearPreview
|
||||
* @property {() => void} clearNodes
|
||||
* @property {(index: number) => void} preview
|
||||
*/
|
||||
|
||||
@@ -131,32 +131,3 @@ export function createChart(chart, chartKey) {
|
||||
|
||||
return figure;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} Chart
|
||||
* @property {string} title
|
||||
* @property {import("./units.js").ChartUnit} unit
|
||||
* @property {import("./views.js").ChartView} [defaultType]
|
||||
* @property {import("./scale.js").ChartScale} [defaultScale]
|
||||
* @property {ChartSeries[]} series
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ChartSeries
|
||||
* @property {string} label
|
||||
* @property {() => string} color
|
||||
* @property {"line"} [role]
|
||||
* @property {(client: typeof import("../../utils/client.js").brk) => import("./timeframes.js").TimeframeMetric} metric
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ChartResult
|
||||
* @property {() => Iterable<[Date, number | null | undefined]>} dateEntries
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} LoadedSeries
|
||||
* @property {ChartSeries} series
|
||||
* @property {string} color
|
||||
* @property {{ date: Date, value: number }[]} entries
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @param {Chart} chart
|
||||
* @returns {{ legend: HTMLElement, menu: HTMLElement, items: HTMLElement[], readout: Readout }}
|
||||
* @returns {{ legend: HTMLElement, menu: HTMLElement, items: HTMLElement[], readout: LegendReadout }}
|
||||
*/
|
||||
export function createLegend(chart) {
|
||||
const legend = document.createElement("figcaption");
|
||||
@@ -41,11 +41,3 @@ export function createLegend(chart) {
|
||||
|
||||
return { legend, menu, items, readout: { time, rows } };
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} Readout
|
||||
* @property {HTMLTimeElement} time
|
||||
* @property {{ value: HTMLOutputElement }[]} rows
|
||||
*/
|
||||
|
||||
/** @typedef {import("../index.js").Chart} Chart */
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
import { createOrderedIndexes } from "../order.js";
|
||||
import { createLinePathData } from "../path.js";
|
||||
import { appendSeriesPath } from "../series-path.js";
|
||||
import { createOrderedIndexes } from "../order.js";
|
||||
import { createLineSeries } from "./series.js";
|
||||
|
||||
/**
|
||||
* @param {SVGGElement} group
|
||||
* @param {LoadedSeries[]} loadedSeries
|
||||
* @param {number} height
|
||||
* @param {SeriesHighlight} highlight
|
||||
* @param {import("../scale.js").ChartScale} scale
|
||||
* @param {import("../order.js").ChartOrder} order
|
||||
* @param {PlotContext} context
|
||||
*/
|
||||
export function renderLinePlot(
|
||||
export function renderLinePlot({
|
||||
group,
|
||||
loadedSeries,
|
||||
height,
|
||||
highlight,
|
||||
scale,
|
||||
order,
|
||||
) {
|
||||
}) {
|
||||
const plottedSeries = createLineSeries(loadedSeries, height, scale);
|
||||
const indexes = createOrderedIndexes(plottedSeries.length, order);
|
||||
|
||||
@@ -36,6 +31,3 @@ export function renderLinePlot(
|
||||
|
||||
return plottedSeries;
|
||||
}
|
||||
|
||||
/** @typedef {import("../highlight.js").SeriesHighlight} SeriesHighlight */
|
||||
/** @typedef {import("../index.js").LoadedSeries} LoadedSeries */
|
||||
|
||||
@@ -15,10 +15,11 @@ function createValueBounds(series) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ date: Date, value: number }[]} entries
|
||||
* @param {import("../scale.js").ScaleBounds} bounds
|
||||
* @param {ChartEntry[]} entries
|
||||
* @param {ScaleBounds} bounds
|
||||
* @param {number} height
|
||||
* @param {import("../scale.js").ChartScale} scale
|
||||
* @param {ChartScale} scale
|
||||
* @returns {ChartPoint[]}
|
||||
*/
|
||||
function createPoints(entries, bounds, height, scale) {
|
||||
const xScale = VIEWBOX_WIDTH / (entries.length - 1);
|
||||
@@ -34,7 +35,7 @@ function createPoints(entries, bounds, height, scale) {
|
||||
/**
|
||||
* @param {LoadedSeries[]} loadedSeries
|
||||
* @param {number} height
|
||||
* @param {import("../scale.js").ChartScale} scale
|
||||
* @param {ChartScale} scale
|
||||
*/
|
||||
export function createLineSeries(loadedSeries, height, scale) {
|
||||
const bounds = createValueBounds(loadedSeries);
|
||||
@@ -45,5 +46,3 @@ export function createLineSeries(loadedSeries, height, scale) {
|
||||
points: createPoints(entries, bounds, height, scale),
|
||||
}));
|
||||
}
|
||||
|
||||
/** @typedef {import("../index.js").LoadedSeries} LoadedSeries */
|
||||
|
||||
@@ -3,10 +3,10 @@ import { fetchTimeframe } from "./timeframes.js";
|
||||
|
||||
/**
|
||||
* @param {ChartResult} result
|
||||
* @returns {{ date: Date, value: number }[]}
|
||||
* @returns {ChartEntry[]}
|
||||
*/
|
||||
function createEntries(result) {
|
||||
/** @type {{ date: Date, value: number }[]} */
|
||||
/** @type {ChartEntry[]} */
|
||||
const entries = [];
|
||||
/** @type {number | undefined} */
|
||||
let lastValue;
|
||||
@@ -53,8 +53,3 @@ export function createSeriesLoader(chart) {
|
||||
return cachedPromise;
|
||||
};
|
||||
}
|
||||
|
||||
/** @typedef {import("./index.js").Chart} Chart */
|
||||
/** @typedef {import("./index.js").ChartResult} ChartResult */
|
||||
/** @typedef {import("./index.js").LoadedSeries} LoadedSeries */
|
||||
/** @typedef {import("./timeframes.js").TimeframeValue} TimeframeValue */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createChartSetting } from "./setting.js";
|
||||
|
||||
const orders = /** @type {const} */ ([
|
||||
export const orders = /** @type {const} */ ([
|
||||
{ value: "ascending", label: "Asc" },
|
||||
{ value: "descending", label: "Dsc" },
|
||||
]);
|
||||
@@ -55,5 +55,3 @@ export function createOrderedIndexes(length, order) {
|
||||
order,
|
||||
);
|
||||
}
|
||||
|
||||
/** @typedef {(typeof orders)[number]["value"]} ChartOrder */
|
||||
|
||||
@@ -12,14 +12,14 @@ function createPathCommand(command, x, y) {
|
||||
return `${command}${formatCoordinate(x)} ${formatCoordinate(y)}`;
|
||||
}
|
||||
|
||||
/** @param {{ x: number, y: number }[]} points */
|
||||
/** @param {ChartPoint[]} points */
|
||||
export function createLinePathData(points) {
|
||||
return points
|
||||
.map(({ x, y }, index) => createPathCommand(index ? "L" : "M", x, y))
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
/** @param {{ x: number, y0: number, y1: number }[]} points */
|
||||
/** @param {StackedPoint[]} points */
|
||||
export function createAreaPathData(points) {
|
||||
const commands = points.map(({ x, y1 }, index) =>
|
||||
createPathCommand(index ? "L" : "M", x, y1),
|
||||
|
||||
@@ -6,66 +6,19 @@ import { renderStackedPlot } from "./stacked/index.js";
|
||||
|
||||
/**
|
||||
* @param {ChartView} view
|
||||
* @param {SVGGElement} group
|
||||
* @param {LoadedSeries[]} loadedSeries
|
||||
* @param {number} height
|
||||
* @param {SeriesHighlight} highlight
|
||||
* @param {ChartScale} scale
|
||||
* @param {ChartOrder} order
|
||||
* @param {PlotContext} context
|
||||
*/
|
||||
export function renderPlot(
|
||||
view,
|
||||
group,
|
||||
loadedSeries,
|
||||
height,
|
||||
highlight,
|
||||
scale,
|
||||
order,
|
||||
) {
|
||||
export function renderPlot(view, context) {
|
||||
switch (view) {
|
||||
case "line":
|
||||
return renderLinePlot(
|
||||
group,
|
||||
loadedSeries,
|
||||
height,
|
||||
highlight,
|
||||
scale,
|
||||
order,
|
||||
);
|
||||
return renderLinePlot(context);
|
||||
case "area":
|
||||
return renderAreaPlot(
|
||||
group,
|
||||
loadedSeries,
|
||||
height,
|
||||
highlight,
|
||||
scale,
|
||||
order,
|
||||
);
|
||||
return renderAreaPlot(context);
|
||||
case "bar":
|
||||
return renderBarPlot(group, loadedSeries, height, highlight, scale, order);
|
||||
return renderBarPlot(context);
|
||||
case "dots":
|
||||
return renderDotsPlot(
|
||||
group,
|
||||
loadedSeries,
|
||||
height,
|
||||
highlight,
|
||||
scale,
|
||||
order,
|
||||
);
|
||||
return renderDotsPlot(context);
|
||||
case "stacked":
|
||||
return renderStackedPlot(
|
||||
group,
|
||||
loadedSeries,
|
||||
height,
|
||||
highlight,
|
||||
scale,
|
||||
order,
|
||||
);
|
||||
return renderStackedPlot(context);
|
||||
}
|
||||
}
|
||||
|
||||
/** @typedef {import("./highlight.js").SeriesHighlight} SeriesHighlight */
|
||||
/** @typedef {import("./index.js").LoadedSeries} LoadedSeries */
|
||||
/** @typedef {import("./order.js").ChartOrder} ChartOrder */
|
||||
/** @typedef {import("./scale.js").ChartScale} ChartScale */
|
||||
/** @typedef {import("./views.js").ChartView} ChartView */
|
||||
|
||||
@@ -8,7 +8,7 @@ import { getViewBoxHeight, VIEWBOX_WIDTH } from "./viewbox.js";
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {SVGSVGElement} args.svg
|
||||
* @param {Readout} args.readout
|
||||
* @param {LegendReadout} args.readout
|
||||
* @param {HTMLElement} args.menu
|
||||
* @param {HTMLElement[]} args.items
|
||||
* @param {HTMLElement} args.status
|
||||
@@ -63,15 +63,14 @@ export function createChartRenderer({
|
||||
highlight.clearNodes();
|
||||
scrubber ??= createScrubber(svg, readout, highlight, chart.unit.format);
|
||||
scrubber.setSeries(
|
||||
renderPlot(
|
||||
getView(),
|
||||
renderPlot(getView(), {
|
||||
group,
|
||||
loadedSeries,
|
||||
height,
|
||||
highlight,
|
||||
getScale(),
|
||||
getOrder(),
|
||||
),
|
||||
scale: getScale(),
|
||||
order: getOrder(),
|
||||
}),
|
||||
height,
|
||||
);
|
||||
}
|
||||
@@ -131,12 +130,3 @@ export function createChartRenderer({
|
||||
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 */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createChartSetting } from "./setting.js";
|
||||
|
||||
const scales = /** @type {const} */ ([
|
||||
export const scales = /** @type {const} */ ([
|
||||
{ value: "linear", label: "Lin" },
|
||||
{ value: "log", label: "Log" },
|
||||
]);
|
||||
@@ -79,12 +79,3 @@ export function scaleY(value, bounds, height, scale) {
|
||||
|
||||
return height - ((value - bounds.min) / (bounds.max - bounds.min)) * height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} ScaleBounds
|
||||
* @property {number} min
|
||||
* @property {number} max
|
||||
* @property {number} minPositive
|
||||
*/
|
||||
|
||||
/** @typedef {(typeof scales)[number]["value"]} ChartScale */
|
||||
|
||||
@@ -2,9 +2,6 @@ import { clamp } from "../math.js";
|
||||
import { createSvgElement } from "../svg.js";
|
||||
import { VIEWBOX_WIDTH } from "../viewbox.js";
|
||||
|
||||
/** @typedef {import("../highlight.js").SeriesHighlight} SeriesHighlight */
|
||||
/** @typedef {import("../legend/index.js").Readout} Readout */
|
||||
|
||||
const dateFormat = new Intl.DateTimeFormat("en-US", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
@@ -27,7 +24,7 @@ function getPointAtStep(series, step) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ReturnType<typeof getPointAtStep>[]} points
|
||||
* @param {ChartPoint[]} points
|
||||
* @param {number} y
|
||||
*/
|
||||
function getClosestPointIndex(points, y) {
|
||||
@@ -56,8 +53,8 @@ function updateTime(time, date) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Readout} readout
|
||||
* @param {ReturnType<typeof getPointAtStep>[]} points
|
||||
* @param {LegendReadout} readout
|
||||
* @param {ChartPoint[]} points
|
||||
* @param {(value: number) => string} format
|
||||
*/
|
||||
function updateReadout(readout, points, format) {
|
||||
@@ -70,7 +67,7 @@ function updateReadout(readout, points, format) {
|
||||
|
||||
/**
|
||||
* @param {SVGSVGElement} svg
|
||||
* @param {Readout} readout
|
||||
* @param {LegendReadout} readout
|
||||
* @param {SeriesHighlight} highlight
|
||||
* @param {(value: number) => string} format
|
||||
*/
|
||||
@@ -231,5 +228,5 @@ export function createScrubber(svg, readout, highlight, format) {
|
||||
/**
|
||||
* @typedef {Object} ScrubberSeries
|
||||
* @property {string} color
|
||||
* @property {{ date: Date, value: number, x: number, y: number }[]} points
|
||||
* @property {ChartPoint[]} points
|
||||
*/
|
||||
|
||||
@@ -21,5 +21,3 @@ export function appendSeriesPath(args) {
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/** @typedef {import("./highlight.js").SeriesHighlight} SeriesHighlight */
|
||||
|
||||
@@ -3,21 +3,16 @@ import { appendSeriesPath } from "../series-path.js";
|
||||
import { createStackedSeries } from "./series.js";
|
||||
|
||||
/**
|
||||
* @param {SVGGElement} group
|
||||
* @param {LoadedSeries[]} loadedSeries
|
||||
* @param {number} height
|
||||
* @param {SeriesHighlight} highlight
|
||||
* @param {import("../scale.js").ChartScale} scale
|
||||
* @param {import("../order.js").ChartOrder} order
|
||||
* @param {PlotContext} context
|
||||
*/
|
||||
export function renderStackedPlot(
|
||||
export function renderStackedPlot({
|
||||
group,
|
||||
loadedSeries,
|
||||
height,
|
||||
highlight,
|
||||
scale,
|
||||
order,
|
||||
) {
|
||||
}) {
|
||||
const { lineIndexes, plottedSeries, stackIndexes } = createStackedSeries(
|
||||
loadedSeries,
|
||||
height,
|
||||
@@ -51,6 +46,3 @@ export function renderStackedPlot(
|
||||
|
||||
return plottedSeries;
|
||||
}
|
||||
|
||||
/** @typedef {import("../highlight.js").SeriesHighlight} SeriesHighlight */
|
||||
/** @typedef {import("../index.js").LoadedSeries} LoadedSeries */
|
||||
|
||||
@@ -37,16 +37,11 @@ function createStackBounds(series, stackOrder, lineIndexes) {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/** @returns {StackedPoint[]} */
|
||||
function createStackedPoints() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LoadedSeries[]} loadedSeries
|
||||
* @param {number} height
|
||||
* @param {import("../order.js").ChartOrder} order
|
||||
* @param {import("../scale.js").ChartScale} scale
|
||||
* @param {ChartOrder} order
|
||||
* @param {ChartScale} scale
|
||||
*/
|
||||
export function createStackedSeries(loadedSeries, height, order, scale) {
|
||||
const indexes = loadedSeries.map((_, index) => index);
|
||||
@@ -64,7 +59,7 @@ export function createStackedSeries(loadedSeries, height, order, scale) {
|
||||
const plottedSeries = loadedSeries.map(({ series, color }) => ({
|
||||
series,
|
||||
color,
|
||||
points: createStackedPoints(),
|
||||
points: /** @type {StackedPoint[]} */ ([]),
|
||||
}));
|
||||
|
||||
const bounds = createStackBounds(loadedSeries, stackIndexes, lineIndexes);
|
||||
@@ -116,15 +111,3 @@ export function createStackedSeries(loadedSeries, height, order, scale) {
|
||||
stackIndexes,
|
||||
};
|
||||
}
|
||||
|
||||
/** @typedef {import("../index.js").LoadedSeries} LoadedSeries */
|
||||
|
||||
/**
|
||||
* @typedef {Object} StackedPoint
|
||||
* @property {Date} date
|
||||
* @property {number} value
|
||||
* @property {number} x
|
||||
* @property {number} y
|
||||
* @property {number} y0
|
||||
* @property {number} y1
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createChartSetting } from "./setting.js";
|
||||
|
||||
const timeframes = /** @type {const} */ ({
|
||||
export const timeframes = /** @type {const} */ ({
|
||||
"1d": { index: "minute10", count: 144 },
|
||||
"1w": { index: "hour1", count: 168 },
|
||||
"1m": { index: "hour4", count: 186 },
|
||||
@@ -9,7 +9,7 @@ const timeframes = /** @type {const} */ ({
|
||||
"8y": { index: "week1", count: 418 },
|
||||
all: { index: "week1" },
|
||||
});
|
||||
const options = /** @type {const} */ ([
|
||||
export const timeframeOptions = /** @type {const} */ ([
|
||||
{ value: "1d", label: "1d" },
|
||||
{ value: "1w", label: "1w" },
|
||||
{ value: "1m", label: "1m" },
|
||||
@@ -21,7 +21,7 @@ const options = /** @type {const} */ ([
|
||||
const setting = createChartSetting({
|
||||
storageKey: "timeframe",
|
||||
legend: "Time",
|
||||
options,
|
||||
options: timeframeOptions,
|
||||
defaultValue: "all",
|
||||
});
|
||||
|
||||
@@ -58,17 +58,3 @@ export function fetchTimeframe(metric, timeframe) {
|
||||
? endpoint.last(config.count).fetch()
|
||||
: endpoint.fetch();
|
||||
}
|
||||
|
||||
/** @typedef {(typeof options)[number]["value"]} TimeframeValue */
|
||||
/** @typedef {(typeof timeframes)[TimeframeValue]["index"]} TimeframeIndex */
|
||||
|
||||
/**
|
||||
* @typedef {Object} TimeframeEndpoint
|
||||
* @property {() => Promise<import("./index.js").ChartResult>} fetch
|
||||
* @property {(count: number) => { fetch: () => Promise<import("./index.js").ChartResult> }} last
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} TimeframeMetric
|
||||
* @property {Record<TimeframeIndex, TimeframeEndpoint>} by
|
||||
*/
|
||||
|
||||
Vendored
+89
@@ -0,0 +1,89 @@
|
||||
import { brk } from "../../utils/client.js";
|
||||
import { orders } from "./order.js";
|
||||
import { scales } from "./scale.js";
|
||||
import { timeframes, timeframeOptions } from "./timeframes.js";
|
||||
import { views } from "./views.js";
|
||||
|
||||
declare global {
|
||||
type ChartEntry = {
|
||||
date: Date;
|
||||
value: number;
|
||||
};
|
||||
type ChartMetric = (client: typeof brk) => TimeframeMetric;
|
||||
type ChartOrder = (typeof orders)[number]["value"];
|
||||
type ChartPoint = ChartEntry & {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
type ChartResult = {
|
||||
dateEntries(): Iterable<[Date, number | null | undefined]>;
|
||||
};
|
||||
type ChartScale = (typeof scales)[number]["value"];
|
||||
type ChartSeries = {
|
||||
label: string;
|
||||
color: () => string;
|
||||
role?: "line";
|
||||
metric: ChartMetric;
|
||||
};
|
||||
type ChartUnit = {
|
||||
id: string;
|
||||
name: string;
|
||||
format(value: number): string;
|
||||
};
|
||||
type ChartView = (typeof views)[number]["value"];
|
||||
type Chart = {
|
||||
title: string;
|
||||
unit: ChartUnit;
|
||||
defaultType?: ChartView;
|
||||
defaultScale?: ChartScale;
|
||||
series: ChartSeries[];
|
||||
};
|
||||
|
||||
type LegendReadout = {
|
||||
time: HTMLTimeElement;
|
||||
rows: { value: HTMLOutputElement }[];
|
||||
};
|
||||
type LoadedSeries = {
|
||||
series: ChartSeries;
|
||||
color: string;
|
||||
entries: ChartEntry[];
|
||||
};
|
||||
type PlotContext = {
|
||||
group: SVGGElement;
|
||||
loadedSeries: LoadedSeries[];
|
||||
height: number;
|
||||
highlight: SeriesHighlight;
|
||||
scale: ChartScale;
|
||||
order: ChartOrder;
|
||||
};
|
||||
type ScaleBounds = {
|
||||
min: number;
|
||||
max: number;
|
||||
minPositive: number;
|
||||
};
|
||||
type SeriesHighlight = {
|
||||
addNode(
|
||||
node: SVGPathElement | SVGCircleElement,
|
||||
index: number,
|
||||
): void;
|
||||
clearPreview(): void;
|
||||
clearNodes(): void;
|
||||
preview(index: number): void;
|
||||
};
|
||||
type StackedPoint = ChartPoint & {
|
||||
y0: number;
|
||||
y1: number;
|
||||
};
|
||||
|
||||
type TimeframeEndpoint = {
|
||||
fetch(): Promise<ChartResult>;
|
||||
last(count: number): { fetch(): Promise<ChartResult> };
|
||||
};
|
||||
type TimeframeIndex = (typeof timeframes)[TimeframeValue]["index"];
|
||||
type TimeframeMetric = {
|
||||
by: Record<TimeframeIndex, TimeframeEndpoint>;
|
||||
};
|
||||
type TimeframeValue = (typeof timeframeOptions)[number]["value"];
|
||||
}
|
||||
|
||||
export {};
|
||||
@@ -8,6 +8,3 @@ export const units = /** @type {const} */ ({
|
||||
utxos: { id: "utxos", name: "UTXOs", format: formatNumberValue },
|
||||
usd: { id: "usd", name: "US Dollars", format: formatNumberValue },
|
||||
});
|
||||
|
||||
/** @typedef {keyof typeof units} ChartUnitKey */
|
||||
/** @typedef {typeof units[ChartUnitKey]} ChartUnit */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createChartSetting } from "./setting.js";
|
||||
|
||||
const views = /** @type {const} */ ([
|
||||
export const views = /** @type {const} */ ([
|
||||
{ value: "line", label: "Line" },
|
||||
{ value: "area", label: "Area" },
|
||||
{ value: "stacked", label: "Stack" },
|
||||
@@ -38,5 +38,3 @@ export function saveView(chartKey, view) {
|
||||
export function createViewControl(currentView, onChange) {
|
||||
return setting.create(currentView, onChange);
|
||||
}
|
||||
|
||||
/** @typedef {(typeof views)[number]["value"]} ChartView */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createPathId } from "../path.js";
|
||||
|
||||
/**
|
||||
* @param {Section} section
|
||||
* @param {LearnSection} section
|
||||
* @param {readonly string[]} path
|
||||
*/
|
||||
function createContentsItem(section, path) {
|
||||
@@ -27,7 +27,7 @@ function createContentsItem(section, path) {
|
||||
return item;
|
||||
}
|
||||
|
||||
/** @param {Section[]} sections */
|
||||
/** @param {LearnSection[]} sections */
|
||||
export function createContents(sections) {
|
||||
const nav = document.createElement("nav");
|
||||
const list = document.createElement("ol");
|
||||
@@ -41,10 +41,3 @@ export function createContents(sections) {
|
||||
nav.append(list);
|
||||
return nav;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} Section
|
||||
* @property {string} title
|
||||
* @property {boolean} [numbered]
|
||||
* @property {Section[]} [children]
|
||||
*/
|
||||
|
||||
@@ -26,7 +26,7 @@ function colorAt(index) {
|
||||
return palette[index % palette.length];
|
||||
}
|
||||
|
||||
/** @param {readonly { label: string, color?: ChartColor, metric: Metric }[]} items */
|
||||
/** @param {readonly { label: string, color?: () => string, metric: ChartMetric }[]} items */
|
||||
export function createCohortSeries(items) {
|
||||
return items.map(({ label, color, metric }, index) => ({
|
||||
label,
|
||||
@@ -38,7 +38,7 @@ export function createCohortSeries(items) {
|
||||
/**
|
||||
* @template {string} Key
|
||||
* @param {readonly (readonly [string, Key])[]} items
|
||||
* @param {(key: Key) => Metric} createMetric
|
||||
* @param {(key: Key) => ChartMetric} createMetric
|
||||
*/
|
||||
export function createCohortSeriesFromKeys(items, createMetric) {
|
||||
return createCohortSeries(
|
||||
@@ -48,6 +48,3 @@ export function createCohortSeriesFromKeys(items, createMetric) {
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
/** @typedef {import("../charts/index.js").ChartSeries["color"]} ChartColor */
|
||||
/** @typedef {import("../charts/index.js").ChartSeries["metric"]} Metric */
|
||||
|
||||
@@ -24,7 +24,7 @@ function createPools(pools) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(window: WindowKey) => TimeframeMetric} createMetric
|
||||
* @param {(window: RollingWindowKey) => TimeframeMetric} createMetric
|
||||
*/
|
||||
function createWindowSeries(createMetric) {
|
||||
return createRollingWindowSeries((window) => () => createMetric(window));
|
||||
@@ -102,7 +102,5 @@ export function createMinorPoolBlocksMinedSeries(pool) {
|
||||
);
|
||||
}
|
||||
|
||||
/** @typedef {import("./rolling-windows.js").RollingWindowKey} WindowKey */
|
||||
/** @typedef {typeof brk.series.pools.major.unknown} MajorPool */
|
||||
/** @typedef {typeof brk.series.pools.minor.blockfills} MinorPool */
|
||||
/** @typedef {import("../charts/timeframes.js").TimeframeMetric} TimeframeMetric */
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { createCohortSeries } from "./cohort-series.js";
|
||||
import { colors } from "../../utils/colors.js";
|
||||
|
||||
const rollingWindows = /** @type {const} */ ([
|
||||
export const rollingWindows = /** @type {const} */ ([
|
||||
["24h", "_24h", colors.sky],
|
||||
["1w", "_1w", colors.cyan],
|
||||
["1m", "_1m", colors.blue],
|
||||
["1y", "_1y", colors.violet],
|
||||
]);
|
||||
|
||||
/** @param {(key: RollingWindowKey) => Metric} createMetric */
|
||||
/** @param {(key: RollingWindowKey) => ChartMetric} createMetric */
|
||||
export function createRollingWindowSeries(createMetric) {
|
||||
return createCohortSeries(
|
||||
rollingWindows.map(([label, key, color]) => ({
|
||||
@@ -18,6 +18,3 @@ export function createRollingWindowSeries(createMetric) {
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
/** @typedef {(typeof rollingWindows)[number][1]} RollingWindowKey */
|
||||
/** @typedef {import("./cohort-series.js").Metric} Metric */
|
||||
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
import { rollingWindows } from "./rolling-windows.js";
|
||||
|
||||
declare global {
|
||||
type RollingWindowKey = (typeof rollingWindows)[number][1];
|
||||
}
|
||||
|
||||
export {};
|
||||
@@ -1,12 +1,12 @@
|
||||
import { createContents } from "./contents/index.js";
|
||||
import { sections } from "./data/index.js";
|
||||
import { createChart as createDataChart } from "./charts/index.js";
|
||||
import { createChart } from "./charts/index.js";
|
||||
import { initHashLinks } from "./hash-links.js";
|
||||
import { initScrollSpy } from "./scroll-spy.js";
|
||||
import { createPathId } from "./path.js";
|
||||
|
||||
/**
|
||||
* @param {Section} section
|
||||
* @param {LearnSection} section
|
||||
* @param {readonly string[]} [path]
|
||||
*/
|
||||
function createSection(section, path = []) {
|
||||
@@ -26,7 +26,7 @@ function createSection(section, path = []) {
|
||||
heading.append(anchor);
|
||||
description.append(section.description);
|
||||
element.append(heading, description);
|
||||
if (section.chart) element.append(createDataChart(section.chart, id));
|
||||
if (section.chart) element.append(createChart(section.chart, id));
|
||||
|
||||
for (const child of children) {
|
||||
element.append(createSection(child, sectionPath));
|
||||
@@ -49,12 +49,3 @@ export function createLearnPage() {
|
||||
initScrollSpy(main);
|
||||
return main;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} Section
|
||||
* @property {string} title
|
||||
* @property {string} description
|
||||
* @property {import("./charts/index.js").Chart} [chart]
|
||||
* @property {boolean} [numbered]
|
||||
* @property {Section[]} [children]
|
||||
*/
|
||||
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
declare global {
|
||||
type LearnSection = {
|
||||
title: string;
|
||||
description: string;
|
||||
chart?: Chart;
|
||||
numbered?: boolean;
|
||||
children?: LearnSection[];
|
||||
};
|
||||
}
|
||||
|
||||
export {};
|
||||
Reference in New Issue
Block a user