diff --git a/CHANGELOG.md b/CHANGELOG.md index 76e70d914..63b4be485 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,8 @@ - Fixed history not being properly registered - Fixed window being moveable on iOS when in standalone mode when it shouldn't be - Split `index.html` and `script.js` into multiple js and css files to load only what's necessary at a given time +- Added `Compare` section to all groups, to compare all datasets within a group +- Updated `Solid Signals` library, which had an important breaking change on the `createEffect` function which might bring some bugs ## Parser diff --git a/website/index.html b/website/index.html index 77b142a42..ca34bf7a2 100644 --- a/website/index.html +++ b/website/index.html @@ -971,7 +971,7 @@ } details:not([open]) > summary:hover > & { - background-color: var(--orange); + background-color: var(--orange) !important; } } diff --git a/website/scripts/chart.js b/website/scripts/chart.js index 13336efb5..6eabf9014 100644 --- a/website/scripts/chart.js +++ b/website/scripts/chart.js @@ -1,5 +1,6 @@ /** - * @import {Options} from './options'; + * @import { HoveredLegend, PriceSeriesType, Series } from "./types/self" + * @import { Options } from './options'; */ /** @@ -205,22 +206,21 @@ export function init({ const debouncedSaveVisibleRange = utils.debounce(saveVisibleRange, 250); function createFetchChunksOfVisibleDatasetsEffect() { - signals.createEffect(() => { - const ids = visibleDatasetIds(); - const datasets = Array.from(activeDatasets()); + signals.createEffect( + () => ({ ids: visibleDatasetIds(), activeDatasets: activeDatasets() }), + ({ ids, activeDatasets }) => { + const datasets = Array.from(activeDatasets); - if (ids.length === 0 || datasets.length === 0) return; + if (ids.length === 0 || datasets.length === 0) return; - signals.untrack(() => { - console.log(ids, datasets); for (let i = 0; i < ids.length; i++) { const id = ids[i]; for (let j = 0; j < datasets.length; j++) { datasets[j].fetch(id); } } - }); - }); + }, + ); } createFetchChunksOfVisibleDatasetsEffect(); @@ -352,9 +352,7 @@ export function init({ ); const hoveredLegend = signals.createSignal( - /** @type {{label: HTMLLabelElement, series: Series} | undefined} */ ( - undefined - ), + /** @type {HoveredLegend | undefined} */ (undefined), ); const notHoveredLegendTransparency = "66"; /** @@ -365,9 +363,13 @@ export function init({ */ function createLegend({ series, disabled, name }) { const div = window.document.createElement("div"); - signals.createEffect(() => { - div.hidden = disabled?.() ? true : false; - }); + + if (disabled) { + signals.createEffect(disabled, (disabled) => { + div.hidden = disabled; + }); + } + elements.legend.prepend(div); const { input, label, spanMain } = utils.dom.createComplexLabeledInput({ @@ -395,8 +397,8 @@ export function init({ hoveredLegend.set(undefined); }); - signals.createEffect(() => { - input.checked = series.active(); + signals.createEffect(series.active, (checked) => { + input.checked = checked; }); function shouldHighlight() { @@ -416,69 +418,74 @@ export function init({ const spanColor = window.document.createElement("span"); spanColors.append(spanColor); - signals.createEffect(() => { - const c = color(); - if (shouldHighlight()) { - spanColor.style.backgroundColor = c; - } else { - spanColor.style.backgroundColor = `${c}${notHoveredLegendTransparency}`; - } - }); + signals.createEffect( + () => ({ color: color(), shouldHighlight: shouldHighlight() }), + ({ color, shouldHighlight }) => { + if (shouldHighlight) { + spanColor.style.backgroundColor = color; + } else { + spanColor.style.backgroundColor = `${color}${notHoveredLegendTransparency}`; + } + }, + ); }); function createHoverEffect() { const initialColors = /** @type {Record} */ ({}); const darkenedColors = /** @type {Record} */ ({}); + /** @type {HoveredLegend | undefined} */ + let previouslyHovered = undefined; + signals.createEffect( - // @ts-ignore - (previouslyHovered) => { - const hovered = hoveredLegend(); - + () => ({ hovered: hoveredLegend(), ids: visibleDatasetIds() }), + ({ hovered, ids }) => { if (!hovered && !previouslyHovered) return hovered; - const ids = visibleDatasetIds(); - for (let i = 0; i < ids.length; i++) { const chunkId = ids[i]; const chunkIndex = utils.chunkIdToIndex(scale(), chunkId); - const chunk = series.chunks[chunkIndex]?.(); + const chunk = series.chunks[chunkIndex]; - if (!chunk) return; + signals.createEffect(chunk, (chunk) => { + if (!chunk) return; - if (hovered) { - const seriesOptions = chunk.options(); - if (!seriesOptions) return; + if (hovered) { + const seriesOptions = chunk.options(); + if (!seriesOptions) return; - initialColors[i] = {}; - darkenedColors[i] = {}; + initialColors[i] = {}; + darkenedColors[i] = {}; - Object.entries(seriesOptions).forEach(([k, v]) => { - if (k.toLowerCase().includes("color") && v) { - if (typeof v === "string" && !v.startsWith("#")) { - return; + Object.entries(seriesOptions).forEach(([k, v]) => { + if (k.toLowerCase().includes("color") && v) { + if (typeof v === "string" && !v.startsWith("#")) { + return; + } + + v = /** @type {string} */ (v).substring(0, 7); + initialColors[i][k] = v; + darkenedColors[i][k] = + `${v}${notHoveredLegendTransparency}`; + } else if (k === "lastValueVisible" && v) { + initialColors[i][k] = true; + darkenedColors[i][k] = false; } + }); + } - v = /** @type {string} */ (v).substring(0, 7); - initialColors[i][k] = v; - darkenedColors[i][k] = `${v}${notHoveredLegendTransparency}`; - } else if (k === "lastValueVisible" && v) { - initialColors[i][k] = true; - darkenedColors[i][k] = false; + signals.createEffect(shouldHighlight, (shouldHighlight) => { + if (shouldHighlight) { + chunk.applyOptions(initialColors[i]); + } else { + chunk.applyOptions(darkenedColors[i]); } }); - } - - if (shouldHighlight()) { - chunk.applyOptions(initialColors[i]); - } else { - chunk.applyOptions(darkenedColors[i]); - } + }); } - return hovered; + previouslyHovered = hovered; }, - undefined, ); } createHoverEffect(); @@ -540,21 +547,22 @@ export function init({ const visible = signals.createMemo(() => active() && !disabled()); - signals.createEffect(() => { - if (disabled()) { - return; - } + signals.createEffect( + () => ({ disabled: disabled(), active: active() }), + ({ disabled, active }) => { + if (disabled) { + return; + } - const a = active(); - - if (a !== (defaultActive || true)) { - utils.url.writeParam(id, a); - utils.storage.write(storageId, a); - } else { - utils.url.removeParam(id); - utils.storage.remove(storageId); - } - }); + if (active !== (defaultActive || true)) { + utils.url.writeParam(id, active); + utils.storage.write(storageId, active); + } else { + utils.url.removeParam(id); + utils.storage.remove(storageId); + } + }, + ); /** @type {Series} */ const series = { @@ -579,22 +587,22 @@ export function init({ chunks[index] = chunk; - signals.createEffect(() => { - const values = json.vec(); + const isMyTurn = signals.createMemo(() => { + if (seriesIndex <= 0) return true; - if (!values) return; + const previousSeriesChunk = chartSeries.at(seriesIndex - 1)?.chunks[ + index + ]; + const isPreviousSeriesOnChart = previousSeriesChunk?.(); - if (seriesIndex > 0) { - const previousSeriesChunk = chartSeries.at(seriesIndex - 1)?.chunks[ - index - ]; - const isPreviousSeriesOnChart = previousSeriesChunk?.(); - if (!isPreviousSeriesOnChart) { - return; - } - } + return !!isPreviousSeriesOnChart; + }); + + signals.createEffect( + () => ({ values: json.vec(), isMyTurn: isMyTurn() }), + ({ values, isMyTurn }) => { + if (!values || !isMyTurn) return; - signals.untrack(() => { let s = chunk(); if (!s) { @@ -647,28 +655,39 @@ export function init({ s.setData(values); setMinMaxMarkersWhenIdle(); + }, + ); + + signals.createEffect( + () => ({ + chunk: chunk(), + currentVec: dataset.fetchedJSONs.at(index)?.vec(), + nextVec: dataset.fetchedJSONs.at(index + 1)?.vec(), + }), + ({ chunk, currentVec, nextVec }) => { + if (chunk && currentVec?.length && nextVec?.length) { + chunk.update(nextVec[0]); + } + }, + ); + + signals.createEffect(chunk, (chunk) => { + const isChunkLastVisible = signals.createMemo(() => { + const last = lastVisibleDatasetIndex(); + return last !== undefined && last === index; }); - }); - signals.createEffect(() => { - const _chunk = chunk(); - const currentVec = dataset.fetchedJSONs.at(index)?.vec(); - const nextVec = dataset.fetchedJSONs.at(index + 1)?.vec(); - - if (_chunk && currentVec?.length && nextVec?.length) { - _chunk.update(nextVec[0]); - } - }); - - const isChunkLastVisible = signals.createMemo(() => { - const last = lastVisibleDatasetIndex(); - return last !== undefined && last === index; - }); - - signals.createEffect(() => { - chunk()?.applyOptions({ - lastValueVisible: series.visible() && isChunkLastVisible(), - }); + signals.createEffect( + () => ({ + visible: series.visible(), + isChunkLastVisible: isChunkLastVisible(), + }), + ({ visible, isChunkLastVisible }) => { + chunk?.applyOptions({ + lastValueVisible: visible && isChunkLastVisible, + }); + }, + ); }); const shouldChunkBeVisible = signals.createMemo(() => { @@ -700,10 +719,17 @@ export function init({ return wasChunkVisible; }); - signals.createEffect(() => { - const visible = series.visible() && chunkVisible(); - chunk()?.applyOptions({ - visible, + signals.createEffect(chunk, (chunk) => { + if (!chunk) return; + + const visible = signals.createMemo( + () => series.visible() && chunkVisible(), + ); + + signals.createEffect(visible, (visible) => { + chunk.applyOptions({ + visible, + }); }); }); }); @@ -774,16 +800,18 @@ export function init({ }); function createLiveCandleUpdateEffect() { - signals.createEffect(() => { - const latest = webSockets.krakenCandle.latest(); - + signals.createEffect(webSockets.krakenCandle.latest, (latest) => { if (!latest) return; const index = utils.chunkIdToIndex(s, latest.year); - const series = priceSeries.chunks.at(index)?.(); + const series = priceSeries.chunks.at(index); - series?.update(latest); + if (series) { + signals.createEffect(series, (series) => { + series?.update(latest); + }); + } }); } createLiveCandleUpdateEffect(); @@ -870,10 +898,10 @@ export function init({ initGoToButtons(elements.timeScaleHeightButtons); function createScaleButtonsToggleEffect() { - signals.createEffect(() => { - const scaleIsDate = scale() === "date"; - elements.timeScaleDateButtons.hidden = !scaleIsDate; - elements.timeScaleHeightButtons.hidden = scaleIsDate; + const isDate = signals.createMemo(() => scale() === "date"); + signals.createEffect(isDate, (isDate) => { + elements.timeScaleDateButtons.hidden = !isDate; + elements.timeScaleHeightButtons.hidden = isDate; }); } createScaleButtonsToggleEffect(); @@ -1065,11 +1093,10 @@ export function init({ ); function createSetMinMaxMarkersWhenIdleEffect() { - signals.createEffect(() => { - visibleTimeRange(); - dark(); - signals.untrack(setMinMaxMarkersWhenIdle); - }); + signals.createEffect( + () => [visibleTimeRange(), dark()], + setMinMaxMarkersWhenIdle, + ); } createSetMinMaxMarkersWhenIdleEffect(); @@ -1097,12 +1124,12 @@ export function init({ const priceLineSeries = _createPriceSeries("Line"); function createLinkPriceSeriesEffect() { - signals.createEffect(() => { - priceCandlestickSeries.active.set(priceLineSeries.active()); + signals.createEffect(priceLineSeries.active, (active) => { + priceCandlestickSeries.active.set(active); }); - signals.createEffect(() => { - priceLineSeries.active.set(priceCandlestickSeries.active()); + signals.createEffect(priceCandlestickSeries.active, (active) => { + priceLineSeries.active.set(active); }); } createLinkPriceSeriesEffect(); @@ -1142,9 +1169,8 @@ export function init({ chartSeries.forEach((series) => { allSeries.unshift(series); - signals.createEffect(() => { - series.active(); - signals.untrack(setMinMaxMarkersWhenIdle); + signals.createEffect(series.active, () => { + setMinMaxMarkersWhenIdle(); }); }); @@ -1153,17 +1179,17 @@ export function init({ ); function createChartVisibilityEffect() { - signals.createEffect(() => { + signals.createEffect(chartVisible, (chartVisible) => { const chartWrapper = chartDiv.parentElement; if (!chartWrapper) throw "Should exist"; - chartWrapper.hidden = !chartVisible(); + chartWrapper.hidden = !chartVisible; }); } createChartVisibilityEffect(); function createTimeScaleVisibilityEffect() { - signals.createEffect(() => { - const visible = chartIndex === chartCount - 1 && chartVisible(); + signals.createEffect(chartVisible, (chartVisible) => { + const visible = chartIndex === chartCount - 1 && chartVisible; chart.timeScale().applyOptions({ visible, @@ -1178,9 +1204,9 @@ export function init({ } createTimeScaleVisibilityEffect(); - signals.createEffect(() => + signals.createEffect(chartMode, (chartMode) => chart.priceScale("right").applyOptions({ - mode: chartMode() === "linear" ? 0 : 1, + mode: chartMode === "linear" ? 0 : 1, }), ); @@ -1235,9 +1261,8 @@ export function init({ } function createApplyChartOptionEffect() { - signals.createEffect(() => { - const option = selected(); - signals.createUntrackedRoot(() => { + signals.createEffect(selected, (option) => { + signals.createRoot(() => { applyChartOption(option); }); }); diff --git a/website/scripts/main.js b/website/scripts/main.js index 77fcb2e4b..eb643373d 100644 --- a/website/scripts/main.js +++ b/website/scripts/main.js @@ -1,19 +1,19 @@ // @ts-check /** - * @import { OptionPath, PartialOption, PartialOptionsGroup, PartialOptionsTree, Option, OptionsGroup, Series, PriceSeriesType, ResourceDataset, TimeScale, SerializedHistory, TimeRange, Unit, Marker, Weighted, DatasetPath, OHLC, FetchedJSON, DatasetValue, FetchedResult, AnyDatasetPath, SeriesBlueprint, BaselineSpecificSeriesBlueprint, CandlestickSpecificSeriesBlueprint, LineSpecificSeriesBlueprint, SpecificSeriesBlueprintWithChart, Signal, Color, SettingsTheme, DatasetCandlestickData, FoldersFilter, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, AnyPath, SimulationOption } from "./types/self" + * @import { Option, ResourceDataset, TimeScale, SerializedHistory, TimeRange, Unit, Marker, Weighted, DatasetPath, OHLC, FetchedJSON, DatasetValue, FetchedResult, AnyDatasetPath, SeriesBlueprint, BaselineSpecificSeriesBlueprint, CandlestickSpecificSeriesBlueprint, LineSpecificSeriesBlueprint, SpecificSeriesBlueprintWithChart, Signal, Color, SettingsTheme, DatasetCandlestickData, FoldersFilter, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, AnyPath, SimulationOption } from "./types/self" * @import {createChart as CreateClassicChart, createChartEx as CreateCustomChart, LineStyleOptions} from "./packages/lightweight-charts/v4.2.0/types"; * @import * as _ from "./packages/ufuzzy/v1.0.14/types" * @import { DeepPartial, ChartOptions, IChartApi, IHorzScaleBehavior, WhitespaceData, SingleValueData, ISeriesApi, Time, LogicalRange, SeriesMarker, CandlestickData, SeriesType, BaselineStyleOptions, SeriesOptionsCommon } from "./packages/lightweight-charts/v4.2.0/types" * @import { DatePath, HeightPath, LastPath } from "./types/paths"; - * @import { SignalOptions, untrack as Untrack } from "./packages/solid-signals/2024-04-17/types/core" - * @import { getOwner as GetOwner, onCleanup as OnCleanup, Owner } from "./packages/solid-signals/2024-04-17/types/owner" - * @import { createSignal as CreateSignal, createEffect as CreateEffect, Accessor, Setter, createMemo as CreateMemo, createRoot as CreateRoot, runWithOwner as RunWithOwner } from "./packages/solid-signals/2024-04-17/types/signals"; + * @import { SignalOptions } from "./packages/solid-signals/2024-10-28/types/core" + * @import { getOwner as GetOwner, onCleanup as OnCleanup, Owner } from "./packages/solid-signals/2024-10-28/types/owner" + * @import { createSignal as CreateSignal, createEffect as CreateEffect, Accessor, Setter, createMemo as CreateMemo, createRoot as CreateRoot, runWithOwner as RunWithOwner } from "./packages/solid-signals/2024-10-28/types/signals"; */ function initPackages() { async function importSignals() { - return import("./packages/solid-signals/2024-04-17/script.js").then( + return import("./packages/solid-signals/2024-10-28/script.js").then( (_signals) => { const signals = { createSolidSignal: /** @type {CreateSignal} */ ( @@ -22,7 +22,6 @@ function initPackages() { createEffect: /** @type {CreateEffect} */ (_signals.createEffect), createMemo: /** @type {CreateMemo} */ (_signals.createMemo), createRoot: /** @type {CreateRoot} */ (_signals.createRoot), - untrack: /** @type {Untrack} */ (_signals.untrack), getOwner: /** @type {GetOwner} */ (_signals.getOwner), runWithOwner: /** @type {RunWithOwner} */ (_signals.runWithOwner), onCleanup: /** @type {OnCleanup} */ (_signals.onCleanup), @@ -34,7 +33,10 @@ function initPackages() { * @returns {Signal} */ createSignal(initialValue, options) { - const [get, set] = this.createSolidSignal(initialValue, options); + const [get, set] = this.createSolidSignal( + /** @type {any} */ (initialValue), + options, + ); // @ts-ignore get.set = set; @@ -54,9 +56,7 @@ function initPackages() { } let firstEffect = true; - this.createEffect(() => { - const value = get(); - + this.createEffect(get, (value) => { if (!save) return; if (!firstEffect && save.id) { @@ -82,13 +82,6 @@ function initPackages() { // @ts-ignore return get; }, - /** - * @param {(dispose: VoidFunction) => void} callback - */ - createUntrackedRoot: (callback) => - signals.untrack(() => { - signals.createRoot(callback); - }), }; return signals; @@ -285,34 +278,35 @@ function initPackages() { minimumWidth: 78, }); - signals.createEffect(() => { - const { default: _defaultColor, off: _offColor } = colors; - - const defaultColor = _defaultColor(); - const offColor = _offColor(); - - chart.applyOptions({ - layout: { - textColor: offColor, - }, - rightPriceScale: { - borderVisible: false, - }, - timeScale: { - borderVisible: false, - }, - crosshair: { - horzLine: { - color: defaultColor, - labelBackgroundColor: defaultColor, + signals.createEffect( + () => ({ + defaultColor: colors.default(), + offColor: colors.off(), + }), + ({ defaultColor, offColor }) => { + chart.applyOptions({ + layout: { + textColor: offColor, }, - vertLine: { - color: defaultColor, - labelBackgroundColor: defaultColor, + rightPriceScale: { + borderVisible: false, }, - }, - }); - }); + timeScale: { + borderVisible: false, + }, + crosshair: { + horzLine: { + color: defaultColor, + labelBackgroundColor: defaultColor, + }, + vertLine: { + color: defaultColor, + labelBackgroundColor: defaultColor, + }, + }, + }); + }, + ); return chart; } @@ -366,8 +360,8 @@ function initPackages() { const series = chart.addBaselineSeries(seriesOptions); signals.runWithOwner(owner, () => { - signals.createEffect(() => { - series.applyOptions(computeColors()); + signals.createEffect(computeColors, (computeColors) => { + series.applyOptions(computeColors); }); }); @@ -409,8 +403,8 @@ function initPackages() { }); signals.runWithOwner(owner, () => { - signals.createEffect(() => { - candlestickSeries.applyOptions(computeColors()); + signals.createEffect(computeColors, (computeColors) => { + candlestickSeries.applyOptions(computeColors); }); }); @@ -441,8 +435,8 @@ function initPackages() { }); signals.runWithOwner(owner, () => { - signals.createEffect(() => { - series.applyOptions(computeColors()); + signals.createEffect(computeColors, (computeColors) => { + series.applyOptions(computeColors); }); }); @@ -909,8 +903,8 @@ const utils = { if (typeof title === "string") { legend.innerHTML = title; } else { - signals.createEffect(() => { - legend.innerHTML = title(); + signals.createEffect(title, (title) => { + legend.innerHTML = title; }); } field.append(legend); @@ -2182,9 +2176,7 @@ function initWebSockets(signals) { krakenCandle.open(); function createDocumentTitleEffect() { - signals.createEffect(() => { - const latest = krakenCandle.latest(); - + signals.createEffect(krakenCandle.latest, (latest) => { if (latest) { const close = latest.close; console.log("close:", close); @@ -2231,13 +2223,13 @@ packages.signals().then((signals) => ); function createFetchLastValuesWhenNeededEffect() { let previousHeight = -1; - signals.createEffect(() => { - if (previousHeight !== lastHeight()) { + signals.createEffect(lastHeight, (lastHeight) => { + if (previousHeight !== lastHeight) { fetch("/api/last").then((response) => { response.json().then((json) => { if (typeof json === "object") { lastValues.set(json); - previousHeight = lastHeight(); + previousHeight = lastHeight; } }); }); @@ -2282,112 +2274,108 @@ packages.signals().then((signals) => let firstChartOption = true; let firstSimulationOption = true; - signals.createEffect(() => { - const option = options.selected(); + signals.createEffect(options.selected, (option) => { + if (previousElement) { + previousElement.hidden = true; + utils.url.resetParams(option); + utils.url.pushHistory(option.id); + } else { + utils.url.replaceHistory({ pathname: option.id }); + } - signals.untrack(() => { - if (previousElement) { - previousElement.hidden = true; - utils.url.resetParams(option); - utils.url.pushHistory(option.id); - } else { - utils.url.replaceHistory({ pathname: option.id }); + const hideTop = option.kind === "home" || option.kind === "pdf"; + elements.selectedHeader.hidden = hideTop; + elements.selectedHr.hidden = hideTop; + + elements.selectedTitle.innerHTML = option.title; + elements.selectedDescription.innerHTML = option.serializedPath; + + /** @type {HTMLElement} */ + let element; + + switch (option.kind) { + case "home": { + element = elements.home; + break; } + case "chart": { + element = elements.charts; - const hideTop = option.kind === "home" || option.kind === "pdf"; - elements.selectedHeader.hidden = hideTop; - elements.selectedHr.hidden = hideTop; + lastChartOption.set(option); - elements.selectedTitle.innerHTML = option.title; - elements.selectedDescription.innerHTML = option.serializedPath; - - /** @type {HTMLElement} */ - let element; - - switch (option.kind) { - case "home": { - element = elements.home; - break; - } - case "chart": { - element = elements.charts; - - lastChartOption.set(option); - - if (firstChartOption) { - const lightweightCharts = packages.lightweightCharts(); - const chartScript = import("./chart.js"); - utils.dom.importStyleAndThen("/styles/chart.css", () => - chartScript.then(({ init: initChartsElement }) => - lightweightCharts.then((lightweightCharts) => - signals.runWithOwner(owner, () => - initChartsElement({ - colors, - consts, - dark, - datasets, - elements, - ids, - lightweightCharts, - options, - selected: /** @type {any} */ (lastChartOption), - signals, - utils, - webSockets, - }), - ), + if (firstChartOption) { + const lightweightCharts = packages.lightweightCharts(); + const chartScript = import("./chart.js"); + utils.dom.importStyleAndThen("/styles/chart.css", () => + chartScript.then(({ init: initChartsElement }) => + lightweightCharts.then((lightweightCharts) => + signals.runWithOwner(owner, () => + initChartsElement({ + colors, + consts, + dark, + datasets, + elements, + ids, + lightweightCharts, + options, + selected: /** @type {any} */ (lastChartOption), + signals, + utils, + webSockets, + }), ), ), - ); - } - firstChartOption = false; - - break; + ), + ); } - case "simulation": { - element = elements.simulation; + firstChartOption = false; - lastSimulationOption.set(option); + break; + } + case "simulation": { + element = elements.simulation; - if (firstSimulationOption) { - const lightweightCharts = packages.lightweightCharts(); - const simulationScript = import("./simulation.js"); + lastSimulationOption.set(option); - utils.dom.importStyleAndThen("/styles/simulation.css", () => - simulationScript.then(({ init }) => - lightweightCharts.then((lightweightCharts) => - signals.runWithOwner(owner, () => - init({ - colors, - consts, - dark, - datasets, - elements, - ids, - lightweightCharts, - options, - selected: /** @type {any} */ (lastChartOption), - signals, - utils, - webSockets, - }), - ), + if (firstSimulationOption) { + const lightweightCharts = packages.lightweightCharts(); + const simulationScript = import("./simulation.js"); + + utils.dom.importStyleAndThen("/styles/simulation.css", () => + simulationScript.then(({ init }) => + lightweightCharts.then((lightweightCharts) => + signals.runWithOwner(owner, () => + init({ + colors, + consts, + dark, + datasets, + elements, + ids, + lightweightCharts, + options, + selected: /** @type {any} */ (lastChartOption), + signals, + utils, + webSockets, + }), ), ), - ); - } - firstSimulationOption = false; + ), + ); + } + firstSimulationOption = false; - break; - } - default: { - return; - } + break; } + default: { + return; + } + } - element.hidden = false; - previousElement = element; - }); + element.hidden = false; + previousElement = element; }); } createApplyOptionEffect(); @@ -2410,13 +2398,16 @@ packages.signals().then((signals) => }); }); - signals.createEffect(() => { - if (options.selected().isFavorite()) { - elements.buttonFavorite.dataset.highlight = ""; - } else { - delete elements.buttonFavorite.dataset.highlight; - } - }); + signals.createEffect( + () => options.selected().isFavorite(), + (isFavorite) => { + if (isFavorite) { + elements.buttonFavorite.dataset.highlight = ""; + } else { + delete elements.buttonFavorite.dataset.highlight; + } + }, + ); } initFavoriteButton(); @@ -2467,9 +2458,7 @@ packages.signals().then((signals) => function createMobileSwitchEffect() { let firstRun = true; - signals.createEffect(() => { - options.selected(); - + signals.createEffect(options.selected, () => { if (!firstRun && !utils.dom.isHidden(elements.selectedLabel)) { elements.selectedLabel.click(); } @@ -2496,16 +2485,14 @@ packages.signals().then((signals) => elements.foldersFilterAllCount.innerHTML = options.list.length.toLocaleString(); - signals.createEffect(() => { - elements.foldersFilterFavoritesCount.innerHTML = options.counters - .favorites() - .toLocaleString(); + signals.createEffect(options.counters.favorites, (counterFavorites) => { + elements.foldersFilterFavoritesCount.innerHTML = + counterFavorites.toLocaleString(); }); - signals.createEffect(() => { - elements.foldersFilterNewCount.innerHTML = options.counters - .new() - .toLocaleString(); + signals.createEffect(options.counters.new, (counterNew) => { + elements.foldersFilterNewCount.innerHTML = + counterNew.toLocaleString(); }); } @@ -2530,8 +2517,7 @@ packages.signals().then((signals) => options.filter.set("new"); }); - signals.createEffect(() => { - const f = options.filter(); + signals.createEffect(options.filter, (f) => { localStorage.setItem(ids.foldersFilter, f); switch (f) { case "all": { @@ -2866,9 +2852,7 @@ packages.signals().then((signals) => } function createUnshiftHistoryEffect() { - signals.createEffect(() => { - const option = options.selected(); - + signals.createEffect(options.selected, (option) => { const head = history.at(0); if ( head && @@ -2967,8 +2951,7 @@ packages.signals().then((signals) => initHistoryListInDom(); function createUpdateHistoryEffect() { - signals.createEffect(() => { - const option = options.selected(); + signals.createEffect(options.selected, (option) => { const date = new Date(); const testedString = dateToTestedString(date); @@ -3088,11 +3071,11 @@ packages.signals().then((signals) => } function createUpdateDataThemeEffect() { - signals.createEffect(() => { - localStorage.setItem(settingsThemeLocalStorageKey, theme()); + signals.createEffect(theme, (theme) => { + localStorage.setItem(settingsThemeLocalStorageKey, theme); updateTheme( - theme() === "dark" || - (theme() === "system" && + theme === "dark" || + (theme === "system" && preferredColorSchemeMatchMedia.matches), ); }); diff --git a/website/scripts/options.js b/website/scripts/options.js index 4b816d90f..2bb5ca8d8 100644 --- a/website/scripts/options.js +++ b/website/scripts/options.js @@ -1,495 +1,514 @@ +/** + * @import { AnySpecificSeriesBlueprint, CohortOption, CohortOptions, Color, DefaultCohortOption, DefaultCohortOptions, OptionPath, OptionsGroup, PartialChartOption, PartialOptionsGroup, PartialOptionsTree, Series, SeriesBlueprint, SeriesBlueprintParam, SeriesBluePrintType, TimeScale } from "./types/self" + */ + const DATE_TO_PREFIX = "date-to-"; const HEIGHT_TO_PREFIX = "height-to-"; +function initGroups() { + const xTermHolders = /** @type {const} */ ([ + { + id: "sth", + key: "sth", + name: "Short Term Holders", + legend: "Short Term Holders - STH", + }, + { + id: "lth", + key: "lth", + name: "Long Term Holders", + legend: "Long Term Holders - LTH", + }, + ]); + + const upTo = /** @type {const} */ ([ + { + id: "up-to-1d", + key: "up_to_1d", + name: "Up To 1 Day", + legend: "1D", + }, + { + id: "up-to-1w", + key: "up_to_1w", + name: "Up To 1 Week", + legend: "1W", + }, + { + id: "up-to-1m", + key: "up_to_1m", + name: "Up To 1 Month", + legend: "1M", + }, + { + id: "up-to-2m", + key: "up_to_2m", + name: "Up To 2 Months", + legend: "2M", + }, + { + id: "up-to-3m", + key: "up_to_3m", + name: "Up To 3 Months", + legend: "3M", + }, + { + id: "up-to-4m", + key: "up_to_4m", + name: "Up To 4 Months", + legend: "4M", + }, + { + id: "up-to-5m", + key: "up_to_5m", + name: "Up To 5 Months", + legend: "5M", + }, + { + id: "up-to-6m", + key: "up_to_6m", + name: "Up To 6 Months", + legend: "6M", + }, + { + id: "up-to-1y", + key: "up_to_1y", + name: "Up To 1 Year", + legend: "1Y", + }, + { + id: "up-to-2y", + key: "up_to_2y", + name: "Up To 2 Years", + legend: "2Y", + }, + { + id: "up-to-3y", + key: "up_to_3y", + name: "Up To 3 Years", + legend: "3Y", + }, + { + id: "up-to-5y", + key: "up_to_5y", + name: "Up To 5 Years", + legend: "5Y", + }, + { + id: "up-to-7y", + key: "up_to_7y", + name: "Up To 7 Years", + legend: "7Y", + }, + { + id: "up-to-10y", + key: "up_to_10y", + name: "Up To 10 Years", + legend: "10Y", + }, + { + id: "up-to-15y", + key: "up_to_15y", + name: "Up To 15 Years", + legend: "15Y", + }, + ]); + + const fromXToY = /** @type {const} */ ([ + { + id: "from-1d-to-1w", + key: "from_1d_to_1w", + name: "From 1 Day To 1 Week", + legend: "1D — 1W", + }, + { + id: "from-1w-to-1m", + key: "from_1w_to_1m", + name: "From 1 Week To 1 Month", + legend: "1W — 1M", + }, + { + id: "from-1m-to-3m", + key: "from_1m_to_3m", + name: "From 1 Month To 3 Months", + legend: "1M — 3M", + }, + { + id: "from-3m-to-6m", + key: "from_3m_to_6m", + name: "From 3 Months To 6 Months", + legend: "3M — 6M", + }, + { + id: "from-6m-to-1y", + key: "from_6m_to_1y", + name: "From 6 Months To 1 Year", + legend: "6M — 1Y", + }, + { + id: "from-1y-to-2y", + key: "from_1y_to_2y", + name: "From 1 Year To 2 Years", + legend: "1Y — 2Y", + }, + { + id: "from-2y-to-3y", + key: "from_2y_to_3y", + name: "From 2 Years To 3 Years", + legend: "2Y — 3Y", + }, + { + id: "from-3y-to-5y", + key: "from_3y_to_5y", + name: "From 3 Years To 5 Years", + legend: "3Y — 5Y", + }, + { + id: "from-5y-to-7y", + key: "from_5y_to_7y", + name: "From 5 Years To 7 Years", + legend: "5Y — 7Y", + }, + { + id: "from-7y-to-10y", + key: "from_7y_to_10y", + name: "From 7 Years To 10 Years", + legend: "7Y — 10Y", + }, + { + id: "from-10y-to-15y", + key: "from_10y_to_15y", + name: "From 10 Years To 15 Years", + legend: "10Y — 15Y", + }, + ]); + + const fromX = /** @type {const} */ ([ + { + id: "from-1y", + key: "from_1y", + name: "From 1 Year", + legend: "1Y+", + }, + { + id: "from-2y", + key: "from_2y", + name: "From 2 Years", + legend: "2Y+", + }, + { + id: "from-4y", + key: "from_4y", + name: "From 4 Years", + legend: "4Y+", + }, + { + id: "from-10y", + key: "from_10y", + name: "From 10 Years", + legend: "10Y+", + }, + { + id: "from-15y", + key: "from_15y", + name: "From 15 Years", + legend: "15Y+", + }, + ]); + + const epochs = /** @type {const} */ ([ + { id: "epoch-1", key: "epoch_1", name: "Epoch 1" }, + { id: "epoch-2", key: "epoch_2", name: "Epoch 2" }, + { id: "epoch-3", key: "epoch_3", name: "Epoch 3" }, + { id: "epoch-4", key: "epoch_4", name: "Epoch 4" }, + { id: "epoch-5", key: "epoch_5", name: "Epoch 5" }, + ]); + + const age = /** @type {const} */ ([ + { + key: "", + id: "", + name: "", + }, + ...xTermHolders, + ...upTo, + ...fromXToY, + ...fromX, + ...epochs, + ]); + + const size = /** @type {const} */ ([ + { + key: "plankton", + name: "Plankton", + size: "1 sat to 0.1 BTC", + }, + { + key: "shrimp", + name: "Shrimp", + size: "0.1 sat to 1 BTC", + }, + { key: "crab", name: "Crab", size: "1 BTC to 10 BTC" }, + { key: "fish", name: "Fish", size: "10 BTC to 100 BTC" }, + { key: "shark", name: "Shark", size: "100 BTC to 1000 BTC" }, + { key: "whale", name: "Whale", size: "1000 BTC to 10 000 BTC" }, + { + key: "humpback", + name: "Humpback", + size: "10 000 BTC to 100 000 BTC", + }, + { + key: "megalodon", + name: "Megalodon", + size: "More than 100 000 BTC", + }, + ]); + + const type = /** @type {const} */ ([ + { key: "p2pk", name: "P2PK" }, + { key: "p2pkh", name: "P2PKH" }, + { key: "p2sh", name: "P2SH" }, + { key: "p2wpkh", name: "P2WPKH" }, + { key: "p2wsh", name: "P2WSH" }, + { key: "p2tr", name: "P2TR" }, + ]); + + const address = /** @type {const} */ ([...size, ...type]); + + const liquidities = /** @type {const} */ ([ + { + key: "illiquid", + id: "illiquid", + name: "Illiquid", + }, + { key: "liquid", id: "liquid", name: "Liquid" }, + { + key: "highly_liquid", + id: "highly-liquid", + name: "Highly Liquid", + }, + ]); + + const averages = /** @type {const} */ ([ + { name: "1 Week", key: "1w", days: 7 }, + { name: "8 Days", key: "8d", days: 8 }, + { name: "13 Days", key: "13d", days: 13 }, + { name: "21 Days", key: "21d", days: 21 }, + { name: "1 Month", key: "1m", days: 30 }, + { name: "34 Days", key: "34d", days: 34 }, + { name: "55 Days", key: "55d", days: 55 }, + { name: "89 Days", key: "89d", days: 89 }, + { name: "144 Days", key: "144d", days: 144 }, + { name: "1 Year", key: "1y", days: 365 }, + { name: "2 Years", key: "2y", days: 2 * 365 }, + { name: "200 Weeks", key: "200w", days: 200 * 7 }, + { name: "4 Years", key: "4y", days: 4 * 365 }, + ]); + + const totalReturns = /** @type {const} */ ([ + { name: "1 Day", key: "1d" }, + { name: "1 Month", key: "1m" }, + { name: "6 Months", key: "6m" }, + { name: "1 Year", key: "1y" }, + { name: "2 Years", key: "2y" }, + { name: "3 Years", key: "3y" }, + { name: "4 Years", key: "4y" }, + { name: "6 Years", key: "6y" }, + { name: "8 Years", key: "8y" }, + { name: "10 Years", key: "10y" }, + ]); + + const compoundReturns = /** @type {const} */ ([ + { name: "4 Years", key: "4y" }, + ]); + + const percentiles = /** @type {const} */ ([ + { + key: "median_price_paid", + id: "median-price-paid", + name: "Median", + title: "Median Paid", + value: 50, + }, + { + key: "95p_price_paid", + id: "95p-price-paid", + name: `95%`, + title: `95th Percentile Paid`, + value: 95, + }, + { + key: "90p_price_paid", + id: "90p-price-paid", + name: `90%`, + title: `90th Percentile Paid`, + value: 90, + }, + { + key: "85p_price_paid", + id: "85p-price-paid", + name: `85%`, + title: `85th Percentile Paid`, + value: 85, + }, + { + key: "80p_price_paid", + id: "80p-price-paid", + name: `80%`, + title: `80th Percentile Paid`, + value: 80, + }, + { + key: "75p_price_paid", + id: "75p-price-paid", + name: `75%`, + title: `75th Percentile Paid`, + value: 75, + }, + { + key: "70p_price_paid", + id: "70p-price-paid", + name: `70%`, + title: `70th Percentile Paid`, + value: 70, + }, + { + key: "65p_price_paid", + id: "65p-price-paid", + name: `65%`, + title: `65th Percentile Paid`, + value: 65, + }, + { + key: "60p_price_paid", + id: "60p-price-paid", + name: `60%`, + title: `60th Percentile Paid`, + value: 60, + }, + { + key: "55p_price_paid", + id: "55p-price-paid", + name: `55%`, + title: `55th Percentile Paid`, + value: 55, + }, + { + key: "45p_price_paid", + id: "45p-price-paid", + name: `45%`, + title: `45th Percentile Paid`, + value: 45, + }, + { + key: "40p_price_paid", + id: "40p-price-paid", + name: `40%`, + title: `40th Percentile Paid`, + value: 40, + }, + { + key: "35p_price_paid", + id: "35p-price-paid", + name: `35%`, + title: `35th Percentile Paid`, + value: 35, + }, + { + key: "30p_price_paid", + id: "30p-price-paid", + name: `30%`, + title: `30th Percentile Paid`, + value: 30, + }, + { + key: "25p_price_paid", + id: "25p-price-paid", + name: `25%`, + title: `25th Percentile Paid`, + value: 25, + }, + { + key: "20p_price_paid", + id: "20p-price-paid", + name: `20%`, + title: `20th Percentile Paid`, + value: 20, + }, + { + key: "15p_price_paid", + id: "15p-price-paid", + name: `15%`, + title: `15th Percentile Paid`, + value: 15, + }, + { + key: "10p_price_paid", + id: "10p-price-paid", + name: `10%`, + title: `10th Percentile Paid`, + value: 10, + }, + { + key: "05p_price_paid", + id: "05p-price-paid", + name: `5%`, + title: `5th Percentile Paid`, + value: 5, + }, + ]); + + return { + xTermHolders, + upTo, + fromX, + fromXToY, + epochs, + age, + type, + size, + address, + liquidities, + averages, + totalReturns, + compoundReturns, + percentiles, + }; +} +/** + * @typedef {ReturnType} Groups + * + * @typedef {Groups["age"][number]["id"]} AgeCohortId + * + * @typedef {Exclude} AgeCohortIdSub + * + * @typedef {Groups["address"][number]["key"]} AddressCohortId + * + * @typedef {Groups["liquidities"][number]["id"]} LiquidityId + * + * @typedef {AgeCohortId | AddressCohortId} AnyCohortId + * + * @typedef {AddressCohortId | LiquidityId} AnyAddressCohortId + * + * @typedef {AnyCohortId | LiquidityId} AnyPossibleCohortId + * + * @typedef {'' | `${AgeCohortIdSub | AddressCohortId | LiquidityId}-`} AnyDatasetPrefix + * + * @typedef {Groups["averages"][number]["key"]} AverageName + * + * @typedef {Groups["totalReturns"][number]["key"]} TotalReturnKey + * + * @typedef {Groups["compoundReturns"][number]["key"]} CompoundReturnKey + * + * @typedef {Groups["percentiles"][number]["id"]} PercentileId + */ + /** * @param {Colors} colors * @returns {PartialOptionsTree} */ function createPartialOptions(colors) { - function initGroups() { - const xTermHolders = /** @type {const} */ ([ - { - id: "sth", - key: "sth", - name: "Short Term Holders", - legend: "Short Term Holders - STH", - }, - { - id: "lth", - key: "lth", - name: "Long Term Holders", - legend: "Long Term Holders - LTH", - }, - ]); - - const upTo = /** @type {const} */ ([ - { - id: "up-to-1d", - key: "up_to_1d", - name: "Up To 1 Day", - legend: "1D", - }, - { - id: "up-to-1w", - key: "up_to_1w", - name: "Up To 1 Week", - legend: "1W", - }, - { - id: "up-to-1m", - key: "up_to_1m", - name: "Up To 1 Month", - legend: "1M", - }, - { - id: "up-to-2m", - key: "up_to_2m", - name: "Up To 2 Months", - legend: "2M", - }, - { - id: "up-to-3m", - key: "up_to_3m", - name: "Up To 3 Months", - legend: "3M", - }, - { - id: "up-to-4m", - key: "up_to_4m", - name: "Up To 4 Months", - legend: "4M", - }, - { - id: "up-to-5m", - key: "up_to_5m", - name: "Up To 5 Months", - legend: "5M", - }, - { - id: "up-to-6m", - key: "up_to_6m", - name: "Up To 6 Months", - legend: "6M", - }, - { - id: "up-to-1y", - key: "up_to_1y", - name: "Up To 1 Year", - legend: "1Y", - }, - { - id: "up-to-2y", - key: "up_to_2y", - name: "Up To 2 Years", - legend: "2Y", - }, - { - id: "up-to-3y", - key: "up_to_3y", - name: "Up To 3 Years", - legend: "3Y", - }, - { - id: "up-to-5y", - key: "up_to_5y", - name: "Up To 5 Years", - legend: "5Y", - }, - { - id: "up-to-7y", - key: "up_to_7y", - name: "Up To 7 Years", - legend: "7Y", - }, - { - id: "up-to-10y", - key: "up_to_10y", - name: "Up To 10 Years", - legend: "10Y", - }, - { - id: "up-to-15y", - key: "up_to_15y", - name: "Up To 15 Years", - legend: "15Y", - }, - ]); - - const fromXToY = /** @type {const} */ ([ - { - id: "from-1d-to-1w", - key: "from_1d_to_1w", - name: "From 1 Day To 1 Week", - legend: "1D — 1W", - }, - { - id: "from-1w-to-1m", - key: "from_1w_to_1m", - name: "From 1 Week To 1 Month", - legend: "1W — 1M", - }, - { - id: "from-1m-to-3m", - key: "from_1m_to_3m", - name: "From 1 Month To 3 Months", - legend: "1M — 3M", - }, - { - id: "from-3m-to-6m", - key: "from_3m_to_6m", - name: "From 3 Months To 6 Months", - legend: "3M — 6M", - }, - { - id: "from-6m-to-1y", - key: "from_6m_to_1y", - name: "From 6 Months To 1 Year", - legend: "6M — 1Y", - }, - { - id: "from-1y-to-2y", - key: "from_1y_to_2y", - name: "From 1 Year To 2 Years", - legend: "1Y — 2Y", - }, - { - id: "from-2y-to-3y", - key: "from_2y_to_3y", - name: "From 2 Years To 3 Years", - legend: "2Y — 3Y", - }, - { - id: "from-3y-to-5y", - key: "from_3y_to_5y", - name: "From 3 Years To 5 Years", - legend: "3Y — 5Y", - }, - { - id: "from-5y-to-7y", - key: "from_5y_to_7y", - name: "From 5 Years To 7 Years", - legend: "5Y — 7Y", - }, - { - id: "from-7y-to-10y", - key: "from_7y_to_10y", - name: "From 7 Years To 10 Years", - legend: "7Y — 10Y", - }, - { - id: "from-10y-to-15y", - key: "from_10y_to_15y", - name: "From 10 Years To 15 Years", - legend: "10Y — 15Y", - }, - ]); - - const fromX = /** @type {const} */ ([ - { - id: "from-1y", - key: "from_1y", - name: "From 1 Year", - legend: "1Y+", - }, - { - id: "from-2y", - key: "from_2y", - name: "From 2 Years", - legend: "2Y+", - }, - { - id: "from-4y", - key: "from_4y", - name: "From 4 Years", - legend: "4Y+", - }, - { - id: "from-10y", - key: "from_10y", - name: "From 10 Years", - legend: "10Y+", - }, - { - id: "from-15y", - key: "from_15y", - name: "From 15 Years", - legend: "15Y+", - }, - ]); - - const epochs = /** @type {const} */ ([ - { id: "epoch-1", key: "epoch_1", name: "1" }, - { id: "epoch-2", key: "epoch_2", name: "2" }, - { id: "epoch-3", key: "epoch_3", name: "3" }, - { id: "epoch-4", key: "epoch_4", name: "4" }, - { id: "epoch-5", key: "epoch_5", name: "5" }, - ]); - - const age = /** @type {const} */ ([ - { - key: "", - id: "", - name: "", - }, - ...xTermHolders, - ...upTo, - ...fromXToY, - ...fromX, - ...epochs, - ]); - - const size = /** @type {const} */ ([ - { - key: "plankton", - name: "Plankton", - size: "1 sat to 0.1 BTC", - }, - { - key: "shrimp", - name: "Shrimp", - size: "0.1 sat to 1 BTC", - }, - { key: "crab", name: "Crab", size: "1 BTC to 10 BTC" }, - { key: "fish", name: "Fish", size: "10 BTC to 100 BTC" }, - { key: "shark", name: "Shark", size: "100 BTC to 1000 BTC" }, - { key: "whale", name: "Whale", size: "1000 BTC to 10 000 BTC" }, - { - key: "humpback", - name: "Humpback", - size: "10 000 BTC to 100 000 BTC", - }, - { - key: "megalodon", - name: "Megalodon", - size: "More than 100 000 BTC", - }, - ]); - - const type = /** @type {const} */ ([ - { key: "p2pk", name: "P2PK" }, - { key: "p2pkh", name: "P2PKH" }, - { key: "p2sh", name: "P2SH" }, - { key: "p2wpkh", name: "P2WPKH" }, - { key: "p2wsh", name: "P2WSH" }, - { key: "p2tr", name: "P2TR" }, - ]); - - const address = /** @type {const} */ ([...size, ...type]); - - const liquidities = /** @type {const} */ ([ - { - key: "illiquid", - id: "illiquid", - name: "Illiquid", - }, - { key: "liquid", id: "liquid", name: "Liquid" }, - { - key: "highly_liquid", - id: "highly-liquid", - name: "Highly Liquid", - }, - ]); - - const averages = /** @type {const} */ ([ - { name: "1 Week", key: "1w", days: 7 }, - { name: "8 Days", key: "8d", days: 8 }, - { name: "13 Days", key: "13d", days: 13 }, - { name: "21 Days", key: "21d", days: 21 }, - { name: "1 Month", key: "1m", days: 30 }, - { name: "34 Days", key: "34d", days: 34 }, - { name: "55 Days", key: "55d", days: 55 }, - { name: "89 Days", key: "89d", days: 89 }, - { name: "144 Days", key: "144d", days: 144 }, - { name: "1 Year", key: "1y", days: 365 }, - { name: "2 Years", key: "2y", days: 2 * 365 }, - { name: "200 Weeks", key: "200w", days: 200 * 7 }, - { name: "4 Years", key: "4y", days: 4 * 365 }, - ]); - - const totalReturns = /** @type {const} */ ([ - { name: "1 Day", key: "1d" }, - { name: "1 Month", key: "1m" }, - { name: "6 Months", key: "6m" }, - { name: "1 Year", key: "1y" }, - { name: "2 Years", key: "2y" }, - { name: "3 Years", key: "3y" }, - { name: "4 Years", key: "4y" }, - { name: "6 Years", key: "6y" }, - { name: "8 Years", key: "8y" }, - { name: "10 Years", key: "10y" }, - ]); - - const compoundReturns = /** @type {const} */ ([ - { name: "4 Years", key: "4y" }, - ]); - - const percentiles = /** @type {const} */ ([ - { - key: "median_price_paid", - id: "median-price-paid", - name: "Median", - title: "Median Paid", - value: 50, - }, - { - key: "95p_price_paid", - id: "95p-price-paid", - name: `95%`, - title: `95th Percentile Paid`, - value: 95, - }, - { - key: "90p_price_paid", - id: "90p-price-paid", - name: `90%`, - title: `90th Percentile Paid`, - value: 90, - }, - { - key: "85p_price_paid", - id: "85p-price-paid", - name: `85%`, - title: `85th Percentile Paid`, - value: 85, - }, - { - key: "80p_price_paid", - id: "80p-price-paid", - name: `80%`, - title: `80th Percentile Paid`, - value: 80, - }, - { - key: "75p_price_paid", - id: "75p-price-paid", - name: `75%`, - title: `75th Percentile Paid`, - value: 75, - }, - { - key: "70p_price_paid", - id: "70p-price-paid", - name: `70%`, - title: `70th Percentile Paid`, - value: 70, - }, - { - key: "65p_price_paid", - id: "65p-price-paid", - name: `65%`, - title: `65th Percentile Paid`, - value: 65, - }, - { - key: "60p_price_paid", - id: "60p-price-paid", - name: `60%`, - title: `60th Percentile Paid`, - value: 60, - }, - { - key: "55p_price_paid", - id: "55p-price-paid", - name: `55%`, - title: `55th Percentile Paid`, - value: 55, - }, - { - key: "45p_price_paid", - id: "45p-price-paid", - name: `45%`, - title: `45th Percentile Paid`, - value: 45, - }, - { - key: "40p_price_paid", - id: "40p-price-paid", - name: `40%`, - title: `40th Percentile Paid`, - value: 40, - }, - { - key: "35p_price_paid", - id: "35p-price-paid", - name: `35%`, - title: `35th Percentile Paid`, - value: 35, - }, - { - key: "30p_price_paid", - id: "30p-price-paid", - name: `30%`, - title: `30th Percentile Paid`, - value: 30, - }, - { - key: "25p_price_paid", - id: "25p-price-paid", - name: `25%`, - title: `25th Percentile Paid`, - value: 25, - }, - { - key: "20p_price_paid", - id: "20p-price-paid", - name: `20%`, - title: `20th Percentile Paid`, - value: 20, - }, - { - key: "15p_price_paid", - id: "15p-price-paid", - name: `15%`, - title: `15th Percentile Paid`, - value: 15, - }, - { - key: "10p_price_paid", - id: "10p-price-paid", - name: `10%`, - title: `10th Percentile Paid`, - value: 10, - }, - { - key: "05p_price_paid", - id: "05p-price-paid", - name: `5%`, - title: `5th Percentile Paid`, - value: 5, - }, - ]); - - return { - xTermHolders, - upTo, - fromX, - fromXToY, - epochs, - age, - type, - size, - address, - liquidities, - averages, - totalReturns, - compoundReturns, - percentiles, - }; - } const groups = initGroups(); - /** - * @typedef {(typeof groups.age)[number]["id"]} AgeCohortId - * @typedef {Exclude} AgeCohortIdSub - * @typedef {(typeof groups.address)[number]["key"]} AddressCohortId - * @typedef {(typeof groups.liquidities[number]["id"])} LiquidityId - * @typedef {AgeCohortId | AddressCohortId} AnyCohortId - * @typedef {AnyCohortId | LiquidityId} AnyPossibleCohortId - * @typedef {'' | `${AgeCohortIdSub | AddressCohortId | LiquidityId}-`} AnyDatasetPrefix - * @typedef {(typeof groups.averages)[number]["key"]} AverageName - * @typedef {(typeof groups.totalReturns)[number]["key"]} TotalReturnKey - * @typedef {(typeof groups.compoundReturns)[number]["key"]} CompoundReturnKey - * @typedef {(typeof groups.percentiles)[number]["id"]} PercentileId - */ const bases = { /** @@ -1332,7 +1351,7 @@ function createPartialOptions(colors) { /** * @returns {PartialOptionsGroup} */ - function createIndicatorsOptinos() { + function createIndicatorsOptions() { return { name: "Indicators", tree: [], @@ -1388,7 +1407,7 @@ function createPartialOptions(colors) { createAllTimeHighOptions(scale), createAveragesOptions(scale), ...(scale === "date" - ? [createReturnsOptions(), createIndicatorsOptinos()] + ? [createReturnsOptions(), createIndicatorsOptions()] : []), ], }; @@ -1713,7 +1732,7 @@ function createPartialOptions(colors) { name: "Coinbases", tree: [ ...(scale === "date" - ? /** @type {PartialOptionsTree} */ ([ + ? /** @satisfies {PartialOptionsTree} */ ([ { name: "Last", tree: [ @@ -1764,7 +1783,7 @@ function createPartialOptions(colors) { { title: "Sum", color: colors.bitcoin, - datasetPath: `${scale}-to-coinbase`, + datasetPath: `${scale}-to-coinbase-1d-sum`, }, ], }, @@ -1779,7 +1798,7 @@ function createPartialOptions(colors) { { title: "Sum", color: colors.dollars, - datasetPath: `${scale}-to-coinbase-in-dollars`, + datasetPath: `${scale}-to-coinbase-in-dollars-1d-sum`, }, ], }, @@ -2086,7 +2105,7 @@ function createPartialOptions(colors) { { title: "Sum", color: colors.dollars, - datasetPath: `${scale}-to-fees-in-dollars`, + datasetPath: `${scale}-to-fees-in-dollars-1d-sum`, }, ], }, @@ -2609,16 +2628,81 @@ function createPartialOptions(colors) { }; } + const cohortOptionOrOptions = { + /** + * @param {DefaultCohortOption | DefaultCohortOptions} arg + */ + toList(arg) { + return "list" in arg ? arg.list : [arg]; + }, + /** + * @param {DefaultCohortOption | DefaultCohortOptions} arg + * @param {string} cohortName + * @param {string} legendName + */ + toLegendName(arg, cohortName, legendName) { + return "list" in arg ? `${cohortName} ${legendName}` : legendName; + }, + /** + * @template {AnyPossibleCohortId} T + * @param {CohortOption | CohortOptions} arg + * @param {SeriesBlueprintParam & Omit} blueprint + */ + toSeriesBlueprints(arg, blueprint) { + return this.toList(arg).map( + ({ scale, datasetId, color, name }) => + /** @satisfies {SeriesBlueprint} */ ({ + title: cohortOptionOrOptions.toLegendName( + arg, + name, + blueprint.title, + ), + color: "list" in arg ? color : blueprint.singleColor || color, + type: blueprint.type || "Line", + // Don't get why TS is annoying here + // @ts-ignore + datasetPath: blueprint.genPath(datasetId, scale), + options: blueprint.options, + }), + ); + }, + /** + * @param {DefaultCohortOption | DefaultCohortOptions} arg + */ + shouldShowAll(arg) { + return "list" in arg || arg.datasetId; + }, + /** + * @param {DefaultCohortOption | DefaultCohortOptions} arg + * @param {(prefix: AnyDatasetPrefix, arg: DefaultCohortOption) => PartialChartOption[]} genOption + */ + genOptionsIfSingle(arg, genOption) { + if (!("list" in arg)) { + const prefix = datasetIdToPrefix(arg.datasetId); + return genOption(prefix, arg); + } else { + return []; + } + }, + /** + * @param {DefaultCohortOption | DefaultCohortOptions} arg + * @param {(scale: 'date') => PartialChartOption[]} genOption + */ + genOptionsIfDate(arg, genOption) { + if (arg.scale === "date") { + return genOption("date"); + } else { + return []; + } + }, + }; + /** - * @param {Object} args - * @param {TimeScale} args.scale - * @param {AnyPossibleCohortId} args.datasetId - * @param {string} args.title - * @param {Color} args.color + * @param {DefaultCohortOption | DefaultCohortOptions} arg * @returns {PartialOptionsGroup} */ - function createCohortUTXOOptions({ scale, color, datasetId, title }) { - const datasetPrefix = datasetIdToPrefix(datasetId); + function createCohortUTXOOptions(arg) { + const { scale, title } = arg; return { name: "UTXOs", @@ -2630,28 +2714,24 @@ function createPartialOptions(colors) { description: "", unit: "Count", icon: "🎫", - bottom: [ - { - title: "Count", - color, - datasetPath: `${scale}-to-${datasetPrefix}utxo-count`, - }, - ], + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Count", + genPath: (id, scale) => + /** @type {const} */ ( + `${scale}-to-${datasetIdToPrefix(id)}utxo-count` + ), + }), }, ], }; } /** - * @param {Object} args - * @param {TimeScale} args.scale - * @param {AnyPossibleCohortId} args.datasetId - * @param {string} args.title - * @param {Color} args.color + * @param {DefaultCohortOption | DefaultCohortOptions} arg * @returns {PartialOptionsGroup} */ - function createCohortRealizedOptions({ scale, color, datasetId, title }) { - const datasetPrefix = datasetIdToPrefix(datasetId); + function createCohortRealizedOptions(arg) { + const { scale, title } = arg; return { name: "Realized", @@ -2663,21 +2743,19 @@ function createPartialOptions(colors) { description: "", unit: "US Dollars", icon: "🏷️", - top: [ - { - title: "Realized Price", - color, - datasetPath: `${scale}-to-${datasetPrefix}realized-price`, - }, - ], + top: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Realized Price", + genPath: (id, scale) => + `${scale}-to-${datasetIdToPrefix(id)}realized-price`, + }), }, - createRatioOptions({ - scale, - color, - ratioDatasetPath: `${scale}-to-market-price-to-${datasetPrefix}realized-price-ratio`, - valueDatasetPath: `${scale}-to-${datasetPrefix}realized-price`, - title: `${title} Realized Price`, - }), + // createRatioOptions({ + // scale, + // color, + // ratioDatasetPath: `${scale}-to-market-price-to-${datasetPrefix}realized-price-ratio`, + // valueDatasetPath: `${scale}-to-${datasetPrefix}realized-price`, + // title: `${title} Realized Price`, + // }), { scale, name: `Capitalization`, @@ -2686,12 +2764,16 @@ function createPartialOptions(colors) { unit: "US Dollars", icon: "💰", bottom: [ - { - title: `${name} Realized Cap.`, - color, - datasetPath: `${scale}-to-${datasetPrefix}realized-cap`, - }, - ...(datasetId + ...cohortOptionOrOptions.toSeriesBlueprints( + arg, + + { + title: "Realized Cap.", + genPath: (id, scale) => + `${scale}-to-${datasetIdToPrefix(id)}realized-cap`, + }, + ), + ...(cohortOptionOrOptions.shouldShowAll(arg) ? /** @type {const} */ ([ { title: "Realized Cap.", @@ -2711,11 +2793,16 @@ function createPartialOptions(colors) { unit: "US Dollars", icon: "🔀", bottom: [ - { - title: `Net Change`, - type: "Baseline", - datasetPath: `${scale}-to-${datasetPrefix}realized-cap-1m-net-change`, - }, + ...cohortOptionOrOptions.toSeriesBlueprints( + arg, + + { + title: "Net Change", + type: "Baseline", + genPath: (id, scale) => + `${scale}-to-${datasetIdToPrefix(id)}realized-cap-1m-net-change`, + }, + ), bases[0](scale), ], }, @@ -2726,16 +2813,16 @@ function createPartialOptions(colors) { description: "", unit: "US Dollars", icon: "🎉", - bottom: [ - { - title: "Realized Profit", - datasetPath: - scale === "date" - ? `date-to-${datasetPrefix}realized-profit-1d-sum` - : `height-to-${datasetPrefix}realized-profit`, - color: colors.profit, + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Realized Profit", + singleColor: colors.profit, + genPath: (id, scale) => { + const prefix = datasetIdToPrefix(id); + return scale === "date" + ? `date-to-${prefix}realized-profit-1d-sum` + : `height-to-${prefix}realized-profit`; }, - ], + }), }, { scale, @@ -2744,46 +2831,48 @@ function createPartialOptions(colors) { description: "", unit: "US Dollars", icon: "⚰️", - bottom: [ - { - title: "Realized Loss", - datasetPath: - scale === "date" - ? `date-to-${datasetPrefix}realized-loss-1d-sum` - : `height-to-${datasetPrefix}realized-loss`, - color: colors.loss, + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Realized Loss", + singleColor: colors.loss, + genPath: (id, scale) => { + const prefix = datasetIdToPrefix(id); + return scale === "date" + ? `date-to-${prefix}realized-loss-1d-sum` + : `height-to-${prefix}realized-loss`; }, - ], - }, - { - scale, - name: `PNL - Profit And Loss`, - title: `${title} Realized Profit And Loss`, - description: "", - unit: "US Dollars", - icon: "⚖️", - bottom: [ - { - title: "Profit", - color: colors.profit, - datasetPath: - scale === "date" - ? `date-to-${datasetPrefix}realized-profit-1d-sum` - : `height-to-${datasetPrefix}realized-profit`, - type: "Baseline", - }, - { - title: "Loss", - color: colors.loss, - datasetPath: - scale === "date" - ? `date-to-${datasetPrefix}negative-realized-loss-1d-sum` - : `height-to-${datasetPrefix}negative-realized-loss`, - type: "Baseline", - }, - bases[0](scale), - ], + }), }, + ...cohortOptionOrOptions.genOptionsIfSingle(arg, (prefix) => [ + { + scale, + name: `PNL - Profit And Loss`, + title: `${title} Realized Profit And Loss`, + description: "", + unit: "US Dollars", + icon: "⚖️", + bottom: [ + { + title: "Profit", + color: colors.profit, + datasetPath: + scale === "date" + ? `date-to-${prefix}realized-profit-1d-sum` + : `height-to-${prefix}realized-profit`, + type: "Baseline", + }, + { + title: "Loss", + color: colors.loss, + datasetPath: + scale === "date" + ? `date-to-${prefix}negative-realized-loss-1d-sum` + : `height-to-${prefix}negative-realized-loss`, + type: "Baseline", + }, + bases[0](scale), + ], + }, + ]), { scale, name: `Net PNL - Net Profit And Loss`, @@ -2792,37 +2881,38 @@ function createPartialOptions(colors) { unit: "US Dollars", icon: "⚖️", bottom: [ - { + ...cohortOptionOrOptions.toSeriesBlueprints(arg, { title: "Net PNL", type: "Baseline", - datasetPath: - scale === "date" - ? `date-to-${datasetPrefix}net-realized-profit-and-loss-1d-sum` - : `height-to-${datasetPrefix}net-realized-profit-and-loss`, - }, + genPath: (id, scale) => { + const prefix = datasetIdToPrefix(id); + return scale === "date" + ? `date-to-${prefix}net-realized-profit-and-loss-1d-sum` + : `height-to-${prefix}net-realized-profit-and-loss`; + }, + }), bases[0](scale), ], }, - ...(scale === "date" - ? /** @type {PartialOptionsTree} */ ([ - { - scale, - name: `Net PNL Relative To Market Cap`, - title: `${title} Net Realized Profit And Loss Relative To Market Capitalization`, - description: "", - unit: "Percentage", - icon: "➗", - bottom: [ - { - title: "Net", - type: "Baseline", - datasetPath: `${scale}-to-${datasetPrefix}net-realized-profit-and-loss-to-market-cap-ratio`, - }, - bases[0](scale), - ], - }, - ]) - : []), + ...cohortOptionOrOptions.genOptionsIfDate(arg, (scale) => [ + { + scale, + name: `Net PNL Relative To Market Cap`, + title: `${title} Net Realized Profit And Loss Relative To Market Capitalization`, + description: "", + unit: "Percentage", + icon: "➗", + bottom: [ + ...cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Net", + type: "Baseline", + genPath: (id) => + `${scale}-to-${datasetIdToPrefix(id)}net-realized-profit-and-loss-to-market-cap-ratio`, + }), + bases[0](scale), + ], + }, + ]), { name: "Cumulative", tree: [ @@ -2833,13 +2923,12 @@ function createPartialOptions(colors) { description: "", unit: "US Dollars", icon: "🎊", - bottom: [ - { - title: "Cumulative Realized Profit", - color: colors.profit, - datasetPath: `${scale}-to-${datasetPrefix}cumulative-realized-profit`, - }, - ], + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Cumulative Realized Profit", + singleColor: colors.profit, + genPath: (id, scale) => + `${scale}-to-${datasetIdToPrefix(id)}cumulative-realized-profit`, + }), }, { scale, @@ -2848,13 +2937,12 @@ function createPartialOptions(colors) { description: "", unit: "US Dollars", icon: "☠️", - bottom: [ - { - title: "Cumulative Realized Loss", - color: colors.loss, - datasetPath: `${scale}-to-${datasetPrefix}cumulative-realized-loss`, - }, - ], + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Cumulative Realized Loss", + singleColor: colors.loss, + genPath: (id, scale) => + `${scale}-to-${datasetIdToPrefix(id)}cumulative-realized-loss`, + }), }, { scale, @@ -2864,11 +2952,12 @@ function createPartialOptions(colors) { unit: "US Dollars", icon: "➕", bottom: [ - { + ...cohortOptionOrOptions.toSeriesBlueprints(arg, { title: "Cumulative Net Realized PNL", type: "Baseline", - datasetPath: `${scale}-to-${datasetPrefix}cumulative-net-realized-profit-and-loss`, - }, + genPath: (id, scale) => + `${scale}-to-${datasetIdToPrefix(id)}cumulative-net-realized-profit-and-loss`, + }), bases[0](scale), ], }, @@ -2880,11 +2969,12 @@ function createPartialOptions(colors) { unit: "US Dollars", icon: "🗓️", bottom: [ - { + ...cohortOptionOrOptions.toSeriesBlueprints(arg, { title: "Cumulative Net Realized PNL 30d Change", - datasetPath: `${scale}-to-${datasetPrefix}cumulative-net-realized-profit-and-loss-1m-net-change`, type: "Baseline", - }, + genPath: (id, scale) => + `${scale}-to-${datasetIdToPrefix(id)}cumulative-net-realized-profit-and-loss-1m-net-change`, + }), bases[0](scale), ], }, @@ -2898,19 +2988,21 @@ function createPartialOptions(colors) { unit: "Ratio", icon: "∶", bottom: [ - { + ...cohortOptionOrOptions.toSeriesBlueprints(arg, { title: "Ratio", - datasetPath: - scale === "date" - ? `date-to-${datasetPrefix}realized-profit-to-loss-1d-sum-ratio` - : `height-to-${datasetPrefix}realized-profit-to-loss-ratio`, type: "Baseline", options: { baseValue: { price: 1, }, }, - }, + genPath: (id, scale) => { + const prefix = datasetIdToPrefix(id); + return scale === "date" + ? `date-to-${prefix}realized-profit-to-loss-1d-sum-ratio` + : `height-to-${prefix}realized-profit-to-loss-ratio`; + }, + }), bases[1](scale, "Even"), ], }, @@ -2925,16 +3017,17 @@ function createPartialOptions(colors) { unit: "Percentage", icon: "➗", bottom: [ - { + ...cohortOptionOrOptions.toSeriesBlueprints(arg, { title: "SOPR", - datasetPath: `${scale}-to-${datasetPrefix}spent-output-profit-ratio`, type: "Baseline", options: { baseValue: { price: 1, }, }, - }, + genPath: (id, scale) => + `${scale}-to-${datasetIdToPrefix(id)}spent-output-profit-ratio`, + }), bases[1](scale), ], }, @@ -2946,16 +3039,17 @@ function createPartialOptions(colors) { unit: "Percentage", icon: "➗", bottom: [ - { + ...cohortOptionOrOptions.toSeriesBlueprints(arg, { title: "aSOPR", - datasetPath: `${scale}-to-${datasetPrefix}adjusted-spent-output-profit-ratio`, type: "Baseline", options: { baseValue: { price: 1, }, }, - }, + genPath: (id, scale) => + `${scale}-to-${datasetIdToPrefix(id)}adjusted-spent-output-profit-ratio`, + }), bases[1](scale), ], }, @@ -2974,16 +3068,16 @@ function createPartialOptions(colors) { description: "", unit: "US Dollars", icon: "➕", - bottom: [ - { - title: "Value", - color: colors.profit, - datasetPath: - scale === "date" - ? `date-to-${datasetPrefix}value-created-1d-sum` - : `height-to-${datasetPrefix}value-created`, + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Value", + singleColor: colors.profit, + genPath: (id, scale) => { + const prefix = datasetIdToPrefix(id); + return scale === "date" + ? `date-to-${prefix}value-created-1d-sum` + : `height-to-${prefix}value-created`; }, - ], + }), }, { scale, @@ -2992,16 +3086,16 @@ function createPartialOptions(colors) { description: "", unit: "US Dollars", icon: "➕", - bottom: [ - { - title: "Adjusted Value", - color: colors.profit, - datasetPath: - scale === "date" - ? `date-to-${datasetPrefix}adjusted-value-created-1d-sum` - : `height-to-${datasetPrefix}adjusted-value-created`, + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Adjusted Value", + singleColor: colors.profit, + genPath: (id, scale) => { + const prefix = datasetIdToPrefix(id); + return scale === "date" + ? `date-to-${prefix}adjusted-value-created-1d-sum` + : `height-to-${prefix}adjusted-value-created`; }, - ], + }), }, ], }, @@ -3015,16 +3109,16 @@ function createPartialOptions(colors) { description: "", unit: "US Dollars", icon: "☄️", - bottom: [ - { - title: "Value", - color: colors.loss, - datasetPath: - scale === "date" - ? `date-to-${datasetPrefix}value-destroyed-1d-sum` - : `height-to-${datasetPrefix}value-destroyed`, + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Value", + singleColor: colors.loss, + genPath: (id, scale) => { + const prefix = datasetIdToPrefix(id); + return scale === "date" + ? `date-to-${prefix}value-destroyed-1d-sum` + : `height-to-${prefix}value-destroyed`; }, - ], + }), }, { scale, @@ -3033,56 +3127,48 @@ function createPartialOptions(colors) { description: "", unit: "US Dollars", icon: "☄️", - bottom: [ - { - title: "Adjusted Value", - color: colors.loss, - datasetPath: - scale === "date" - ? `date-to-${datasetPrefix}adjusted-value-destroyed-1d-sum` - : `height-to-${datasetPrefix}adjusted-value-destroyed`, + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Adjusted Value", + singleColor: colors.loss, + genPath: (id, scale) => { + const prefix = datasetIdToPrefix(id); + return scale === "date" + ? `date-to-${prefix}adjusted-value-destroyed-1d-sum` + : `height-to-${prefix}adjusted-value-destroyed`; }, - ], + }), }, ], }, ], }, - .../** @satisfies {PartialOptionsTree} */ ( - scale === "date" - ? [ - { - scale, - name: `Sell Side Risk Ratio`, - title: `${title} Sell Side Risk Ratio`, - description: "", - unit: "Percentage", - icon: "🥵", - bottom: [ - { - title: "Ratio", - datasetPath: `${scale}-to-${datasetPrefix}sell-side-risk-ratio`, - color, - }, - ], - }, - ] - : [] - ), + ...cohortOptionOrOptions.genOptionsIfDate(arg, (scale) => [ + { + scale, + name: `Sell Side Risk Ratio`, + title: `${title} Sell Side Risk Ratio`, + description: "", + unit: "Percentage", + icon: "🥵", + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Ratio", + genPath: (id) => + /** @type {const} */ ( + `${scale}-to-${datasetIdToPrefix(id)}sell-side-risk-ratio` + ), + }), + }, + ]), ], }; } /** - * @param {Object} args - * @param {TimeScale} args.scale - * @param {AnyPossibleCohortId} args.datasetId - * @param {string} args.title - * @param {Color} args.color + * @param {DefaultCohortOption | DefaultCohortOptions} arg * @returns {PartialOptionsGroup} */ - function createCohortUnrealizedOptions({ scale, color, datasetId, title }) { - const datasetPrefix = datasetIdToPrefix(datasetId); + function createCohortUnrealizedOptions(arg) { + const { scale, title } = arg; return { name: "Unrealized", @@ -3094,13 +3180,12 @@ function createPartialOptions(colors) { description: "", unit: "US Dollars", icon: "🤑", - bottom: [ - { - title: "Profit", - datasetPath: `${scale}-to-${datasetPrefix}unrealized-profit`, - color: colors.profit, - }, - ], + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Profit", + genPath: (id) => + `${scale}-to-${datasetIdToPrefix(id)}unrealized-profit`, + singleColor: colors.profit, + }), }, { scale, @@ -3109,37 +3194,38 @@ function createPartialOptions(colors) { description: "", unit: "US Dollars", icon: "😭", - bottom: [ - { - title: "Loss", - datasetPath: `${scale}-to-${datasetPrefix}unrealized-loss`, - color: colors.loss, - }, - ], - }, - { - scale, - name: `PNL - Profit And Loss`, - title: `${title} Unrealized Profit And Loss`, - description: "", - unit: "US Dollars", - icon: "🤔", - bottom: [ - { - title: "Profit", - color: colors.profit, - datasetPath: `${scale}-to-${datasetPrefix}unrealized-profit`, - type: "Baseline", - }, - { - title: "Loss", - color: colors.loss, - datasetPath: `${scale}-to-${datasetPrefix}negative-unrealized-loss`, - type: "Baseline", - }, - bases[0](scale), - ], + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Loss", + genPath: (id) => + `${scale}-to-${datasetIdToPrefix(id)}unrealized-loss`, + singleColor: colors.loss, + }), }, + ...cohortOptionOrOptions.genOptionsIfSingle(arg, (prefix) => [ + { + scale, + name: `PNL - Profit And Loss`, + title: `${title} Unrealized Profit And Loss`, + description: "", + unit: "US Dollars", + icon: "🤔", + bottom: [ + { + title: "Profit", + color: colors.profit, + datasetPath: `${scale}-to-${prefix}unrealized-profit`, + type: "Baseline", + }, + { + title: "Loss", + color: colors.loss, + datasetPath: `${scale}-to-${prefix}negative-unrealized-loss`, + type: "Baseline", + }, + bases[0](scale), + ], + }, + ]), { scale, name: `Net PNL - Net Profit And Loss`, @@ -3148,11 +3234,12 @@ function createPartialOptions(colors) { unit: "US Dollars", icon: "⚖️", bottom: [ - { + ...cohortOptionOrOptions.toSeriesBlueprints(arg, { title: "Net Unrealized PNL", - datasetPath: `${scale}-to-${datasetPrefix}net-unrealized-profit-and-loss`, + genPath: (id) => + `${scale}-to-${datasetIdToPrefix(id)}net-unrealized-profit-and-loss`, type: "Baseline", - }, + }), bases[0](scale), ], }, @@ -3164,11 +3251,12 @@ function createPartialOptions(colors) { unit: "Percentage", icon: "➗", bottom: [ - { + ...cohortOptionOrOptions.toSeriesBlueprints(arg, { title: "Relative Net Unrealized PNL", - datasetPath: `${scale}-to-${datasetPrefix}net-unrealized-profit-and-loss-to-market-cap-ratio`, + genPath: (id) => + `${scale}-to-${datasetIdToPrefix(id)}net-unrealized-profit-and-loss-to-market-cap-ratio`, type: "Baseline", - }, + }), bases[0](scale), ], }, @@ -3177,15 +3265,11 @@ function createPartialOptions(colors) { } /** - * @param {Object} args - * @param {TimeScale} args.scale - * @param {AnyPossibleCohortId} args.datasetId - * @param {string} args.title - * @param {Color} args.color + * @param {DefaultCohortOption | DefaultCohortOptions} arg * @returns {PartialOptionsGroup} */ - function createCohortSupplyOptions({ scale, color, datasetId, title }) { - const datasetPrefix = datasetIdToPrefix(datasetId); + function createCohortSupplyOptions(arg) { + const { scale, title } = arg; return { name: "Supply", @@ -3193,39 +3277,41 @@ function createPartialOptions(colors) { { name: "Absolute", tree: [ - { - scale, - name: "All", - title: `${title} Profit And Loss`, - icon: "❌", - description: "", - unit: "US Dollars", - bottom: [ - { - title: "In Profit", - color: colors.profit, - datasetPath: `${scale}-to-${datasetPrefix}supply-in-profit`, - }, - { - title: "In Loss", - color: colors.loss, - datasetPath: `${scale}-to-${datasetPrefix}supply-in-loss`, - }, - { - title: "Total", - color: colors.default, - datasetPath: `${scale}-to-${datasetPrefix}supply`, - }, - { - title: "Halved Total", - color: colors.off, - datasetPath: `${scale}-to-${datasetPrefix}halved-supply`, - options: { - lineStyle: 4, + ...cohortOptionOrOptions.genOptionsIfSingle(arg, (prefix) => [ + { + scale, + name: "All", + title: `${title} Profit And Loss`, + icon: "❌", + description: "", + unit: "US Dollars", + bottom: [ + { + title: "In Profit", + color: colors.profit, + datasetPath: `${scale}-to-${prefix}supply-in-profit`, }, - }, - ], - }, + { + title: "In Loss", + color: colors.loss, + datasetPath: `${scale}-to-${prefix}supply-in-loss`, + }, + { + title: "Total", + color: colors.default, + datasetPath: `${scale}-to-${prefix}supply`, + }, + { + title: "Halved Total", + color: colors.off, + datasetPath: `${scale}-to-${prefix}halved-supply`, + options: { + lineStyle: 4, + }, + }, + ], + }, + ]), { scale, name: `Total`, @@ -3233,13 +3319,10 @@ function createPartialOptions(colors) { icon: "∑", description: "", unit: "Bitcoin", - bottom: [ - { - title: "Supply", - color, - datasetPath: `${scale}-to-${datasetPrefix}supply`, - }, - ], + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Supply", + genPath: (id) => `${scale}-to-${datasetIdToPrefix(id)}supply`, + }), }, { scale, @@ -3248,13 +3331,12 @@ function createPartialOptions(colors) { description: "", unit: "Bitcoin", icon: "📈", - bottom: [ - { - title: "Supply", - color: colors.profit, - datasetPath: `${scale}-to-${datasetPrefix}supply-in-profit`, - }, - ], + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Supply", + singleColor: colors.profit, + genPath: (id) => + `${scale}-to-${datasetIdToPrefix(id)}supply-in-profit`, + }), }, { scale, @@ -3263,52 +3345,53 @@ function createPartialOptions(colors) { description: "", unit: "Bitcoin", icon: "📉", - bottom: [ - { - title: "Supply", - color: colors.loss, - datasetPath: `${scale}-to-${datasetPrefix}supply-in-loss`, - }, - ], + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Supply", + singleColor: colors.loss, + genPath: (id) => + `${scale}-to-${datasetIdToPrefix(id)}supply-in-loss`, + }), }, ], }, { name: "Relative To Circulating", tree: [ - { - scale, - name: "All", - title: `${title} Profit And Loss Relative To Circulating Supply`, - description: "", - unit: "Percentage", - icon: "🔀", - bottom: [ - { - title: "In Profit", - color: colors.profit, - datasetPath: `${scale}-to-${datasetPrefix}supply-in-profit-to-circulating-supply-ratio`, - }, - { - title: "In Loss", - color: colors.loss, - datasetPath: `${scale}-to-${datasetPrefix}supply-in-loss-to-circulating-supply-ratio`, - }, - { - title: "100%", - color: colors.default, - datasetPath: `${scale}-to-${datasetPrefix}supply-to-circulating-supply-ratio`, - }, - { - title: "50%", - color: colors.off, - datasetPath: `${scale}-to-${datasetPrefix}halved-supply-to-circulating-supply-ratio`, - options: { - lineStyle: 4, + ...cohortOptionOrOptions.genOptionsIfSingle(arg, (prefix) => [ + { + scale, + name: "All", + title: `${title} Profit And Loss Relative To Circulating Supply`, + description: "", + unit: "Percentage", + icon: "🔀", + bottom: [ + { + title: "In Profit", + color: colors.profit, + datasetPath: `${scale}-to-${prefix}supply-in-profit-to-circulating-supply-ratio`, }, - }, - ], - }, + { + title: "In Loss", + color: colors.loss, + datasetPath: `${scale}-to-${prefix}supply-in-loss-to-circulating-supply-ratio`, + }, + { + title: "100%", + color: colors.default, + datasetPath: `${scale}-to-${prefix}supply-to-circulating-supply-ratio`, + }, + { + title: "50%", + color: colors.off, + datasetPath: `${scale}-to-${prefix}halved-supply-to-circulating-supply-ratio`, + options: { + lineStyle: 4, + }, + }, + ], + }, + ]), { scale, name: `Total`, @@ -3316,13 +3399,11 @@ function createPartialOptions(colors) { description: "", unit: "Percentage", icon: "∑", - bottom: [ - { - title: "Supply", - color, - datasetPath: `${scale}-to-${datasetPrefix}supply-to-circulating-supply-ratio`, - }, - ], + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Supply", + genPath: (id) => + `${scale}-to-${datasetIdToPrefix(id)}supply-to-circulating-supply-ratio`, + }), }, { scale, @@ -3331,13 +3412,12 @@ function createPartialOptions(colors) { description: "", unit: "Percentage", icon: "📈", - bottom: [ - { - title: "Supply", - color: colors.profit, - datasetPath: `${scale}-to-${datasetPrefix}supply-in-profit-to-circulating-supply-ratio`, - }, - ], + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Supply", + singleColor: colors.profit, + genPath: (id) => + `${scale}-to-${datasetIdToPrefix(id)}supply-in-profit-to-circulating-supply-ratio`, + }), }, { scale, @@ -3346,56 +3426,57 @@ function createPartialOptions(colors) { description: "", unit: "Percentage", icon: "📉", - bottom: [ - { - title: "Supply", - color: colors.loss, - datasetPath: `${scale}-to-${datasetPrefix}supply-in-loss-to-circulating-supply-ratio`, - }, - ], + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Supply", + singleColor: colors.loss, + genPath: (id) => + `${scale}-to-${datasetIdToPrefix(id)}supply-in-loss-to-circulating-supply-ratio`, + }), }, ], }, { name: "Relative To Own", tree: [ - { - scale, - name: "All", - title: `${title} Supply In Profit And Loss Relative To Own Supply`, - description: "", - unit: "Percentage", - icon: "🔀", - bottom: [ - { - title: "In Profit", - color: colors.profit, - datasetPath: `${scale}-to-${datasetPrefix}supply-in-profit-to-own-supply-ratio`, - }, - { - title: "In Loss", - color: colors.loss, - datasetPath: `${scale}-to-${datasetPrefix}supply-in-loss-to-own-supply-ratio`, - }, - { - title: "100%", - color: colors.default, - datasetPath: `${scale}-to-100`, - options: { - lastValueVisible: false, + ...cohortOptionOrOptions.genOptionsIfSingle(arg, (prefix) => [ + { + scale, + name: "All", + title: `${title} Supply In Profit And Loss Relative To Own Supply`, + description: "", + unit: "Percentage", + icon: "🔀", + bottom: [ + { + title: "In Profit", + color: colors.profit, + datasetPath: `${scale}-to-${prefix}supply-in-profit-to-own-supply-ratio`, }, - }, - { - title: "50%", - color: colors.off, - datasetPath: `${scale}-to-50`, - options: { - lineStyle: 4, - lastValueVisible: false, + { + title: "In Loss", + color: colors.loss, + datasetPath: `${scale}-to-${prefix}supply-in-loss-to-own-supply-ratio`, }, - }, - ], - }, + { + title: "100%", + color: colors.default, + datasetPath: `${scale}-to-100`, + options: { + lastValueVisible: false, + }, + }, + { + title: "50%", + color: colors.off, + datasetPath: `${scale}-to-50`, + options: { + lineStyle: 4, + lastValueVisible: false, + }, + }, + ], + }, + ]), { scale, name: "In Profit", @@ -3403,13 +3484,12 @@ function createPartialOptions(colors) { description: "", unit: "Percentage", icon: "📈", - bottom: [ - { - title: "Supply", - color: colors.profit, - datasetPath: `${scale}-to-${datasetPrefix}supply-in-profit-to-own-supply-ratio`, - }, - ], + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Supply", + singleColor: colors.profit, + genPath: (id) => + `${scale}-to-${datasetIdToPrefix(id)}supply-in-profit-to-own-supply-ratio`, + }), }, { scale, @@ -3418,13 +3498,12 @@ function createPartialOptions(colors) { description: "", unit: "Percentage", icon: "📉", - bottom: [ - { - title: "Supply", - color: colors.loss, - datasetPath: `${scale}-to-${datasetPrefix}supply-in-loss-to-own-supply-ratio`, - }, - ], + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Supply", + singleColor: colors.loss, + genPath: (id) => + `${scale}-to-${datasetIdToPrefix(id)}supply-in-loss-to-own-supply-ratio`, + }), }, ], }, @@ -3440,24 +3519,21 @@ function createPartialOptions(colors) { } /** - * @param {Object} args - * @param {TimeScale} args.scale - * @param {AnyPossibleCohortId} args.datasetId - * @param {string} args.title - * @param {Color} args.color + * @param {DefaultCohortOption | DefaultCohortOptions} arg * @returns {PartialOptionsGroup} */ - function createCohortPricesPaidOptions({ scale, color, datasetId, title }) { + function createCohortPricesPaidOptions(arg) { + const { scale, title } = arg; + /** * @param {Object} args * @param {TimeScale} args.scale - * @param {AnyPossibleCohortId} args.cohortId + * @param {AnyDatasetPrefix} args.prefix * @param {PercentileId} args.id * @returns {AnyDatasetPath} */ - function generatePath({ scale, cohortId, id }) { - const datasetPrefix = datasetIdToPrefix(cohortId); - return /** @type {const} */ (`${scale}-to-${datasetPrefix}${id}`); + function generatePath({ scale, prefix, id }) { + return /** @type {const} */ (`${scale}-to-${prefix}${id}`); } return { @@ -3470,39 +3546,37 @@ function createPartialOptions(colors) { description: "", unit: "US Dollars", icon: "~", - top: [ - { - title: "Average", - color, - datasetPath: `${scale}-to-${datasetIdToPrefix( - datasetId, - )}realized-price`, - }, - ], + top: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Average", + genPath: (id) => + `${scale}-to-${datasetIdToPrefix(id)}realized-price`, + }), }, - { - scale, - name: `Deciles`, - title: `${title} deciles`, - icon: "🌗", - description: "", - unit: "US Dollars", - top: groups.percentiles - .filter(({ value }) => Number(value) % 10 === 0) - .map(({ name, id }) => { - const datasetPath = generatePath({ - scale, - cohortId: datasetId, - id, - }); + ...cohortOptionOrOptions.genOptionsIfSingle(arg, (prefix, arg) => [ + { + scale, + name: `Deciles`, + title: `${title} deciles`, + icon: "🌗", + description: "", + unit: "US Dollars", + top: groups.percentiles + .filter(({ value }) => Number(value) % 10 === 0) + .map(({ name, id }) => { + const datasetPath = generatePath({ + scale, + prefix, + id, + }); - return { - datasetPath, - color, - title: name, - }; - }), - }, + return { + datasetPath, + color: arg.color, + title: name, + }; + }), + }, + ]), ...groups.percentiles.map((percentile) => ({ scale, name: percentile.name, @@ -3510,63 +3584,31 @@ function createPartialOptions(colors) { description: "", unit: /** @type {const} */ ("US Dollars"), icon: "🌓", - top: [ - { - title: percentile.name, - color, - datasetPath: generatePath({ + top: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Relative Net Unrealized PNL", + genPath: (id) => + generatePath({ scale, - cohortId: datasetId, + prefix: datasetIdToPrefix(id), id: percentile.id, }), - }, - ], + }), })), ], }; } /** - * @param {Object} args - * @param {string} args.name - * @param {TimeScale} args.scale - * @param {AnyPossibleCohortId} args.datasetId - * @param {string} args.title - * @param {Color} args.color + * @param {DefaultCohortOption | DefaultCohortOptions} arg * @returns {PartialOptionsTree} */ - function createCohortOptions({ name, scale, color, datasetId, title }) { + function createCohortOptions(arg) { return [ - createCohortUTXOOptions({ - color, - datasetId, - scale, - title, - }), - createCohortRealizedOptions({ - color, - datasetId, - scale, - title, - }), - createCohortUnrealizedOptions({ - color, - datasetId, - scale, - title, - }), - createCohortSupplyOptions({ - color, - datasetId, - scale, - title, - }), - createCohortPricesPaidOptions({ - color, - datasetId, - scale, - title, - }), + createCohortUTXOOptions(arg), + createCohortRealizedOptions(arg), + createCohortUnrealizedOptions(arg), + createCohortSupplyOptions(arg), + createCohortPricesPaidOptions(arg), ]; } @@ -3579,43 +3621,27 @@ function createPartialOptions(colors) { function createLiquidityOptions({ scale, color }) { return { name: `Split By Liquidity`, - tree: groups.liquidities.map((liquidity) => { - /** @type {PartialOptionsGroup} */ - const folder = { - name: liquidity.name, - tree: createCohortOptions({ - title: `${liquidity.name}`, - name: `${liquidity.name}`, - scale, - color, - datasetId: liquidity.id, - }), - }; - return folder; - }), + tree: groups.liquidities.map(({ name, id }) => + createAddressCohortOptionGroup({ + name, + title: name, + scale, + color, + datasetId: id, + }), + ), }; } /** - * @param {Object} args - * @param {TimeScale} args.scale - * @param {string} args.name - * @param {AnyPossibleCohortId} args.datasetId - * @param {string} args.title - * @param {Color} args.color + * + * @param {DefaultCohortOption | DefaultCohortOptions} arg * @returns {PartialOptionsGroup} */ - function createCohortOptionsGroup({ scale, color, name, datasetId, title }) { + function createCohortOptionsGroup(arg) { return { - name, - - tree: createCohortOptions({ - title, - name, - scale, - color, - datasetId: datasetId, - }), + name: "list" in arg ? "Compare" : arg.name, + tree: createCohortOptions(arg), }; } @@ -3624,157 +3650,130 @@ function createPartialOptions(colors) { * @returns {PartialOptionsGroup} */ function createHodlersOptions(scale) { + /** + * @param {Object} arg + * @param {string} arg.name + * @param {string} [arg.title] + * @param {DefaultCohortOption[]} arg.list + */ + function createFolder({ name, title, list }) { + return { + name, + tree: [ + createCohortOptionsGroup({ + scale, + name, + title: title || name, + list, + }), + ...list.map((cohort) => createCohortOptionsGroup(cohort)), + ], + }; + } + return { name: "Hodlers", tree: [ - { - scale, - name: `Hodl Supply`, - title: `Hodl Supply`, - description: "", - icon: "🌊", - unit: "Percentage", - bottom: [ - { - title: `24h`, - color: colors.up_to_1d, - datasetPath: `${scale}-to-up-to-1d-supply-to-circulating-supply-ratio`, - }, - - ...groups.fromXToY.map(({ key, id, name, legend }) => ({ - title: legend, - color: colors[key], - datasetPath: /** @type {const} */ ( - `${scale}-to-${id}-supply-to-circulating-supply-ratio` - ), - })), - - { - title: `15y+`, - color: colors.from_15y, - datasetPath: `${scale}-to-from-15y-supply-to-circulating-supply-ratio`, - }, - ], - }, - { + createFolder({ name: "X Term Holders", - tree: [ - ...groups.xTermHolders.map(({ key, id, name, legend }) => - createCohortOptionsGroup({ - scale, - color: colors[key], - name: legend, - datasetId: id, - title: name, - }), - ), - ], - }, - { + list: groups.xTermHolders.map(({ key, id, name, legend }) => ({ + scale, + color: colors[key], + name: legend, + datasetId: id, + title: name, + })), + }), + createFolder({ name: "Up To X", - tree: groups.upTo.map(({ key, id, name }) => - createCohortOptionsGroup({ - scale, - color: colors[key], - name, - datasetId: id, - title: name, - }), - ), - }, - { + list: groups.upTo.map(({ key, id, name }) => ({ + scale, + color: colors[key], + name, + datasetId: id, + title: name, + })), + }), + createFolder({ name: "From X To Y", - tree: groups.fromXToY.map(({ key, id, name }) => - createCohortOptionsGroup({ - scale, - color: colors[key], - name, - datasetId: id, - title: name, - }), - ), - }, - { + list: groups.fromXToY.map(({ key, id, name }) => ({ + scale, + color: colors[key], + name, + datasetId: id, + title: name, + })), + }), + createFolder({ name: "From X", - tree: groups.fromX.map(({ key, id, name }) => - createCohortOptionsGroup({ - scale, - color: colors[key], - name, - datasetId: id, - title: name, - }), - ), - }, - { - name: "Epochs", - tree: groups.epochs.map(({ key, id, name }) => - createCohortOptionsGroup({ - scale, - color: colors[key], - name, - datasetId: id, - title: name, - }), - ), - }, + list: groups.fromX.map(({ key, id, name }) => ({ + scale, + color: colors[key], + name, + datasetId: id, + title: name, + })), + }), + createFolder({ + name: "Epoch X", + title: "Epochs", + list: groups.epochs.map(({ key, id, name }) => ({ + scale, + color: colors[key], + name, + datasetId: id, + title: name, + })), + }), ], }; } + // * @param {Object} args + // * @param {TimeScale} args.scale + // * @param {string} args.name + // * @param {string} args.title + // * @param {AddressCohortId | LiquidityId} args.datasetId + // * @param {Color} args.color /** - * @param {Object} args - * @param {TimeScale} args.scale - * @param {string} args.name - * @param {AddressCohortId} args.datasetId - * @param {Color} args.color + * @param {CohortOption | CohortOptions} arg * @returns {PartialChartOption} */ - function createAddressCountOption({ scale, color, name, datasetId }) { + function createAddressCountOption(arg) { + const { scale, title } = arg; + return { scale, name: `Address Count`, - title: `${name} Address Count`, + title: `${title} Address Count`, description: "", unit: "Count", icon: "📕", - bottom: [ - { - title: "Address Count", - color, - datasetPath: `${scale}-to-${datasetId}-address-count`, - }, - ], + bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { + title: "Address Count", + genPath: (id) => `${scale}-to-${id}-address-count`, + }), }; } /** - * @param {Object} args - * @param {TimeScale} args.scale - * @param {string} args.name - * @param {AddressCohortId} args.datasetId - * @param {Color} args.color - * @param {string} [args.filenameAddon] + * @param {CohortOption | CohortOptions} arg * @returns {PartialOptionsGroup} */ - function createAddressOptions({ - scale, - color, - name, - filenameAddon, - datasetId, - }) { + function createAddressCohortOptionGroup(arg) { return { - name: filenameAddon ? `${name} - ${filenameAddon}` : name, + name: + "list" in arg + ? "Compare" + : arg.filenameAddon + ? `${arg.name} - ${arg.filenameAddon}` + : arg.name, tree: [ - createAddressCountOption({ scale, name, datasetId, color }), - ...createCohortOptions({ - title: name, - scale, - name, - color, - datasetId, - }), + { + name: "Addresses", + tree: [createAddressCountOption(arg)], + }, + ...createCohortOptions(arg), ], }; } @@ -3784,6 +3783,27 @@ function createPartialOptions(colors) { * @returns {PartialOptionsGroup} */ function createAddressesOptions(scale) { + /** + * @param {Object} arg + * @param {string} arg.name + * @param {string} [arg.title] + * @param {CohortOption[]} arg.list + */ + function createFolder({ name, title, list }) { + return { + name, + tree: [ + createAddressCohortOptionGroup({ + scale, + name, + title: title || name, + list, + }), + ...list.map((cohort) => createAddressCohortOptionGroup(cohort)), + ], + }; + } + return { name: "Addresses", tree: [ @@ -3847,30 +3867,29 @@ function createPartialOptions(colors) { }, ], }, - { + createFolder({ name: "By Size", - tree: groups.size.map(({ key, name, size }) => - createAddressOptions({ - scale, - color: colors[key], - name, - filenameAddon: size, - datasetId: key, - }), - ), - }, - { - scale, + title: "Address Sizes", + list: groups.size.map(({ key, name, size }) => ({ + scale, + color: colors[key], + name, + title: name, + filenameAddon: size, + datasetId: key, + })), + }), + createFolder({ name: "By Type", - tree: groups.type.map(({ key, name }) => - createAddressOptions({ - scale, - color: colors[key], - name, - datasetId: key, - }), - ), - }, + title: "Address Types", + list: groups.type.map(({ key, name }) => ({ + scale, + color: colors[key], + name, + title: name, + datasetId: key, + })), + }), ], }; } @@ -4348,7 +4367,7 @@ function createPartialOptions(colors) { ], }, ...(scale === "date" - ? /** @type {PartialOptionsTree} */ ([ + ? /** @satisfies {PartialOptionsTree} */ ([ { scale, icon: "❤️", @@ -4689,7 +4708,7 @@ function createPartialOptions(colors) { ], }, ...(scale === "date" - ? /** @type {PartialOptionsTree} */ ([ + ? /** @satisfies {PartialOptionsTree} */ ([ { name: "Inflation Rate", tree: [ @@ -4950,8 +4969,8 @@ export function initOptions({ function createCountersEffects(option) { let firstFavoritesRun = true; - signals.createEffect(() => { - if (option.isFavorite()) { + signals.createEffect(option.isFavorite, (favorite) => { + if (favorite) { favoritesCount.set((c) => c + 1); } else if (!firstFavoritesRun) { favoritesCount.set((c) => c - 1); @@ -4961,8 +4980,8 @@ export function initOptions({ let firstNewRun = true; - signals.createEffect(() => { - if (!option.visited()) { + signals.createEffect(option.visited, (visited) => { + if (!visited) { newCount.set((c) => c + 1); } else if (!firstNewRun) { newCount.set((c) => c - 1); @@ -4974,7 +4993,7 @@ export function initOptions({ return { favorites: favoritesCount, new: newCount, - createEffect: createCountersEffects, + createEffects: createCountersEffects, }; } const counters = initCounters(); @@ -5003,7 +5022,7 @@ export function initOptions({ /** * @param {SeriesBlueprint[]} array */ - function getMainValueFromBlueprints(array) { + function getMainIdFromBlueprints(array) { const searchArray = array.filter( (blueprint) => blueprint.options?.lastValueVisible !== false && @@ -5022,9 +5041,7 @@ export function initOptions({ .replace(DATE_TO_PREFIX, "") .replace(HEIGHT_TO_PREFIX, ""); - return /** @type {number | undefined} */ ( - lastValues()?.[/** @type {LastPath} */ (id)] - ); + return /** @type {LastPath} */ (id); } /** @@ -5081,9 +5098,7 @@ export function initOptions({ labelTitle: option.title, name: name || option.name, onClick: () => { - // if (option.kind !== "pdf") { selected.set(option); - // } }, href: `/${option.kind === "pdf" ? option.file : option.id}`, }); @@ -5105,30 +5120,36 @@ export function initOptions({ spanMain.append(spanValue); if (!option.top?.length && !option.bottom?.length) { - signals.createEffect(() => { - const close = - webSockets.krakenCandle.latest()?.close ?? lastValues()?.close; - - if (close) { - spanValue.innerHTML = formatValue(close, "US Dollars"); - } - }); + signals.createEffect( + () => webSockets.krakenCandle.latest()?.close ?? lastValues()?.close, + (close) => { + if (close) { + spanValue.innerHTML = formatValue(close, "US Dollars"); + } + }, + ); } else if (option.bottom?.length) { const bottom = option.bottom; - signals.createEffect(() => { - spanValue.innerHTML = formatValue( - getMainValueFromBlueprints(bottom), - option.unit, - ); - }); + const id = getMainIdFromBlueprints(bottom); + + if (id) { + signals.createEffect(lastValues, (lastValues) => { + if (lastValues) { + spanValue.innerHTML = formatValue(lastValues[id], option.unit); + } + }); + } } else if (option.top?.length) { const top = option.top; - signals.createEffect(() => { - spanValue.innerHTML = formatValue( - getMainValueFromBlueprints(top), - option.unit, - ); - }); + const id = getMainIdFromBlueprints(top); + + if (id) { + signals.createEffect(lastValues, (lastValues) => { + if (lastValues) { + spanValue.innerHTML = formatValue(lastValues[id], option.unit); + } + }); + } } } @@ -5142,30 +5163,31 @@ export function initOptions({ } function createFavoriteEffect() { + let wasFavorite = false; + /** @type {HTMLElement | undefined} */ + let iconFavorite = undefined; signals.createEffect( - // @ts-ignore - (_wasFavorite) => { - const wasFavorite = /** @type {boolean} */ (_wasFavorite); - const isFavorite = option.isFavorite(); - + option.isFavorite, + (isFavorite) => { if (!wasFavorite && isFavorite) { - const iconFavorite = window.document.createElement("svg"); + iconFavorite = window.document.createElement("svg"); spanMain.append(iconFavorite); iconFavorite.outerHTML = ''; } else if (wasFavorite && !isFavorite) { - spanMain.lastElementChild?.remove(); + iconFavorite?.remove(); + iconFavorite = undefined; } - return isFavorite; + wasFavorite = isFavorite; }, false, ); } function createCheckEffect() { - signals.createEffect(() => { - if (selected()?.id === option.id) { + signals.createEffect(selected, (selected) => { + if (selected?.id === option.id) { input.checked = true; spanNew?.remove(); option.visited.set(true); @@ -5225,24 +5247,20 @@ export function initOptions({ partialTree.forEach((anyPartial, partialIndex) => { const renderLi = signals.createSignal(true); - const li = signals.createMemo( - // @ts-ignored - (_previous) => { - const previous = /** @type {HTMLLIElement | null} */ (_previous); - previous?.remove(); + const li = signals.createMemo((_previous) => { + const previous = _previous; + previous?.remove(); - const _ul = ul(); + const _ul = ul(); - if (renderLi() && _ul) { - const li = window.document.createElement("li"); - utils.dom.insertElementAtIndex(_ul, li, partialIndex); - return li; - } else { - return null; - } - }, - undefined, - ); + if (renderLi() && _ul) { + const li = window.document.createElement("li"); + utils.dom.insertElementAtIndex(_ul, li, partialIndex); + return li; + } else { + return null; + } + }, /** @type {HTMLLIElement | null} */ (null)); if ("tree" in anyPartial) { const folderId = ids.fromString( @@ -5277,19 +5295,17 @@ export function initOptions({ listForSum.push(childOptionsCount); - signals.createEffect(() => { - const _li = li(); - - if (!_li) { + signals.createEffect(li, (li) => { + if (!li) { passedDetails.set(null); return; } - signals.createEffect(() => { - if (selected().path.includes(thisPath)) { - _li.dataset.highlight = ""; + signals.createEffect(selected, (selected) => { + if (selected.path.includes(thisPath)) { + li.dataset.highlight = ""; } else { - delete _li.dataset.highlight; + delete li.dataset.highlight; } }); @@ -5298,7 +5314,7 @@ export function initOptions({ details.open = !!localStorage.getItem(folderOpenLocalStorageKey); details.id = folderId; detailsList.push(details); - _li.appendChild(details); + li.appendChild(details); const summary = window.document.createElement("summary"); details.appendChild(summary); @@ -5312,8 +5328,8 @@ export function initOptions({ const supCount = window.document.createElement("sup"); - signals.createEffect(() => { - supCount.innerHTML = childOptionsCount().toLocaleString(); + signals.createEffect(childOptionsCount, (childOptionsCount) => { + supCount.innerHTML = childOptionsCount.toLocaleString(); }); spanName.append(supCount); @@ -5331,8 +5347,7 @@ export function initOptions({ }); function createRenderLiEffect() { - signals.createEffect(() => { - const count = childOptionsCount(); + signals.createEffect(childOptionsCount, (count) => { renderLi.set(!!count); }); } @@ -5395,7 +5410,7 @@ export function initOptions({ ); option.visited.set(!!localStorage.getItem(optionToVisitedKey(option))); - counters.createEffect(option); + counters.createEffects(option); list.push(option); optionsIds?.push(option.id); @@ -5403,57 +5418,62 @@ export function initOptions({ const hidden = signals.createSignal(true); function createHiddenEffect() { - signals.createEffect(() => { - switch (filter()) { - case "all": { - hidden.set(false); - break; + signals.createEffect( + () => ({ + filter: filter(), + favorite: option.isFavorite(), + visited: option.visited(), + }), + ({ filter, favorite, visited }) => { + switch (filter) { + case "all": { + hidden.set(false); + break; + } + case "favorites": { + hidden.set(!favorite); + break; + } + case "new": { + hidden.set(visited); + break; + } } - case "favorites": { - hidden.set(!option.isFavorite()); - break; - } - case "new": { - hidden.set(option.visited()); - break; - } - } - }); + }, + ); } createHiddenEffect(); function createRenderLiEffect() { - signals.createEffect(() => { - renderLi.set(!hidden()); + signals.createEffect(hidden, (hidden) => { + renderLi.set(!hidden); }); } createRenderLiEffect(); - signals.createEffect(() => { - const _li = li(); - - if (!_li) { + signals.createEffect(li, (li) => { + if (!li) { return; } - signals.createEffect(() => { - if (selected() === option) { - _li.dataset.highlight = ""; + signals.createEffect(selected, (selected) => { + if (selected === option) { + li.dataset.highlight = ""; } else { - delete _li.dataset.highlight; + delete li.dataset.highlight; } }); - signals.untrack(() => { - const label = createOptionLabeledInput({ - option, - frame: "folders", - }); - _li.append(label); + const label = createOptionLabeledInput({ + option, + frame: "folders", }); + + li.append(label); }); const memo = signals.createMemo(() => (hidden() ? 0 : 1)); + listForSum.push(memo); } }); diff --git a/website/scripts/packages/solid-signals/2024-10-28/script.js b/website/scripts/packages/solid-signals/2024-10-28/script.js index 02bb65516..1c7f14b5b 100644 --- a/website/scripts/packages/solid-signals/2024-10-28/script.js +++ b/website/scripts/packages/solid-signals/2024-10-28/script.js @@ -1,18 +1,13 @@ // src/error.ts -var NotReadyError = class extends Error { -}; +var NotReadyError = class extends Error {}; var NoOwnerError = class extends Error { constructor() { - super( - "" - ); + super(""); } }; var ContextNotFoundError = class extends Error { constructor() { - super( - "" - ); + super(""); } }; @@ -50,14 +45,12 @@ var Owner = class { h = defaultContext; f = null; constructor(signal = false) { - if (currentOwner && !signal) - currentOwner.append(this); + if (currentOwner && !signal) currentOwner.append(this); } append(child) { child.k = this; child.j = this; - if (this.g) - this.g.j = child; + if (this.g) this.g.j = child; child.g = this.g; this.g = child; if (child.h !== this.h) { @@ -68,9 +61,10 @@ var Owner = class { } } dispose(self = true) { - if (this.a === STATE_DISPOSED) - return; - let head = self ? this.j || this.k : this, current = this.g, next = null; + if (this.a === STATE_DISPOSED) return; + let head = self ? this.j || this.k : this, + current = this.g, + next = null; while (current && current.k === this) { current.dispose(true); current.n(); @@ -78,16 +72,12 @@ var Owner = class { current.g = null; current = next; } - if (self) - this.n(); - if (current) - current.j = !self ? this : this.j; - if (head) - head.g = current; + if (self) this.n(); + if (current) current.j = !self ? this : this.j; + if (head) head.g = current; } n() { - if (this.j) - this.j.g = null; + if (this.j) this.j.g = null; this.k = null; this.j = null; this.h = defaultContext; @@ -96,8 +86,7 @@ var Owner = class { this.emptyDisposal(); } emptyDisposal() { - if (!this.e) - return; + if (!this.e) return; if (Array.isArray(this.e)) { for (let i = 0; i < this.e.length; i++) { const callable = this.e[i]; @@ -109,9 +98,9 @@ var Owner = class { this.e = null; } handleError(error) { - if (!this.f) - throw error; - let i = 0, len = this.f.length; + if (!this.f) throw error; + let i = 0, + len = this.f.length; for (i = 0; i < len; i++) { try { this.f[i](error); @@ -120,8 +109,7 @@ var Owner = class { error = e; } } - if (i === len) - throw error; + if (i === len) throw error; } }; function createContext(defaultValue, description) { @@ -131,7 +119,9 @@ function getContext(context, owner = currentOwner) { if (!owner) { throw new NoOwnerError(); } - const value = hasContext(context, owner) ? owner.h[context.id] : context.defaultValue; + const value = hasContext(context, owner) + ? owner.h[context.id] + : context.defaultValue; if (isUndefined(value)) { throw new ContextNotFoundError(); } @@ -143,15 +133,14 @@ function setContext(context, value, owner = currentOwner) { } owner.h = { ...owner.h, - [context.id]: isUndefined(value) ? context.defaultValue : value + [context.id]: isUndefined(value) ? context.defaultValue : value, }; } function hasContext(context, owner = currentOwner) { return !isUndefined(owner?.h[context.id]); } function onCleanup(disposable) { - if (!currentOwner) - return; + if (!currentOwner) return; const node = currentOwner; if (!node.e) { node.e = disposable; @@ -176,12 +165,10 @@ var Computations = []; var RenderEffects = []; var Effects = []; function flushSync() { - if (!runningScheduled) - runScheduled(); + if (!runningScheduled) runScheduled(); } function flushQueue() { - if (scheduled) - return; + if (scheduled) return; scheduled = true; queueMicrotask(runScheduled); } @@ -193,8 +180,7 @@ function runTop(node) { } } for (let i = ancestors.length - 1; i >= 0; i--) { - if (ancestors[i].a !== STATE_DISPOSED) - ancestors[i].l(); + if (ancestors[i].a !== STATE_DISPOSED) ancestors[i].l(); } } function runScheduled() { @@ -222,8 +208,7 @@ function runScheduled() { } function runPureQueue(queue) { for (let i = 0; i < queue.length; i++) { - if (queue[i].a !== STATE_CLEAN) - runTop(queue[i]); + if (queue[i].a !== STATE_CLEAN) runTop(queue[i]); } } function runEffectQueue(queue) { @@ -274,16 +259,12 @@ var Computation2 = class extends Owner { this.s = compute2; this.a = compute2 ? STATE_DIRTY : STATE_CLEAN; this.d = initialValue; - if (options?.equals !== void 0) - this.t = options.equals; - if (options?.unobserved) - this.x = options?.unobserved; + if (options?.equals !== void 0) this.t = options.equals; + if (options?.unobserved) this.x = options?.unobserved; } y() { - if (this.s) - this.l(); - if (!this.b || this.b.length) - track(this); + if (this.s) this.l(); + if (!this.b || this.b.length) track(this); newFlags |= this.i & ~currentMask; if (this.i & ERROR_BIT) { throw this.d; @@ -336,11 +317,14 @@ var Computation2 = class extends Owner { } /** Update the computation with a new value. */ write(value, flags = 0, raw = false) { - const newValue = !raw && typeof value === "function" ? value(this.d) : value; - const valueChanged = newValue !== UNCHANGED && (!!(flags & ERROR_BIT) || this.t === false || !this.t(this.d, newValue)); - if (valueChanged) - this.d = newValue; - const changedFlagsMask = this.i ^ flags, changedFlags = changedFlagsMask & flags; + const newValue = + !raw && typeof value === "function" ? value(this.d) : value; + const valueChanged = + newValue !== UNCHANGED && + (!!(flags & ERROR_BIT) || this.t === false || !this.t(this.d, newValue)); + if (valueChanged) this.d = newValue; + const changedFlagsMask = this.i ^ flags, + changedFlags = changedFlagsMask & flags; this.i = flags; this.w = clock + 1; if (this.c) { @@ -358,8 +342,7 @@ var Computation2 = class extends Owner { * Set the current node's state, and recursively mark all of this node's observers as STATE_CHECK */ m(state) { - if (this.a >= state) - return; + if (this.a >= state) return; this.a = state; if (this.c) { for (let i = 0; i < this.c.length; i++) { @@ -374,17 +357,16 @@ var Computation2 = class extends Owner { * @param newFlags The source's new flags, masked to just the changed ones. */ z(mask, newFlags2) { - if (this.a >= STATE_DIRTY) - return; + if (this.a >= STATE_DIRTY) return; if (mask & this.p) { this.m(STATE_DIRTY); return; } - if (this.a >= STATE_CHECK) - return; + if (this.a >= STATE_CHECK) return; const prevFlags = this.i & mask; const deltaFlags = prevFlags ^ newFlags2; - if (newFlags2 === prevFlags) ; else if (deltaFlags & prevFlags & mask) { + if (newFlags2 === prevFlags); + else if (deltaFlags & prevFlags & mask) { this.m(STATE_CHECK); } else { this.i ^= deltaFlags; @@ -433,10 +415,8 @@ var Computation2 = class extends Owner { * Remove ourselves from the owner graph and the computation graph */ n() { - if (this.a === STATE_DISPOSED) - return; - if (this.b) - removeSourceObservers(this, 0); + if (this.a === STATE_DISPOSED) return; + if (this.b) removeSourceObservers(this, 0); super.n(); } }; @@ -450,7 +430,7 @@ function loadingState(node) { node.l(); return !!(node.i & LOADING_BIT); }, - options + options, ); computation.p = ERROR_BIT | LOADING_BIT; setOwner(prevOwner); @@ -466,7 +446,7 @@ function errorState(node) { node.l(); return !!(node.i & ERROR_BIT); }, - options + options, ); computation.p = ERROR_BIT; setOwner(prevOwner); @@ -474,10 +454,13 @@ function errorState(node) { } function track(computation) { if (currentObserver) { - if (!newSources && currentObserver.b && currentObserver.b[newSourcesIndex] === computation) { + if ( + !newSources && + currentObserver.b && + currentObserver.b[newSourcesIndex] === computation + ) { newSourcesIndex++; - } else if (!newSources) - newSources = [computation]; + } else if (!newSources) newSources = [computation]; else if (computation !== newSources[newSources.length - 1]) { newSources.push(computation); } @@ -487,7 +470,9 @@ function track(computation) { } } function update(node) { - const prevSources = newSources, prevSourcesIndex = newSourcesIndex, prevFlags = newFlags; + const prevSources = newSources, + prevSourcesIndex = newSourcesIndex, + prevFlags = newFlags; newSources = null; newSourcesIndex = 0; newFlags = 0; @@ -504,8 +489,7 @@ function update(node) { } } finally { if (newSources) { - if (node.b) - removeSourceObservers(node, newSourcesIndex); + if (node.b) removeSourceObservers(node, newSourcesIndex); if (node.b && newSourcesIndex > 0) { node.b.length = newSourcesIndex + newSources.length; for (let i = 0; i < newSources.length; i++) { @@ -517,10 +501,8 @@ function update(node) { let source; for (let i = newSourcesIndex; i < node.b.length; i++) { source = node.b[i]; - if (!source.c) - source.c = [node]; - else - source.c.push(node); + if (!source.c) source.c = [node]; + else source.c.push(node); } } else if (node.b && newSourcesIndex < node.b.length) { removeSourceObservers(node, newSourcesIndex); @@ -541,8 +523,7 @@ function removeSourceObservers(node, index) { swap = source.c.indexOf(node); source.c[swap] = source.c[source.c.length - 1]; source.c.pop(); - if (!source.c.length) - source.x?.(); + if (!source.c.length) source.x?.(); } } } @@ -550,8 +531,7 @@ function isEqual(a, b) { return a === b; } function untrack(fn) { - if (currentObserver === null) - return fn(); + if (currentObserver === null) return fn(); return compute(getOwner(), fn, null); } function hasUpdated(fn) { @@ -565,7 +545,9 @@ function hasUpdated(fn) { } } function compute(owner, compute2, observer) { - const prevOwner = setOwner(owner), prevObserver = currentObserver, prevMask = currentMask; + const prevOwner = setOwner(owner), + prevObserver = currentObserver, + prevMask = currentMask; currentObserver = observer; currentMask = observer?.p ?? DEFAULT_FLAGS; try { @@ -583,8 +565,7 @@ var EagerComputation = class extends Computation2 { Computations.push(this); } m(state) { - if (this.a >= state) - return; + if (this.a >= state) return; if (this.a === STATE_CLEAN) { Computations.push(this); flushQueue(); @@ -604,8 +585,7 @@ var BaseEffect = class extends Computation2 { this.o = initialValue; } write(value) { - if (value === UNCHANGED) - return this.d; + if (value === UNCHANGED) return this.d; this.d = value; this.q = true; return value; @@ -626,8 +606,7 @@ var Effect = class extends BaseEffect { flushQueue(); } m(state) { - if (this.a >= state) - return; + if (this.a >= state) return; if (this.a === STATE_CLEAN) { Effects.push(this); flushQueue(); @@ -642,8 +621,7 @@ var RenderEffect = class extends BaseEffect { RenderEffects.push(this); } m(state) { - if (this.a >= state) - return; + if (this.a >= state) return; if (this.a === STATE_CLEAN) { RenderEffects.push(this); flushQueue(); @@ -659,7 +637,7 @@ function createSignal(first, second, third) { const node2 = new Computation2( first(p ? untrack(p[0]) : second), null, - third + third, ); return [node2.read.bind(node2), node2.write.bind(node2)]; }); @@ -677,7 +655,7 @@ function createAsync(fn, initial, options) { return { wait() { return source; - } + }, }; } const signal = new Computation2(initial, null, options); @@ -689,16 +667,15 @@ function createAsync(fn, initial, options) { }, (error) => { signal.write(error, ERROR_BIT); - } + }, ); } else { let abort = false; - onCleanup(() => abort = true); + onCleanup(() => (abort = true)); (async () => { try { for await (let value of source) { - if (abort) - return; + if (abort) return; signal.write(value, 0); } } catch (error) { @@ -711,43 +688,28 @@ function createAsync(fn, initial, options) { return () => lhs.wait().wait(); } function createMemo(compute2, initialValue, options) { - let node = new Computation2( - initialValue, - compute2, - options - ); + let node = new Computation2(initialValue, compute2, options); let value; return () => { if (node) { value = node.wait(); - if (!node.b?.length) - node = void 0; + if (!node.b?.length) node = void 0; } return value; }; } function createEffect(compute2, effect, initialValue, options) { - void new Effect( - initialValue, - compute2, - effect, - void 0 - ); + void new Effect(initialValue, compute2, effect, void 0); } function createRenderEffect(compute2, effect, initialValue, options) { - void new RenderEffect( - initialValue, - compute2, - effect, - void 0 - ); + void new RenderEffect(initialValue, compute2, effect, void 0); } function createRoot(init) { const owner = new Owner(); return compute( owner, !init.length ? init : () => init(() => owner.dispose()), - null + null, ); } function runWithOwner(owner, run) { @@ -768,4 +730,33 @@ function catchError(fn, handler) { } } -export { Computation2 as Computation, ContextNotFoundError, Effect, NoOwnerError, NotReadyError, Owner, RenderEffect, catchError, compute, createAsync, createContext, createEffect, createMemo, createRenderEffect, createRoot, createSignal, flushSync, getContext, getObserver, getOwner, hasContext, hasUpdated, isEqual, onCleanup, runWithOwner, setContext, setOwner, untrack }; +export { + Computation2 as Computation, + ContextNotFoundError, + Effect, + NoOwnerError, + NotReadyError, + Owner, + RenderEffect, + catchError, + compute, + createAsync, + createContext, + createEffect, + createMemo, + createRenderEffect, + createRoot, + createSignal, + flushSync, + getContext, + getObserver, + getOwner, + hasContext, + hasUpdated, + isEqual, + onCleanup, + runWithOwner, + setContext, + setOwner, + untrack, +}; diff --git a/website/scripts/service-worker.js b/website/scripts/service-worker.js index 3af08c3b2..348171cb2 100644 --- a/website/scripts/service-worker.js +++ b/website/scripts/service-worker.js @@ -19,7 +19,7 @@ self.addEventListener("install", (_event) => { "/styles/chart.css", "/scripts/packages/lean-qr/v2.3.4/script.js", "/scripts/packages/lightweight-charts/v4.2.0/script.js", - "/scripts/packages/solid-signals/2024-04-17/script.js", + "/scripts/packages/solid-signals/2024-10-28/script.js", "/scripts/packages/ufuzzy/v1.0.14/script.js", ]); }), diff --git a/website/scripts/simulation.js b/website/scripts/simulation.js index 0f29eba2b..6affaa992 100644 --- a/website/scripts/simulation.js +++ b/website/scripts/simulation.js @@ -384,8 +384,8 @@ function appendInputDate({ id, title, value, parent, today, signals }) { input.min = "2011-01-01"; - signals.createEffect(() => { - input.max = today().toJSON().split("T")[0]; + signals.createEffect(today, (today) => { + input.max = today.toJSON().split("T")[0]; }); parent.append(input); diff --git a/website/scripts/types/self.d.ts b/website/scripts/types/self.d.ts index 39723ce95..95ec806df 100644 --- a/website/scripts/types/self.d.ts +++ b/website/scripts/types/self.d.ts @@ -1,7 +1,7 @@ import { Accessor, Setter, -} from "../packages/solid-signals/2024-04-17/types/signals"; +} from "../packages/solid-signals/2024-10-28/types/signals"; import { DeepPartial, BaselineStyleOptions, @@ -17,7 +17,8 @@ import { ISeriesApi, } from "../packages/lightweight-charts/v4.2.0/types"; import { DatePath, HeightPath, LastPath } from "./paths"; -import { Owner } from "../packages/solid-signals/2024-04-17/types/owner"; +import { Owner } from "../packages/solid-signals/2024-10-28/types/owner"; +import { AnyPossibleCohortId } from "../options"; type GrowToSize = A["length"] extends N ? A @@ -52,7 +53,7 @@ interface BaselineSpecificSeriesBlueprint { interface CandlestickSpecificSeriesBlueprint { type: "Candlestick"; - color?: undefined; + color?: Color; options?: DeepPartial; } @@ -80,6 +81,8 @@ type SeriesBlueprint = { formatNumber?: false; } & AnySpecificSeriesBlueprint; +type SeriesBluePrintType = NonNullable; + type Unit = | "" | "Bitcoin" @@ -289,3 +292,38 @@ declare global { MyNamespace: any; } } + +interface HoveredLegend { + label: HTMLLabelElement; + series: Series; +} + +type NotFunction = T extends Function ? never : T; + +type Groups = import("../options").Groups; + +type DefaultCohortOption = CohortOption; + +interface CohortOption { + scale: TimeScale; + name: string; + title: string; + datasetId: Id; + color: Color; + filenameAddon?: string; +} + +type DefaultCohortOptions = CohortOptions; + +interface CohortOptions { + scale: TimeScale; + name: string; + title: string; + list: CohortOption[]; +} + +interface SeriesBlueprintParam { + title: string; + singleColor?: Color; + genPath: (id: T, scale: TimeScale) => AnyDatasetPath; +}