general: snapshot

This commit is contained in:
k
2024-07-08 17:31:51 +02:00
parent 80ea12ed48
commit 04359fbf31
37 changed files with 787 additions and 322 deletions

View File

@@ -1,28 +0,0 @@
import { defaultSeriesOptions } from "./options";
type AreaOptions = DeepPartial<AreaStyleOptions & SeriesOptionsCommon>;
export const createAreaSeries = (
chart: IChartApi,
options?: AreaOptions & {
color?: string;
},
) => {
const { color } = options || {};
// const fillColor = `${color}11`;
const fillColor = color;
const seriesOptions: AreaOptions = {
// priceScaleId: 'left',
...defaultSeriesOptions,
lineColor: color,
topColor: fillColor,
bottomColor: fillColor,
...options,
};
const series = chart.addAreaSeries(seriesOptions);
return series;
};

View File

@@ -10,43 +10,51 @@ export const DEFAULT_BASELINE_COLORS = [
DEFAULT_BASELINE_BOTTOM_COLOR,
];
export const createBaseLineSeries = (
chart: IChartApi,
options: BaselineSeriesOptions,
) => {
const {
title,
color,
topColor,
topLineColor,
bottomColor,
bottomLineColor,
base,
lineColor,
} = options;
const transparent = `transparent`;
const allTopColor = topColor || color || DEFAULT_BASELINE_TOP_COLOR;
const topFillColor = `transparent`;
const allBottomColor = bottomColor || color || DEFAULT_BASELINE_BOTTOM_COLOR;
const bottomFillColor = `transparent`;
export const createBaseLineSeries = ({
chart,
dark,
color,
topColor,
bottomColor,
options,
}: {
chart: IChartApi;
dark: Accessor<boolean>;
color?: Color;
topColor?: Color;
bottomColor?: Color;
options?: DeepPartialBaselineOptions & {
base?: number;
};
}) => {
const topLineColor = topColor || color || DEFAULT_BASELINE_TOP_COLOR;
const bottomLineColor = bottomColor || color || DEFAULT_BASELINE_BOTTOM_COLOR;
const seriesOptions: DeepPartialBaselineOptions = {
priceScaleId: "right",
...defaultSeriesOptions,
// lineWidth: 1,
...options,
...options.options,
...(base ? { baseValue: { type: "price", price: base } } : {}),
topLineColor: topLineColor || lineColor || allTopColor,
topFillColor1: topFillColor,
topFillColor2: topFillColor,
bottomLineColor: bottomLineColor || lineColor || allBottomColor,
bottomFillColor1: bottomFillColor,
bottomFillColor2: bottomFillColor,
title,
...(options?.base
? { baseValue: { type: "price", price: options?.base } }
: {}),
topFillColor1: transparent,
topFillColor2: transparent,
bottomFillColor1: transparent,
bottomFillColor2: transparent,
};
const series = chart.addBaselineSeries(seriesOptions);
createEffect(() => {
series.applyOptions({
topLineColor: topLineColor(dark),
bottomLineColor: bottomLineColor(dark),
});
});
return series;
};

View File

@@ -1,21 +1,18 @@
import { colors } from "/src/scripts/utils/colors";
export const createCandlesticksSeries = (
chart: IChartApi,
options: PriceSeriesOptions = {},
): [ISeriesApi<"Candlestick">, string[]] => {
export const createCandlesticksSeries = ({
chart,
dark,
options = {},
}: {
chart: IChartApi;
dark: Accessor<boolean>;
options?: PriceSeriesOptions;
}): [ISeriesApi<"Candlestick">, Color[]] => {
const { inverseColors } = options;
const upColor = inverseColors ? colors.loss : colors.profit;
const downColor = inverseColors ? colors.profit : colors.loss;
const candlestickSeries = chart.addCandlestickSeries({
baseLineVisible: false,
upColor,
wickUpColor: upColor,
downColor,
wickDownColor: downColor,
borderVisible: false,
priceLineVisible: false,
baseLineColor: "",
@@ -25,5 +22,22 @@ export const createCandlesticksSeries = (
...options.seriesOptions,
});
return [candlestickSeries, [upColor, downColor]];
const _upColor = inverseColors ? colors.loss : colors.profit;
const _downColor = inverseColors ? colors.profit : colors.loss;
createEffect(() => {
const upColor = _upColor(dark);
const downColor = _downColor(dark);
candlestickSeries.applyOptions({
upColor,
wickUpColor: upColor,
downColor,
wickDownColor: downColor,
});
});
return [candlestickSeries, [_upColor, _downColor]];
};

View File

@@ -15,34 +15,24 @@ export function createChart(
dark,
priceScaleOptions,
}: {
dark: boolean;
dark: Accessor<boolean>;
priceScaleOptions: DeepPartialPriceScaleOptions;
},
) {
console.log(`chart: create (scale: ${scale})`);
const { white, black } = colors;
const textColor = dark ? white : black;
const borderColor = dark ? "#332F24" : "#F1E4E0";
const options: DeepPartialChartOptions = {
autoSize: true,
layout: {
fontFamily: "Lexend",
background: { color: "transparent" },
fontSize: 14,
textColor,
},
grid: {
vertLines: { visible: false },
horzLines: { visible: false },
},
rightPriceScale: {
borderColor,
},
timeScale: {
borderColor,
minBarSpacing: 0.05,
shiftVisibleRangeOnNewBar: false,
allowShiftVisibleRangeOnWhitespaceReplacement: false,
@@ -54,14 +44,6 @@ export function createChart(
},
crosshair: {
mode: CrosshairMode.Normal,
horzLine: {
color: textColor,
labelBackgroundColor: textColor,
},
vertLine: {
color: textColor,
labelBackgroundColor: textColor,
},
},
localization: {
priceFormatter: valueToString,
@@ -90,5 +72,34 @@ export function createChart(
minimumWidth: 78,
});
createEffect(() => {
const { white } = colors;
const textColor = white(dark);
const borderColor = dark() ? "#332F24" : "#F1E4E0";
chart.applyOptions({
layout: {
textColor,
},
rightPriceScale: {
borderColor,
},
timeScale: {
borderColor,
},
crosshair: {
horzLine: {
color: textColor,
labelBackgroundColor: textColor,
},
vertLine: {
color: textColor,
labelBackgroundColor: textColor,
},
},
});
});
return chart;
}

View File

@@ -6,10 +6,17 @@ type HistogramOptions = DeepPartial<
export const PRICE_SCALE_MOMENTUM_ID = "momentum";
export const createHistogramSeries = (
chart: IChartApi,
options?: HistogramOptions,
) => {
export const createHistogramSeries = ({
chart,
// dark,
// color,
options,
}: {
chart: IChartApi;
// dark: Accessor<boolean>;
// color: Color;
options?: HistogramOptions;
}) => {
const seriesOptions: HistogramOptions = {
priceScaleId: "left",
...defaultSeriesOptions,

View File

@@ -9,6 +9,7 @@ export class HorzScaleBehaviorHeight implements IHorzScaleBehavior<number> {
setOptions() {}
preprocessData() {}
updateFormatter() {}
createConverterToInternalObj() {
return (price) => price;
}

View File

@@ -22,7 +22,7 @@ export function createSeriesLegend<Scale extends ResourceScale>({
id: string;
presetId: string;
title: string;
color: Accessor<string | string[]>;
color: Color | Color[];
seriesList: Accessor<ISeriesApi<SeriesType> | undefined>[];
defaultVisible?: boolean;
disabled?: Accessor<boolean>;

View File

@@ -1,10 +1,26 @@
import { defaultSeriesOptions } from "./options";
export const createLineSeries = (
chart: IChartApi,
options?: DeepPartialLineOptions,
) =>
chart.addLineSeries({
export const createLineSeries = ({
chart,
dark,
color,
options,
}: {
chart: IChartApi;
dark: Accessor<boolean>;
color: Color;
options?: DeepPartialLineOptions;
}) => {
const series = chart.addLineSeries({
...defaultSeriesOptions,
...options,
});
createEffect(() => {
series.applyOptions({
color: color(dark),
});
});
return series;
};

View File

@@ -6,12 +6,12 @@ export function setMinMaxMarkers({
scale,
visibleRange,
legendList,
activeRange,
activeIds,
}: {
scale: ResourceScale;
visibleRange: TimeRange | undefined;
legendList: SeriesLegend[];
activeRange: Accessor<number[]>;
activeIds: Accessor<number[]>;
}) {
if (!visibleRange) return;
@@ -24,7 +24,7 @@ export function setMinMaxMarkers({
let min = undefined as [number, Time, number, ISeriesApi<any>] | undefined;
legendList.forEach(({ seriesList, dataset }) => {
activeRange().forEach((id) => {
activeIds().forEach((id) => {
const seriesIndex = chunkIdToIndex(scale, id);
const series = seriesList.at(seriesIndex)?.();

View File

@@ -1,48 +1,13 @@
import { HEIGHT_CHUNK_SIZE } from "../datasets";
import { debounce } from "../utils/debounce";
import { tick } from "../utils/tick";
import { writeURLParam } from "../utils/urlParams";
const LOCAL_STORAGE_RANGE_KEY = "chart-range";
const URL_PARAMS_RANGE_FROM_KEY = "from";
const URL_PARAMS_RANGE_TO_KEY = "to";
export function initTimeScale({
scale,
activeRange,
exactRange,
charts,
}: {
scale: ResourceScale;
activeRange: RWS<number[]>;
exactRange: RWS<TimeRange | undefined>;
charts: ChartObject[];
}) {
const firstChart = charts.at(0)?.chart;
if (!firstChart) return;
firstChart.timeScale().subscribeVisibleTimeRangeChange((range) => {
if (!range) return;
exactRange.set(range);
debouncedSetActiveRange({ range, activeRange });
debouncedSaveTimeRange({ scale, range });
});
setTimeScale(firstChart, getInitialRange(scale));
}
function setTimeScale(chart: IChartApi, range: TimeRange | null) {
if (range) {
setTimeout(() => {
chart.timeScale().setVisibleRange(range);
}, 1);
}
}
function getInitialRange(scale: ResourceScale): TimeRange {
export function getInitialTimeRange(scale: ResourceScale): TimeRange {
const urlParams = new URLSearchParams(window.location.search);
const urlFrom = urlParams.get(URL_PARAMS_RANGE_FROM_KEY);
@@ -94,38 +59,76 @@ function getInitialRange(scale: ResourceScale): TimeRange {
}
}
export function initTimeScale({
scale,
activeIds,
exactRange,
charts,
}: {
scale: ResourceScale;
activeIds: RWS<number[]>;
exactRange: RWS<TimeRange>;
charts: ChartObject[];
}) {
const firstChart = charts.at(0)?.chart;
if (!firstChart) return;
firstChart.timeScale().subscribeVisibleTimeRangeChange((range) => {
if (!range) return;
exactRange.set(range);
debouncedsetActiveIds({ exactRange: range, activeIds: activeIds });
debouncedSaveTimeRange({ scale, range });
});
setTimeScale(firstChart, exactRange());
}
async function setTimeScale(chart: IChartApi, range: TimeRange | null) {
if (range) {
await tick();
chart.timeScale().setVisibleRange(range);
}
}
function getLocalStorageKey(scale: ResourceScale) {
return `${LOCAL_STORAGE_RANGE_KEY}-${scale}`;
}
function setActiveRange({
range,
activeRange,
export function setActiveIds({
exactRange,
activeIds,
}: {
range: TimeRange;
activeRange: RWS<number[]>;
exactRange: TimeRange;
activeIds: RWS<number[]>;
}) {
let ids: number[] = [];
const today = new Date();
if (typeof range.from === "string" && typeof range.to === "string") {
const from = new Date(range.from).getUTCFullYear();
const to = new Date(range.to).getUTCFullYear();
if (
typeof exactRange.from === "string" &&
typeof exactRange.to === "string"
) {
const from = new Date(exactRange.from).getUTCFullYear();
const to = new Date(exactRange.to).getUTCFullYear();
ids = Array.from({ length: to - from + 1 }, (_, i) => i + from).filter(
(year) => year >= 2009 && year <= today.getUTCFullYear(),
);
} else {
const from = Math.floor(Number(range.from) / HEIGHT_CHUNK_SIZE);
const to = Math.floor(Number(range.to) / HEIGHT_CHUNK_SIZE);
const from = Math.floor(Number(exactRange.from) / HEIGHT_CHUNK_SIZE);
const to = Math.floor(Number(exactRange.to) / HEIGHT_CHUNK_SIZE);
const length = to - from + 1;
ids = Array.from({ length }, (_, i) => (from + i) * HEIGHT_CHUNK_SIZE);
}
const old = activeRange();
const old = activeIds();
if (
old.length !== ids.length ||
@@ -134,11 +137,11 @@ function setActiveRange({
) {
console.log("range:", ids);
activeRange.set(ids);
activeIds.set(ids);
}
}
const debouncedSetActiveRange = debounce(setActiveRange, 100);
const debouncedsetActiveIds = debounce(setActiveIds, 100);
function saveTimeRange({
scale,

View File

@@ -19,6 +19,4 @@ interface BaselineSeriesOptions {
title?: string;
}
type SeriesLegend = ReturnType<
typeof import("../../chart/legend").createSeriesLegend
>;
type SeriesLegend = ReturnType<typeof import("./legend").createSeriesLegend>;

View File

@@ -4,9 +4,9 @@ import { createLineSeries } from "./line";
export const GENESIS_DAY = "2009-01-03";
const whitespaceStartDate = new Date("1970-01-01");
const whitespaceStartDateYear = whitespaceStartDate.getFullYear();
const whitespaceStartDateMonth = whitespaceStartDate.getMonth();
const whitespaceStartDateDate = whitespaceStartDate.getDate();
const whitespaceStartDateYear = whitespaceStartDate.getUTCFullYear();
const whitespaceStartDateMonth = whitespaceStartDate.getUTCMonth();
const whitespaceStartDateDate = whitespaceStartDate.getUTCDate();
const whitespaceEndDate = new Date("2141-01-01");
const whitespaceDateDataset: (WhitespaceData | SingleValueData)[] = new Array(
getNumberOfDaysBetweenTwoDates(whitespaceStartDate, whitespaceEndDate),
@@ -47,7 +47,7 @@ for (let i = 0; i < whitespaceHeightDataset.length; i++) {
}
export function setWhitespace(chart: IChartApi, scale: ResourceScale) {
const whitespace = createLineSeries(chart);
const whitespace = chart.addLineSeries();
if (scale === "date") {
whitespace.setData(whitespaceDateDataset);
@@ -63,3 +63,81 @@ export function setWhitespace(chart: IChartApi, scale: ResourceScale) {
return whitespace;
}
// ---
// import { HEIGHT_CHUNK_SIZE } from "../datasets";
// import { dateToString } from "../utils/date";
// export const GENESIS_DAY = "2009-01-03";
// function leapYear(year: number) {
// return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
// }
// const whitespaceStartDate = new Date("1970-01-01");
// export const whitespaceStartDateYear = whitespaceStartDate.getFullYear();
// const whitespaceStartDateMonth = whitespaceStartDate.getMonth();
// const whitespaceStartDateDate = whitespaceStartDate.getDate();
// const whitespaceEndDate = new Date("2141-01-01");
// const whitespaceEndDateYear = whitespaceEndDate.getFullYear();
// export const whitespaceDateDatasets: (WhitespaceData | SingleValueData)[][] =
// Array.from(
// { length: whitespaceEndDateYear - whitespaceStartDateYear },
// (_, i) => new Array(leapYear(whitespaceStartDateYear + i) ? 366 : 365),
// );
// for (let i = 0; i < whitespaceDateDatasets.length; i++) {
// const year = whitespaceStartDateYear + i;
// const whitespaceDateDataset = whitespaceDateDatasets[i];
// // Hack to be able to scroll freely
// // Setting them all to NaN is much slower
// for (let j = 0; j < whitespaceDateDataset.length; j++) {
// const date = new Date(
// year,
// whitespaceStartDateMonth,
// whitespaceStartDateDate + j,
// );
// const time = dateToString(date);
// if (j === whitespaceDateDataset.length - 1) {
// whitespaceDateDataset[j] = {
// time,
// value: NaN,
// };
// } else {
// whitespaceDateDataset[j] = {
// time,
// };
// }
// }
// }
// export const whitespaceHeightStart = -50_000;
// export const whitespaceHeightDatasets: (WhitespaceData | SingleValueData)[][] =
// Array.from(
// { length: (new Date().getUTCFullYear() - 2009 + 1) * 6 },
// () => new Array(HEIGHT_CHUNK_SIZE),
// );
// for (let i = 0; i < whitespaceHeightDatasets.length; i++) {
// const offset = HEIGHT_CHUNK_SIZE * i;
// const whitespaceHeightDataset = whitespaceHeightDatasets[i];
// for (let j = 0; j < whitespaceHeightDataset.length; j++) {
// const height = whitespaceHeightStart + offset + j;
// if (j === whitespaceHeightDataset.length - 1) {
// whitespaceHeightDataset[j] = {
// time: height as any,
// value: NaN,
// };
// } else {
// whitespaceHeightDataset[j] = {
// time: height as any,
// };
// }
// }
// }

View File

@@ -127,7 +127,7 @@ function createAddressPresetFolder<Scale extends ResourceScale>({
scale: Scale;
name: string;
datasetKey: AddressCohortKey;
color: string;
color: Color;
}): PartialPresetFolder {
return {
name,
@@ -159,7 +159,7 @@ export function createLiquidityFolder<Scale extends ResourceScale>({
scale: Scale;
name: string;
datasetKey: AddressCohortKey | "";
color: string;
color: Color;
}): PartialPresetFolder {
return {
name: `Split By Liquidity`,
@@ -189,7 +189,7 @@ export function createAddressCountPreset<Scale extends ResourceScale>({
scale: Scale;
name: string;
datasetKey: AddressCohortKey;
color: string;
color: Color;
}): PartialPreset {
return {
scale,

View File

@@ -11,7 +11,11 @@ import { createHistogramSeries } from "../lightweightCharts/histogram";
import { createSeriesLegend } from "../lightweightCharts/legend";
import { createLineSeries } from "../lightweightCharts/line";
import { setMinMaxMarkers } from "../lightweightCharts/markers";
import { initTimeScale } from "../lightweightCharts/time";
import {
getInitialTimeRange,
initTimeScale,
setActiveIds,
} from "../lightweightCharts/time";
import { setWhitespace } from "../lightweightCharts/whitespace";
import { colors } from "../utils/colors";
import { debounce } from "../utils/debounce";
@@ -27,7 +31,9 @@ export enum SeriesType {
type SeriesConfig<Scale extends ResourceScale> =
| {
dataset: ResourceDataset<Scale>;
color?: string;
color?: Color;
topColor?: Color;
bottomColor?: Color;
colors?: undefined;
seriesType: SeriesType.Based;
title: string;
@@ -37,8 +43,8 @@ type SeriesConfig<Scale extends ResourceScale> =
}
| {
dataset: ResourceDataset<Scale>;
color?: string;
colors?: string[];
color?: Color;
colors?: Color[];
seriesType: SeriesType.Histogram;
title: string;
options?: DeepPartialHistogramOptions;
@@ -57,7 +63,7 @@ type SeriesConfig<Scale extends ResourceScale> =
}
| {
dataset: ResourceDataset<Scale>;
color: string;
color: Color;
colors?: undefined;
seriesType?: SeriesType.Line;
title: string;
@@ -78,7 +84,7 @@ export function applySeriesList<Scale extends ResourceScale>({
priceOptions,
legendSetter,
dark,
activeRange,
activeIds,
}: {
charts: RWS<IChartApi[]>;
parentDiv: HTMLDivElement;
@@ -90,8 +96,8 @@ export function applySeriesList<Scale extends ResourceScale>({
top?: SeriesConfig<Scale>[];
bottom?: SeriesConfig<Scale>[];
datasets: Datasets;
dark: boolean;
activeRange: RWS<number[]>;
dark: Accessor<boolean>;
activeIds: RWS<number[]>;
}) {
// ---
// Reset states
@@ -107,8 +113,6 @@ export function applySeriesList<Scale extends ResourceScale>({
return [];
});
activeRange.set([]);
parentDiv.replaceChildren();
// ---
@@ -124,11 +128,18 @@ export function applySeriesList<Scale extends ResourceScale>({
const activeDatasets: ResourceDataset<any, any>[] = [];
const lastActiveIndex = createMemo(() => {
const last = activeRange().at(-1);
const last = activeIds().at(-1);
return last !== undefined ? chunkIdToIndex(scale, last) : undefined;
});
const exactRange = createRWS(undefined as TimeRange | undefined);
const exactRange = createRWS(getInitialTimeRange(scale));
setActiveIds({
exactRange: exactRange(),
activeIds: activeIds,
});
const seriesNumber = 1 + (top || []).length + (bottom || []).length;
const charts = [top || [], bottom]
.flatMap((list) => (list ? [list] : []))
@@ -157,16 +168,74 @@ export function applySeriesList<Scale extends ResourceScale>({
const whitespace = setWhitespace(chart, scale);
if (exactRange()) {
chart.timeScale().setVisibleRange(exactRange());
}
// const whitespace = new Array<ISeriesApi<"Line"> | undefined>(
// scale === "date"
// ? whitespaceDateDatasets.length
// : whitespaceHeightDatasets.length,
// ).fill(undefined);
// function createWhitespaceSeriesIfNeeded(index: number) {
// console.log(index);
// if (index >= 0 && index < whitespace.length && !whitespace[index]) {
// const series = createLineSeries(chart);
// whitespace[index] = series;
// if (scale === "date") {
// series.setData(whitespaceDateDatasets[index]);
// } else {
// series.setData(whitespaceHeightDatasets[index]);
// }
// }
// }
// createEffect(() => {
// const ids = activeIds();
// console.log(ids);
// const idsLength = ids.length;
// for (let i = 0; i < idsLength; i++) {
// const id = ids[i];
// const whitespaceIndex = chunkIdToIndex(
// scale,
// scale === "date"
// ? id - whitespaceStartDateYear
// : id - whitespaceHeightStart,
// );
// if (i === 0) {
// createWhitespaceSeriesIfNeeded(whitespaceIndex - 1);
// }
// createWhitespaceSeriesIfNeeded(whitespaceIndex);
// if (i === idsLength - 1) {
// createWhitespaceSeriesIfNeeded(whitespaceIndex + 1);
// }
// }
// });
const chartLegend: SeriesLegend[] = [];
const debouncedSetMinMaxMarkers = debounce(() => {
function _setMinMaxMarkers() {
setMinMaxMarkers({
scale,
visibleRange: exactRange(),
legendList: chartLegend,
activeRange,
activeIds: activeIds,
});
}, 50);
}
const debouncedSetMinMaxMarkers = debounce(
_setMinMaxMarkers,
seriesNumber * 10,
);
createEffect(on(exactRange, debouncedSetMinMaxMarkers));
if (index === 0) {
const dataset =
@@ -206,7 +275,7 @@ export function applySeriesList<Scale extends ResourceScale>({
}
return createSeriesGroup({
activeRange,
activeIds,
seriesConfig,
chart,
chartLegend,
@@ -214,6 +283,7 @@ export function applySeriesList<Scale extends ResourceScale>({
preset,
disabled: () => priceSeriesType() !== seriesType,
debouncedSetMinMaxMarkers,
dark,
});
}
@@ -233,13 +303,14 @@ export function applySeriesList<Scale extends ResourceScale>({
activeDatasets.push(seriesConfig.dataset);
createSeriesGroup({
activeRange,
activeIds: activeIds,
seriesConfig,
chartLegend,
chart,
preset,
lastActiveIndex,
debouncedSetMinMaxMarkers,
dark,
});
});
@@ -249,8 +320,6 @@ export function applySeriesList<Scale extends ResourceScale>({
createEffect(on(legend.visible, debouncedSetMinMaxMarkers));
});
createEffect(on(exactRange, debouncedSetMinMaxMarkers));
return [
{
scale,
@@ -271,7 +340,8 @@ export function applySeriesList<Scale extends ResourceScale>({
chart.div.style.border = "";
visibleCharts.push(chart);
} else {
chart.div.style.height = "0px";
chart.div.style.height = "100%";
// chart.div.style.height = "0px";
chart.div.style.border = "none";
}
});
@@ -296,13 +366,13 @@ export function applySeriesList<Scale extends ResourceScale>({
initTimeScale({
scale,
charts,
activeRange,
activeIds: activeIds,
exactRange,
});
const activeDatasetsLength = activeDatasets.length;
createEffect(() => {
const range = activeRange();
const range = activeIds();
untrack(() => {
for (let i = 0; i < range.length; i++) {
@@ -382,7 +452,7 @@ function updateVisiblePriceSeriesType(
}
function createSeriesGroup<Scale extends ResourceScale>({
activeRange,
activeIds,
seriesConfig,
preset,
chartLegend,
@@ -390,8 +460,9 @@ function createSeriesGroup<Scale extends ResourceScale>({
disabled,
lastActiveIndex,
debouncedSetMinMaxMarkers,
dark,
}: {
activeRange: Accessor<number[]>;
activeIds: Accessor<number[]>;
seriesConfig: SeriesConfig<Scale>;
preset: Preset;
chart: IChartApi;
@@ -399,6 +470,7 @@ function createSeriesGroup<Scale extends ResourceScale>({
lastActiveIndex: Accessor<number | undefined>;
disabled?: Accessor<boolean>;
debouncedSetMinMaxMarkers: VoidFunction;
dark: Accessor<boolean>;
}) {
const {
dataset,
@@ -417,15 +489,12 @@ function createSeriesGroup<Scale extends ResourceScale>({
ISeriesApi<"Baseline" | "Line" | "Histogram" | "Candlestick"> | undefined
>[] = new Array(dataset.fetchedJSONs.length);
let defaultSeriesColor: string | string[] | undefined = undefined;
const legend = createSeriesLegend({
id: stringToId(title),
presetId: preset.id,
title,
seriesList,
color: () =>
colors || color || defaultSeriesColor || DEFAULT_BASELINE_COLORS,
color: colors || color || DEFAULT_BASELINE_COLORS,
defaultVisible,
disabled,
dataset,
@@ -448,37 +517,47 @@ function createSeriesGroup<Scale extends ResourceScale>({
if (!s) {
switch (type) {
case SeriesType.Based: {
s = createBaseLineSeries(chart, {
s = createBaseLineSeries({
chart,
dark,
color,
...options,
topColor: seriesConfig.topColor,
bottomColor: seriesConfig.bottomColor,
options,
});
break;
}
case SeriesType.Candlestick: {
const candlestickSeries = createCandlesticksSeries(
const candlestickSeries = createCandlesticksSeries({
chart,
options,
);
dark,
});
s = candlestickSeries[0];
defaultSeriesColor = candlestickSeries[1];
if (!colors && !color) {
legend.color = candlestickSeries[1];
}
break;
}
case SeriesType.Histogram: {
s = createHistogramSeries(chart, {
color,
...options,
s = createHistogramSeries({
chart,
options,
});
break;
}
default:
case SeriesType.Line: {
s = createLineSeries(chart, {
s = createLineSeries({
chart,
color,
...options,
dark,
options,
});
break;
@@ -494,9 +573,7 @@ function createSeriesGroup<Scale extends ResourceScale>({
s.setData(values);
untrack(() => {
debouncedSetMinMaxMarkers();
});
debouncedSetMinMaxMarkers();
});
});
@@ -522,7 +599,7 @@ function createSeriesGroup<Scale extends ResourceScale>({
});
const inRange = createMemo(() => {
const range = activeRange();
const range = activeIds();
if (range.length) {
const start = chunkIdToIndex(scale, range.at(0)!);

View File

@@ -540,7 +540,7 @@ export function createPresets<Scale extends ResourceScale>({
bottom: [
{
title: "Concurrent Liveliness 14d Median",
color: `${colors.liveliness}66`,
color: colors.darkLiveliness,
dataset:
params.datasets[scale].concurrent_liveliness_2w_median,
},
@@ -732,13 +732,13 @@ export function createPresets<Scale extends ResourceScale>({
bottom: [
{
title: "Active Supply Net Change",
color: `${colors.liveliness}80`,
color: colors.liveliness,
dataset: params.datasets[scale].active_supply_3m_net_change,
seriesType: SeriesType.Based,
},
{
title: "Vaulted Supply Net Change",
color: `${colors.vaultedPrice}80`,
color: colors.vaultedPrice,
seriesType: SeriesType.Based,
dataset:
params.datasets[scale].vaulted_supply_3m_net_change,

View File

@@ -1,3 +1,4 @@
import { phone } from "/src/env";
import { createRWS } from "/src/solid/rws";
import { colors } from "../utils/colors";
@@ -32,7 +33,7 @@ export function createPresets(): Presets {
name: "Charts",
tree: [
{
name: "By Date",
name: "By Block Date",
tree: [
createMarketPresets("date"),
createBlocksPresets(),
@@ -57,28 +58,30 @@ export function createPresets(): Presets {
],
} satisfies PartialPresetFolder,
{
name: "By Height",
tree: [
createMarketPresets("height"),
createMinersPresets("height"),
createTransactionsPresets("height"),
...createCohortPresetList({
scale: "height",
color: colors.bitcoin,
name: "",
datasetKey: "",
title: "",
}),
createLiquidityFolder({
scale: "height",
color: colors.bitcoin,
datasetKey: "",
name: "",
}),
createHodlersPresets({ scale: "height" }),
createAddressesPresets({ scale: "height" }),
createCoinblocksPresets({ scale: "height" }),
],
name: "By Block Height - Desktop/Tablet Only",
tree: !phone
? [
createMarketPresets("height"),
createMinersPresets("height"),
createTransactionsPresets("height"),
...createCohortPresetList({
scale: "height",
color: colors.bitcoin,
name: "",
datasetKey: "",
title: "",
}),
createLiquidityFolder({
scale: "height",
color: colors.bitcoin,
datasetKey: "",
name: "",
}),
createHodlersPresets({ scale: "height" }),
createAddressesPresets({ scale: "height" }),
createCoinblocksPresets({ scale: "height" }),
]
: [],
} satisfies PartialPresetFolder,
],
},
@@ -263,7 +266,14 @@ function checkIfDuplicateIds(ids: string[]) {
}
function findInitialPreset(presets: Preset[]): Preset {
const urlPreset = document.location.pathname.substring(1);
let urlPreset = document.location.pathname.substring(1);
if (phone && urlPreset.startsWith("height" satisfies ResourceScale)) {
urlPreset = urlPreset.replace(
"height" satisfies ResourceScale,
"date" satisfies ResourceScale,
);
}
return (
(urlPreset &&

View File

@@ -45,7 +45,7 @@ function createPresetFolder({
key,
}: {
scale: ResourceScale;
color: string;
color: Color;
name: string;
key: AverageName;
}) {

View File

@@ -12,7 +12,7 @@ export function createCohortPresetFolder<Scale extends ResourceScale>({
scale: Scale;
name: string;
datasetKey: AnyPossibleCohortKey;
color: string;
color: Color;
title: string;
}) {
return {
@@ -38,7 +38,7 @@ export function createCohortPresetList<Scale extends ResourceScale>({
scale: Scale;
datasetKey: AnyPossibleCohortKey;
title: string;
color: string;
color: Color;
}) {
const datasetPrefix = datasetKey
? (`${datasetKey}_` as const)

View File

@@ -25,8 +25,8 @@ type ApplyPreset = (params: {
datasets: Datasets;
preset: Preset;
legendSetter: Setter<SeriesLegend[]>;
dark: boolean;
activeRange: RWS<number[]>;
dark: Accessor<boolean>;
activeIds: RWS<number[]>;
}) => void;
interface PartialPresetFolder {

View File

@@ -41,48 +41,129 @@ import {
// DO NOT USE TRANSPARENCY HERE
// ---
const lightRed = redTailwind[300];
const red = redTailwind[500];
const darkRed = redTailwind[900];
const orange = orangeTailwind[500];
const darkOrange = orangeTailwind[900];
const amber = amberTailwind[500];
const darkAmber = amberTailwind[900];
const yellow = yellowTailwind[500];
const darkYellow = yellowTailwind[500];
const lime = limeTailwind[500];
const darkLime = limeTailwind[900];
const green = greenTailwind[500];
const darkGreen = greenTailwind[900];
const lightEmerald = emeraldTailwind[300];
const emerald = emeraldTailwind[500];
const darkEmerald = emeraldTailwind[900];
const teal = tealTailwind[500];
const darkTeal = tealTailwind[900];
const cyan = cyanTailwind[500];
const darkCyan = cyanTailwind[900];
const sky = skyTailwind[500];
const darkSky = skyTailwind[900];
const blue = blueTailwind[500];
const darkBlue = blueTailwind[900];
const indigo = indigoTailwind[500];
const darkIndigo = indigoTailwind[900];
const violet = violetTailwind[500];
const darkViolet = violetTailwind[900];
const purple = purpleTailwind[500];
const darkPurple = purpleTailwind[900];
const fuchsia = fuchsiaTailwind[500];
const darkFuchsia = fuchsiaTailwind[900];
const pink = pinkTailwind[500];
const darkPink = pinkTailwind[900];
const rose = roseTailwind[500];
const darkRose = roseTailwind[900];
function lightRed(dark: Accessor<boolean>) {
return dark() ? redTailwind[300] : redTailwind[800];
}
function red(dark: Accessor<boolean>) {
return dark() ? redTailwind[500] : redTailwind[600];
}
function darkRed(dark: Accessor<boolean>) {
return dark() ? redTailwind[900] : redTailwind[100];
}
function orange(dark: Accessor<boolean>) {
return dark() ? orangeTailwind[500] : orangeTailwind[600];
}
function darkOrange(dark: Accessor<boolean>) {
return dark() ? orangeTailwind[900] : orangeTailwind[100];
}
function amber(dark: Accessor<boolean>) {
return dark() ? amberTailwind[500] : amberTailwind[600];
}
function darkAmber(dark: Accessor<boolean>) {
return dark() ? amberTailwind[900] : amberTailwind[100];
}
function yellow(dark: Accessor<boolean>) {
return dark() ? yellowTailwind[500] : yellowTailwind[600];
}
function darkYellow(dark: Accessor<boolean>) {
return dark() ? yellowTailwind[500] : yellowTailwind[600];
}
function lime(dark: Accessor<boolean>) {
return dark() ? limeTailwind[500] : limeTailwind[600];
}
function darkLime(dark: Accessor<boolean>) {
return dark() ? limeTailwind[900] : limeTailwind[100];
}
function green(dark: Accessor<boolean>) {
return dark() ? greenTailwind[500] : greenTailwind[600];
}
function darkGreen(dark: Accessor<boolean>) {
return dark() ? greenTailwind[900] : greenTailwind[100];
}
function lightEmerald(dark: Accessor<boolean>) {
return dark() ? emeraldTailwind[300] : emeraldTailwind[800];
}
function emerald(dark: Accessor<boolean>) {
return dark() ? emeraldTailwind[500] : emeraldTailwind[600];
}
function darkEmerald(dark: Accessor<boolean>) {
return dark() ? emeraldTailwind[900] : emeraldTailwind[100];
}
function teal(dark: Accessor<boolean>) {
return dark() ? tealTailwind[500] : tealTailwind[600];
}
function darkTeal(dark: Accessor<boolean>) {
return dark() ? tealTailwind[900] : tealTailwind[100];
}
function cyan(dark: Accessor<boolean>) {
return dark() ? cyanTailwind[500] : cyanTailwind[600];
}
function darkCyan(dark: Accessor<boolean>) {
return dark() ? cyanTailwind[900] : cyanTailwind[100];
}
function sky(dark: Accessor<boolean>) {
return dark() ? skyTailwind[500] : skyTailwind[600];
}
function darkSky(dark: Accessor<boolean>) {
return dark() ? skyTailwind[900] : skyTailwind[100];
}
function blue(dark: Accessor<boolean>) {
return dark() ? blueTailwind[500] : blueTailwind[600];
}
function darkBlue(dark: Accessor<boolean>) {
return dark() ? blueTailwind[900] : blueTailwind[100];
}
function indigo(dark: Accessor<boolean>) {
return dark() ? indigoTailwind[500] : indigoTailwind[600];
}
function darkIndigo(dark: Accessor<boolean>) {
return dark() ? indigoTailwind[900] : indigoTailwind[100];
}
function violet(dark: Accessor<boolean>) {
return dark() ? violetTailwind[500] : violetTailwind[600];
}
function darkViolet(dark: Accessor<boolean>) {
return dark() ? violetTailwind[900] : violetTailwind[100];
}
function purple(dark: Accessor<boolean>) {
return dark() ? purpleTailwind[500] : purpleTailwind[600];
}
function darkPurple(dark: Accessor<boolean>) {
return dark() ? purpleTailwind[900] : purpleTailwind[100];
}
function fuchsia(dark: Accessor<boolean>) {
return dark() ? fuchsiaTailwind[500] : fuchsiaTailwind[600];
}
function darkFuchsia(dark: Accessor<boolean>) {
return dark() ? fuchsiaTailwind[900] : fuchsiaTailwind[100];
}
function pink(dark: Accessor<boolean>) {
return dark() ? pinkTailwind[500] : pinkTailwind[600];
}
function darkPink(dark: Accessor<boolean>) {
return dark() ? pinkTailwind[900] : pinkTailwind[100];
}
function rose(dark: Accessor<boolean>) {
return dark() ? roseTailwind[500] : roseTailwind[600];
}
function darkRose(dark: Accessor<boolean>) {
return dark() ? roseTailwind[900] : roseTailwind[100];
}
const darkWhite = grayTailwind[400];
const gray = grayTailwind[600];
function darkWhite(dark: Accessor<boolean>) {
return dark() ? grayTailwind[400] : grayTailwind[400];
}
function gray(dark: Accessor<boolean>) {
return dark() ? grayTailwind[600] : grayTailwind[400];
}
const black = "#000000";
const white = "#ffffff";
function white(dark: Accessor<boolean>) {
return dark() ? "#ffffff" : "#000000";
}
function black(dark: Accessor<boolean>) {
return dark() ? "#000000" : "#ffffff";
}
export const convertCandleToCandleColor = (
candle: { close: number; open: number },

View File

@@ -1,10 +1,6 @@
// import { ONE_DAY_IN_MS } from "./time";
import { ONE_DAY_IN_MS } from "./time";
export const dateToString = (date: Date) => date.toJSON().split("T")[0];
// export const FIVE_MONTHS_IN_DAYS = 30 * 5;
export const getNumberOfDaysBetweenTwoDates = (oldest: Date, youngest: Date) =>
Math.round(Math.abs((youngest.getTime() - oldest.getTime()) / ONE_DAY_IN_MS));

View File

@@ -3,7 +3,3 @@ export function sleep(ms: number) {
setTimeout(resolve, ms);
});
}
export function tick() {
return sleep(1);
}

View File

@@ -1,5 +1,5 @@
import { sleep } from "./sleep";
export function tick() {
return new Promise((resolve) => {
setTimeout(resolve, 0);
});
return sleep(0);
}

1
app/src/scripts/utils/types.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
type Color = (dark: Accessor<boolean>) => string;