website: start containing lc code in wrapper

This commit is contained in:
nym21
2024-12-03 17:31:56 +01:00
parent 141cd819a1
commit 783aed5826
10 changed files with 535 additions and 489 deletions

View File

@@ -0,0 +1,122 @@
import { Signal } from "../solid-signals/types";
import { Accessor } from "../solid-signals/2024-11-02/types/signals";
import { Owner } from "../solid-signals/2024-11-02/types/core/owner";
import {
DeepPartial,
BaselineStyleOptions,
CandlestickStyleOptions,
LineStyleOptions,
SeriesOptionsCommon,
Time,
CandlestickData,
ISeriesApi,
BaselineData,
} from "./v4.2.0/types";
import { Color } from "../../scripts/types/self";
interface BaseSeriesBlueprint {
title: string;
defaultActive?: boolean;
}
interface BaselineSeriesBlueprint extends BaseSeriesBlueprint {
type: "Baseline";
color?: Color;
options?: DeepPartial<BaselineStyleOptions & SeriesOptionsCommon>;
data?: Accessor<BaselineData<Time>[]>;
}
interface CandlestickSeriesBlueprint extends BaseSeriesBlueprint {
type: "Candlestick";
color?: Color;
options?: DeepPartial<CandlestickStyleOptions & SeriesOptionsCommon>;
data?: Accessor<CandlestickData<Time>[]>;
}
interface LineSeriesBlueprint extends BaseSeriesBlueprint {
type?: "Line";
color: Color;
options?: DeepPartial<LineStyleOptions & SeriesOptionsCommon>;
data?: Accessor<LineData<Time>[]>;
}
type AnySpecificSeriesBlueprint =
| BaselineSeriesBlueprint
| CandlestickSeriesBlueprint
| LineSeriesBlueprint;
type SeriesType = NonNullable<AnySpecificSeriesBlueprint["type"]>;
type PriceSeriesType = "Candlestick" | "Line";
type RemoveSeriesBlueprintFluff<Blueprint extends AnySpecificSeriesBlueprint> =
Omit<Blueprint, "type" | "title">;
type SplitSeriesBlueprint = {
datasetPath: AnyDatasetPath;
main?: boolean;
} & AnySpecificSeriesBlueprint;
type SingleSeriesBlueprint = AnySpecificSeriesBlueprint;
interface CreateBaseSeriesParameters extends BaseSeriesBlueprint {
id: string;
disabled?: Accessor<boolean>;
color?: Color;
}
interface BaseSeries {
id: string;
title: string;
color: Color | Color[];
active: Signal<boolean>;
visible: Accessor<boolean>;
disabled: Accessor<boolean>;
}
interface SingleSeries extends BaseSeries {
iseries: ISeriesApi<any>;
}
interface SplitSeries extends BaseSeries {
chunks: Array<Accessor<ISeriesApi<SeriesType> | undefined>>;
dataset: ResourceDataset<TimeScale, number>;
}
interface CreateSingleSeriesParameters {
blueprint: SingleSeriesBlueprint;
id: string;
}
interface CreateSplitSeriesParameters<S extends TimeScale> {
dataset: ResourceDataset<S>;
blueprint: SplitSeriesBlueprint;
id: string;
index: number;
setMinMaxMarkersWhenIdle: VoidFunction;
disabled?: Accessor<boolean>;
}
type ChartPane = IChartApi & {
whitespace: ISeriesApi<"Line">;
hidden: () => boolean;
setHidden: (b: boolean) => void;
setInitialVisibleTimeRange: VoidFunction;
createSingleSeries: (a: CreateSingleSeriesParameters) => SingleSeries;
createSplitSeries: <S extends TimeScale>(
a: CreateSplitSeriesParameters<S>,
) => SplitSeries;
splitSeries: SplitSeries[];
};
interface CreatePaneParameters {
unit: Unit;
paneIndex?: number;
whitespace?: true;
options?: DeepPartial<ChartOptions>;
config?: SingleSeriesBlueprint[];
}
interface Marker {
weight: number;
time: Time;
value: number;
seriesChunk: ISeriesApi<any>;
}
interface HoveredLegend {
label: HTMLLabelElement;
series: SingleSeries | SplitSeries;
}

View File

