Skip to content

Commit

Permalink
feat: ✨ add core definitions
Browse files Browse the repository at this point in the history
added core definitions for charts.
  • Loading branch information
DavideSegullo committed May 24, 2024
1 parent a2e5869 commit 9e0767a
Show file tree
Hide file tree
Showing 4 changed files with 275 additions and 0 deletions.
49 changes: 49 additions & 0 deletions packages/web/components/chart/light-weight-charts/area-chart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type {
AreaData,
AreaSeriesOptions,
AreaStyleOptions,
DeepPartial,
ISeriesApi,
SeriesOptionsCommon,
Time,
TimeChartOptions,
WhitespaceData,
} from "lightweight-charts";

import { ChartController, type ChartControllerParams } from "./chart";

export class AreaChartController<
T = TimeChartOptions,
K = Time
> extends ChartController<T, K> {
series: ISeriesApi<
"Area",
Time,
AreaData<Time> | WhitespaceData<Time>,
AreaSeriesOptions,
DeepPartial<AreaStyleOptions & SeriesOptionsCommon>
>[] = [];

constructor(params: ChartControllerParams<T, K>) {
super(params);

if (params.series && params.series.length > 0) {
for (const s of params.series) {
const series = this.api.addAreaSeries(s.options);
series.setData(s.data);

this.series.push(series);
}
}
}

override applyOptions(params: Partial<ChartControllerParams<T, K>>): void {
super.applyOptions(params);

if (params.series && params.series.length > 0) {
for (const [key, s] of params.series.entries()) {
this.series[key].setData(s.data);
}
}
}
}
81 changes: 81 additions & 0 deletions packages/web/components/chart/light-weight-charts/chart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import EventEmitter from "eventemitter3";
import {
createChart,
type DeepPartial,
type IChartApi,
type MouseEventParams,
type SeriesDataItemTypeMap,
type SeriesOptionsMap,
type Time,
type TimeChartOptions,
} from "lightweight-charts";

export interface Series {
type: keyof SeriesOptionsMap;
options: DeepPartial<SeriesOptionsMap[keyof SeriesOptionsMap]>;
data: SeriesDataItemTypeMap<Time>[keyof SeriesOptionsMap][];
}

export interface ChartControllerParams<T = TimeChartOptions, K = Time> {
options: DeepPartial<T>;
series?: Series[];
container: HTMLElement;
onCrosshairMove?: (param: MouseEventParams<K>) => void;
}

export type ChartControllerEvents<T = TimeChartOptions, K = Time> = {
crosshairMove: (param: MouseEventParams<K>) => void;
init: (params: ChartControllerParams<T, K>) => void;
remove: (params: ChartControllerParams<T, K>) => void;
};

export abstract class ChartController<T = TimeChartOptions, K = Time> {
protected api: IChartApi;
protected onCrosshairMove: ((param: MouseEventParams<K>) => void) | undefined;

events = new EventEmitter<ChartControllerEvents<T, K>>();

constructor(protected params: ChartControllerParams<T, K>) {
const { options, container, onCrosshairMove } = params;

this.onCrosshairMove = onCrosshairMove;

this.api = createChart(container, {
width: container?.clientWidth,
...options,
});

this.api.timeScale().fitContent();

this.api.subscribeCrosshairMove((param) => {
this.onCrosshairMove?.(param as never);
this.events.emit("crosshairMove", param as never);
});

this.events.emit("init", this.params);
}

applyOptions(params: Partial<ChartControllerParams<T, K>>) {
if (params.options) {
this.api.applyOptions(params.options);
}

if (params.onCrosshairMove) {
this.onCrosshairMove = params.onCrosshairMove;
}
}

resize() {
this.api.applyOptions({
...this.params.options,
width: this.params.container.clientWidth,
});

this.api.timeScale().fitContent();
}

remove() {
this.api.remove();
this.events.emit("remove", this.params);
}
}
49 changes: 49 additions & 0 deletions packages/web/components/chart/light-weight-charts/line-chart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type {
DeepPartial,
ISeriesApi,
LineData,
LineSeriesOptions,
LineStyleOptions,
SeriesOptionsCommon,
Time,
TimeChartOptions,
WhitespaceData,
} from "lightweight-charts";

import { ChartController, type ChartControllerParams } from "./chart";

export class LineChartController<
T = TimeChartOptions,
K = Time
> extends ChartController<T, K> {
series: ISeriesApi<
"Line",
Time,
LineData<Time> | WhitespaceData<Time>,
LineSeriesOptions,
DeepPartial<LineStyleOptions & SeriesOptionsCommon>
>[] = [];

constructor(params: ChartControllerParams<T, K>) {
super(params);

if (params.series && params.series.length > 0) {
for (const s of params.series) {
const series = this.api.addLineSeries(s.options);
series.setData(s.data);

this.series.push(series);
}
}
}

override applyOptions(params: Partial<ChartControllerParams<T, K>>): void {
super.applyOptions(params);

if (params.series && params.series.length > 0) {
for (const [key, s] of params.series.entries()) {
this.series[key].setData(s.data);
}
}
}
}
96 changes: 96 additions & 0 deletions packages/web/components/chart/light-weight-charts/linear-chart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import {
AreaData,
MouseEventParams,
Time,
TimeChartOptions,
} from "lightweight-charts";

import { AreaChartController } from "./area-chart";
import { type ChartControllerParams } from "./chart";

export class LinearChartController extends AreaChartController {
tooltip: HTMLDivElement;

constructor(params: ChartControllerParams<TimeChartOptions, Time>) {
super(params);

this.tooltip = document.createElement("div");
this.tooltip.style.borderRadius = "20px";
this.tooltip.style.background = "black";
this.tooltip.style.color = "white";
this.tooltip.style.padding = "20px";
this.tooltip.style.position = "absolute";
this.tooltip.style.display = "none";
this.tooltip.style.pointerEvents = "none";
this.tooltip.style.zIndex = "9999";

params.container.appendChild(this.tooltip);

this.events.on("remove", () => {
this.tooltip.remove();
});

this.events.on("crosshairMove", this.crosshairMove.bind(this));
}

crosshairMove(param: MouseEventParams<Time>) {
if (
param.point === undefined ||
!param.time ||
param.point.x < 0 ||
param.point.x > this.params.container.clientWidth ||
param.point.y < 0 ||
param.point.y > this.params.container.clientHeight
) {
this.tooltip.style.display = "none";
} else {
this.tooltip.style.display = "block";

const dataSeries = Array.from(param.seriesData, ([key, value]) => ({
key,
value,
}));

const [_, secondSeriesData] = dataSeries;

const content = dataSeries
.map((series, index) => {
const seriesData = series.value as AreaData;

return `
<div>
<h6>
<span>${secondSeriesData ? "$" : ""}</span>
<span>${seriesData.value}</span>
</h6>
</div>
`;
})
.join("");

const toolTipWidth = secondSeriesData ? 180 : 90;
const toolTipHeight = 64;
const toolTipMargin = 15;

this.tooltip.innerHTML = `<div>
${content}
</div>
`;

const y = param.point.y;
let left = param.point.x + toolTipMargin;
if (left > this.params.container.clientWidth - toolTipWidth) {
left = param.point.x - toolTipMargin - toolTipWidth;
}

let top = y + toolTipMargin;

if (top > this.params.container.clientHeight - toolTipHeight) {
top = y - toolTipHeight - toolTipMargin;
}

this.tooltip.style.left = left + "px";
this.tooltip.style.top = top + "px";
}
}
}

0 comments on commit 9e0767a

Please sign in to comment.