mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-09 06:31:57 -07:00
171 lines
4.4 KiB
JavaScript
171 lines
4.4 KiB
JavaScript
import { createSelect } from "../../../scripts/utils/dom.js";
|
|
import { GENESIS_DATE, todayISODate, toISODate } from "../time.js";
|
|
import { createHeatmapPersistedValue, findChoiceByKey } from "./shared.js";
|
|
|
|
/**
|
|
* @typedef {Object} RangeChoice
|
|
* @property {string} label
|
|
* @property {string} date
|
|
*/
|
|
|
|
/**
|
|
* @param {HeatmapOption} option
|
|
* @param {(range: { from: string, to: string }) => void} onChange
|
|
*/
|
|
export function createDateControls(option, onChange) {
|
|
const currentYear = new Date().getUTCFullYear();
|
|
const fromChoices = createFromChoices(currentYear);
|
|
const toChoices = createToChoices(currentYear);
|
|
const fallbackFromChoice = fromChoices.at(-1) ?? fromChoices[0];
|
|
const fallbackToChoice = toChoices[0];
|
|
const defaultFromChoice = findChoiceByKey(
|
|
fromChoices,
|
|
option.defaults?.from ?? "",
|
|
fallbackFromChoice,
|
|
rangeChoiceLabel,
|
|
);
|
|
const defaultToChoice = findChoiceByKey(
|
|
toChoices,
|
|
option.defaults?.to ?? "",
|
|
fallbackToChoice,
|
|
rangeChoiceLabel,
|
|
);
|
|
|
|
const persistedFrom = createHeatmapPersistedValue(
|
|
option,
|
|
"from",
|
|
"hm_from",
|
|
rangeChoiceLabel(defaultFromChoice),
|
|
);
|
|
const persistedTo = createHeatmapPersistedValue(
|
|
option,
|
|
"to",
|
|
"hm_to",
|
|
rangeChoiceLabel(defaultToChoice),
|
|
);
|
|
|
|
let fromChoice = findChoiceByKey(
|
|
fromChoices,
|
|
persistedFrom.value,
|
|
defaultFromChoice,
|
|
rangeChoiceLabel,
|
|
);
|
|
let toChoice = findChoiceByKey(
|
|
toChoices,
|
|
persistedTo.value,
|
|
defaultToChoice,
|
|
rangeChoiceLabel,
|
|
);
|
|
|
|
if (fromChoice.date > toChoice.date) {
|
|
toChoice = findSameLabelChoice(toChoices, fromChoice, defaultToChoice);
|
|
}
|
|
persistDateChoices();
|
|
|
|
const fromSelect = createSelect({
|
|
id: "heatmap-from",
|
|
label: "from",
|
|
choices: fromChoices,
|
|
initialValue: fromChoice,
|
|
onChange(choice) {
|
|
fromChoice = choice;
|
|
if (fromChoice.date > toChoice.date) {
|
|
toChoice = findSameLabelChoice(toChoices, fromChoice, defaultToChoice);
|
|
toSelect.set(toChoice);
|
|
}
|
|
persistDateChoices();
|
|
onChange({ from: fromChoice.date, to: toChoice.date });
|
|
},
|
|
toKey: rangeChoiceLabel,
|
|
toLabel: rangeChoiceLabel,
|
|
});
|
|
const toSelect = createSelect({
|
|
id: "heatmap-to",
|
|
label: "to",
|
|
choices: toChoices,
|
|
initialValue: toChoice,
|
|
onChange(choice) {
|
|
toChoice = choice;
|
|
if (fromChoice.date > toChoice.date) {
|
|
fromChoice = findSameLabelChoice(
|
|
fromChoices,
|
|
toChoice,
|
|
defaultFromChoice,
|
|
);
|
|
fromSelect.set(fromChoice);
|
|
}
|
|
persistDateChoices();
|
|
onChange({ from: fromChoice.date, to: toChoice.date });
|
|
},
|
|
toKey: rangeChoiceLabel,
|
|
toLabel: rangeChoiceLabel,
|
|
});
|
|
|
|
return {
|
|
elements: [fromSelect.element, toSelect.element],
|
|
from: fromChoice.date,
|
|
to: toChoice.date,
|
|
};
|
|
|
|
function persistDateChoices() {
|
|
persistedFrom.setImmediate(rangeChoiceLabel(fromChoice));
|
|
persistedTo.setImmediate(rangeChoiceLabel(toChoice));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {number} currentYear
|
|
* @returns {RangeChoice[]}
|
|
*/
|
|
function createFromChoices(currentYear) {
|
|
const choices = [{ label: "genesis", date: GENESIS_DATE }];
|
|
for (let year = 2009; year <= currentYear; year++) {
|
|
choices.push({
|
|
label: String(year),
|
|
date: year === 2009 ? GENESIS_DATE : yearStartISODate(year),
|
|
});
|
|
}
|
|
return choices;
|
|
}
|
|
|
|
/**
|
|
* @param {number} currentYear
|
|
* @returns {RangeChoice[]}
|
|
*/
|
|
function createToChoices(currentYear) {
|
|
const today = todayISODate();
|
|
const todayTime = Date.parse(`${today}T00:00:00Z`);
|
|
const choices = [{ label: "today", date: today }];
|
|
for (let year = currentYear; year >= 2009; year--) {
|
|
choices.push({ label: String(year), date: yearEndISODate(year, todayTime) });
|
|
}
|
|
return choices;
|
|
}
|
|
|
|
/** @param {RangeChoice} choice */
|
|
function rangeChoiceLabel(choice) {
|
|
return choice.label;
|
|
}
|
|
|
|
/**
|
|
* @param {readonly RangeChoice[]} choices
|
|
* @param {RangeChoice} choice
|
|
* @param {RangeChoice} fallback
|
|
*/
|
|
function findSameLabelChoice(choices, choice, fallback) {
|
|
return choices.find((candidate) => candidate.label === choice.label) ?? fallback;
|
|
}
|
|
|
|
/** @param {number} year */
|
|
function yearStartISODate(year) {
|
|
return toISODate(new Date(Date.UTC(year, 0, 1)));
|
|
}
|
|
|
|
/**
|
|
* @param {number} year
|
|
* @param {number} todayTime
|
|
*/
|
|
function yearEndISODate(year, todayTime) {
|
|
return toISODate(new Date(Math.min(Date.UTC(year, 11, 31), todayTime)));
|
|
}
|