@@ -357,6 +357,7 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
* @param {TimeScale} param0.scale
* @param {"static" | "moveable"} param0.kind
* @param {Utilities} param0.utils
* @param {Owner | null} [param0.owner]
* @param {CreatePaneParameters[]} [param0.config]
*/
function createChart({
@@ -368,7 +369,13 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
scale,
config,
utils,
owner: _owner,
}) {
/** @type {SplitSeries[]} */
const chartSplitSeries = [];
let owner = _owner || signals.getOwner();
const div = window.document.createElement("div");
div.classList.add("chart");
parent.append(div);
@@ -541,14 +548,14 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
inputName: utils.stringToId(`selected-${series.title}-${extraName}`),
inputValue: "value",
labelTitle: "Click to toggle",
onClick: (event) => {
event.preventDefault();
event.stopPropagation();
input.checked = !input.checked;
onClick: () => {
series.active.set(input.checked);
},
type: "solo",
});
input.checked = series.active();
const spanMain = window.document.createElement("span");
spanMain.classList.add("main");
label.append(spanMain);
@@ -568,10 +575,6 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
hoveredLegend.set(undefined);
});
signals.createEffect(series.active, (checked) => {
input.checked = checked;
});
function shouldHighlight() {
const hovered = hoveredLegend();
return (
@@ -680,7 +683,7 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
}
} else {
applySeriesOption({
series: series.series,
series: series.iseries,
hovered,
});
}
@@ -727,6 +730,9 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
chartDiv.classList.add("lightweight-chart");
chartWrapper.append(chartDiv);
/** @type {SplitSeries[]} */
const paneSplitSeries = [];
options = { ...options };
if (kind === "static") {
options.handleScale = false;
@@ -748,9 +754,9 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
});
/**
* @param {CreateBaselineSeriesParams} args
* @param {RemoveSeriesBlueprintFluff<BaselineSeriesBlueprint>} args
*/
function createBaseLineSeries({ color, options, owner, data }) {
function createBaseLineSeries({ color, options, data }) {
const topLineColor = color || colors.profit;
const bottomLineColor = color || colors.loss;
@@ -784,16 +790,20 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
});
if (data) {
series.setData(data);
signals.runWithOwner(owner, () => {
signals.createEffect(data, (data) => {
series.setData(data);
});
});
}
return series;
}
/**
* @param {CreateCandlestickSeriesParams} args
* @param {RemoveSeriesBlueprintFluff<CandlestickSeriesBlueprint>} args
*/
function createCandlestickSeries({ options, owner, data }) {
function createCandlestickSeries({ options, data }) {
function computeColors() {
const upColor = colors.profit();
const downColor = colors.loss();
@@ -825,16 +835,20 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
});
if (data) {
series.setData(data);
signals.runWithOwner(owner, () => {
signals.createEffect(data, (data) => {
series.setData(data);
});
});
}
return series;
}
/**
* @param {CreateLineSeriesParams} args
* @param {RemoveSeriesBlueprintFluff<LineSeriesBlueprint>} args
*/
function createLineSeries({ color, options, owner, data }) {
function createLineSeries({ color, options, data }) {
function computeColors() {
return {
color: color(),
@@ -847,49 +861,41 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
...computeColors(),
});
if (data) {
series.setData(data);
}
signals.runWithOwner(owner, () => {
signals.createEffect(computeColors, (computeColors) => {
series.applyOptions(computeColors);
});
});
if (data) {
signals.runWithOwner(owner, () => {
signals.createEffect(data, (data) => {
series.setData(data);
});
});
}
return series;
}
/**
* @template {TimeScale} S
* @param {CreateSplitSeriesParameters<S>} args
* @param {CreateBaseSeriesParameters} args
*/
function createSplitSeries({
option,
index: seriesIndex,
function createBaseSeries({
id,
disabled: _disabled,
setMinMaxMarkersWhenIdle,
dataset,
seriesBlueprint,
splitSeries,
title,
color,
defaultActive,
}) {
const {
title,
color,
defaultActive,
type,
options: seriesOptions,
} = seriesBlueprint;
/** @type {Signal<ISeriesApi<SeriesType> | undefined>[]} */
const chunks = new Array(dataset.fetchedJSONs.length);
const id = utils.stringToId(title);
const storageId = utils.stringToId(`${option.id}-${title}`);
const keyPrefix = id;
const paramKey = utils.stringToId(title);
const storageKey = `${keyPrefix}-${paramKey}`;
const active = signals.createSignal(
utils.url.readBoolParam(id) ??
utils.storage.readBool(storageId) ??
utils.url.readBoolParam(paramKey) ??
utils.storage.readBool(storageKey) ??
defaultActive ??
true,
);
@@ -898,12 +904,10 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
const visible = signals.createMemo(() => active() && !disabled());
/** @satisfies {SplitSeries} */
/** @satisfies {BaseSeries} */
const series = {
active,
chunks,
color: color || [colors.profit, colors.loss],
dataset,
disabled,
id,
title,
@@ -918,32 +922,97 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
}
if (active !== (defaultActive || true)) {
utils.url.writeParam(id, active);
utils.storage.write(storageId, active);
utils.url.writeParam(paramKey, active);
utils.storage.write(storageKey, active);
} else {
utils.url.removeParam(id);
utils.storage.remove(storageId);
utils.url.removeParam(paramKey);
utils.storage.remove(storageKey);
}
},
);
splitSeries.push(series);
return series;
}
const owner = signals.getOwner();
const chartPane = /** @type {ChartPane} */ (_chart);
chartPane.createSingleSeries = function ({ blueprint, id }) {
/** @type {ISeriesApi<SeriesType>} */
let s;
switch (blueprint.type) {
case "Baseline": {
s = createBaseLineSeries(blueprint);
break;
}
case "Candlestick": {
s = createCandlestickSeries(blueprint);
break;
}
default:
case "Line": {
s = createLineSeries(blueprint);
break;
}
}
/** @satisfies {SingleSeries} */
const series = {
...createBaseSeries({
id,
title: blueprint.title,
color: blueprint.color,
defaultActive: blueprint.defaultActive,
}),
iseries: s,
};
signals.createEffect(series.visible, (visible) => {
series.iseries.applyOptions({
visible,
});
});
createLegend({ series });
return series;
};
chartPane.createSplitSeries = function ({
id,
index: seriesIndex,
disabled,
setMinMaxMarkersWhenIdle,
dataset,
blueprint,
}) {
/** @satisfies {SplitSeries} */
const series = {
...createBaseSeries({
id,
title: blueprint.title,
color: blueprint.color,
defaultActive: blueprint.defaultActive,
disabled,
}),
dataset,
chunks: new Array(dataset.fetchedJSONs.length),
};
paneSplitSeries.push(series);
chartSplitSeries.unshift(series);
dataset.fetchedJSONs.forEach((json, index) => {
const chunk = signals.createSignal(
/** @type {ISeriesApi<SeriesType> | undefined} */ (undefined),
);
chunks[index] = chunk;
series.chunks[index] = chunk;
const isMyTurn = signals.createMemo(() => {
if (seriesIndex <= 0) return true;
const previousSeriesChunk = splitSeries.at(seriesIndex - 1)?.chunks[
index
];
const previousSeriesChunk = paneSplitSeries.at(seriesIndex - 1)
?.chunks[index];
const isPreviousSeriesOnChart = previousSeriesChunk?.();
return !!isPreviousSeriesOnChart;
@@ -957,28 +1026,25 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
let s = chunk();
if (!s) {
switch (type) {
switch (blueprint.type) {
case "Baseline": {
s = createBaseLineSeries({
color,
options: seriesOptions,
owner,
color: blueprint.color,
options: blueprint.options,
});
break;
}
case "Candlestick": {
s = createCandlestickSeries({
options: seriesOptions,
owner,
options: blueprint.options,
});
break;
}
default:
case "Line": {
s = createLineSeries({
color,
options: seriesOptions,
owner,
color: blueprint.color,
options: blueprint.options,
});
break;
}
@@ -1069,23 +1135,17 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
});
});
createLegend({ series, extraName: type });
createLegend({ series, extraName: blueprint.type });
return series;
}
const chartPane = /** @type {ChartPane} */ (_chart);
chartPane.createSplitSeries = createSplitSeries;
chartPane.createBaseLineSeries = createBaseLineSeries;
chartPane.createCandlesticksSeries = createCandlestickSeries;
chartPane.createLineSeries = createLineSeries;
};
chartPane.hidden = () => {
return chartWrapper.hidden;
};
chartPane.setHidden = (b) => {
chartWrapper.hidden = b;
};
chartPane.splitSeries = paneSplitSeries;
chartPane.setInitialVisibleTimeRange = () => {
const range = visibleTimeRange();
@@ -1143,26 +1203,15 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
}
createUnitAndModeElements();
config?.forEach((params) => {
// createLegend(params);
switch (params.kind) {
case "line": {
chartPane.createLineSeries(params);
break;
}
case "candle": {
chartPane.createCandlesticksSeries(params);
break;
}
case "baseline": {
chartPane.createBaseLineSeries(params);
break;
}
}
});
switch (kind) {
case "static": {
config?.forEach((params) => {
chartPane.createSingleSeries({
id: utils.stringToId(params.title),
blueprint: params,
});
});
chartPane.timeScale().fitContent();
break;
@@ -1199,9 +1248,11 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
*
* @param {Object} param0
* @param {TimeScale} param0.scale
* @param {Owner | null} param0.owner
*/
function reset({ scale: _scale }) {
function reset({ scale: _scale, owner: _owner }) {
scale = _scale;
owner = _owner;
panes.forEach((pane) => pane.remove());
panes.length = 0;
legendElement.innerHTML = "";
@@ -1265,6 +1316,7 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
saveVisibleRange,
getTicksToWidthRatio,
debouncedSaveVisibleRange,
splitSeries: chartSplitSeries,
};
}