feat: chart.js changes

This commit is contained in:
bartosz-lipinski 2021-01-01 16:00:55 -06:00
parent 94762b0763
commit f40a37582f
10 changed files with 144 additions and 125 deletions

13
package-lock.json generated
View File

@ -2301,6 +2301,14 @@
"base-x": "^3.0.6"
}
},
"@types/chart.js": {
"version": "2.9.29",
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.29.tgz",
"integrity": "sha512-WOZMitUU3gHDM0oQsCsVivX+oDsIki93szcTmmUPBm39cCvAELBjokjSDVOoA3xiIEbb+jp17z/3S2tIqruwOQ==",
"requires": {
"moment": "^2.10.2"
}
},
"@types/color-name": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
@ -4320,6 +4328,11 @@
"moment": "^2.10.2"
}
},
"chartjs": {
"version": "0.3.24",
"resolved": "https://registry.npmjs.org/chartjs/-/chartjs-0.3.24.tgz",
"integrity": "sha1-Ot3rWuNgaz6J40bCfVLKFYQW6T0="
},
"chartjs-color": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz",

View File

@ -13,6 +13,7 @@
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"@types/chart.js": "^2.9.29",
"@types/echarts": "^4.9.0",
"@types/react-router-dom": "^5.1.6",
"antd": "^4.6.6",
@ -27,7 +28,6 @@
"jazzicon": "^1.5.0",
"lodash": "^4.17.20",
"react": "^16.13.1",
"react-chartjs-2": "^2.11.1",
"react-dom": "^16.13.1",
"react-github-btn": "^1.2.0",
"react-intl": "^5.10.2",

View File

@ -108,7 +108,7 @@ export const AppLayout = (props: any) => {
<Menu.Item key="6" icon={< LineChartOutlined/>}>
<Link
to={{
pathname: "/marginTrading",
pathname: "/margin",
}}
>
{LABELS.MARGIN_TRADING}

View File

@ -233,7 +233,7 @@ export const useEnrichedPools = (pools: PoolInfo[]) => {
const poolKeys = pools.map((p) => p.pubkeys.account.toBase58()).join(',');
useEffect(() => {
if (!marketEmitter || !subscribeToMarket || pools.length == 0) {
if (!marketEmitter || !subscribeToMarket || pools.length === 0) {
return;
}
//@ts-ignore

View File

@ -47,9 +47,9 @@ export function Routes() {
<Route path='/repay/:reserve' children={<RepayReserveView />} />
<Route exact path='/liquidate' children={<LiquidateView />} />
<Route path='/liquidate/:id' children={<LiquidateReserveView />} />
<Route exact path='/marginTrading' children={<MarginTrading />} />
<Route exact path='/margin' children={<MarginTrading />} />
<Route path='/marginTrading/:id' children={<NewPosition />} />
<Route path='/margin/:id' children={<NewPosition />} />
<Route exact path='/faucet' children={<FaucetView />} />
</Switch>
</AppLayout>

View File

@ -16,7 +16,7 @@ export const MarginTradeItem = (props: { reserve: LendingReserve; address: Publi
const apr = calculateBorrowAPY(props.reserve);
return (
<Link to={`/marginTrading/${props.address.toBase58()}`}>
<Link to={`/margin/${props.address.toBase58()}`}>
<div className='choose-margin-item'>
<span style={{ display: 'flex' }}>
<TokenIcon mintAddress={props.reserve.liquidityMint} />

View File

@ -57,7 +57,7 @@ export default function Breakdown({ item }: { item: Position }) {
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-around', alignItems: 'center' }}>
<Card>
<Statistic
title='Leverage'
title='Borrowed'
value={brokeragePart * exchangeRate}
precision={2}
valueStyle={{ color: brokerageColor }}
@ -66,7 +66,7 @@ export default function Breakdown({ item }: { item: Position }) {
</Card>
<Card>
<Statistic
title='My Collateral Value'
title='My Collateral'
value={myPart}
precision={2}
valueStyle={{ color: myColor }}
@ -101,14 +101,6 @@ export default function Breakdown({ item }: { item: Position }) {
}}
style={{ marginBottom: '20px' }}
/>
<span style={{ float: 'right', fontSize: '9px' }}>
<a
href='https://github.com/bZxNetwork/fulcrum_ui/blob/development/packages/fulcrum-website/assets/js/trading.js'
target='blank'
>
credit
</a>
</span>
</Card>
</div>
);

View File

@ -1,7 +1,6 @@
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { Line } from 'react-chartjs-2';
import React, { useEffect, useRef } from 'react';
import Chart, { ChartPluginsOptions } from "chart.js";
import { Position } from './interfaces';
import { debounce } from 'lodash';
// Special thanks to
// https://github.com/bZxNetwork/fulcrum_ui/blob/development/packages/fulcrum-website/assets/js/trading.js
@ -61,24 +60,11 @@ const baseData = [
{ x: 50, y: 65 },
];
function getChartData({ item, priceChange }: { item: Position; priceChange: number }) {
function getChartData() {
//the only way to create an immutable copy of array with objects inside.
const baseDashed = JSON.parse(JSON.stringify(baseData.slice(Math.floor(baseData.length) / 2)));
const baseDashed = getBaseDashed();
const baseSolid = JSON.parse(JSON.stringify(baseData.slice(0, Math.floor(baseData.length) / 2 + 1)));
const leverage = item.leverage;
baseDashed.forEach((item: { y: number; x: number }, index: number) => {
if (index !== 0) item.y += (item.y * priceChange) / 100;
});
var leverageData = baseDashed.map((item: { x: number; y: number }, index: number) => {
if (index === 0) {
return { x: item.x, y: item.y };
}
const gain = (priceChange * leverage) / 100;
return { x: item.x, y: item.y * (1 + gain) };
});
return {
datasets: [
{
@ -90,10 +76,10 @@ function getChartData({ item, priceChange }: { item: Position; priceChange: numb
},
{
backgroundColor: 'transparent',
borderColor: priceChange >= 0 ? 'rgb(51, 223, 204)' : 'rgb(255,79,79)',
borderWidth: 4,
radius: 0,
data: leverageData,
data: baseDashed,
borderDash: [15, 3],
label: 'LEVERAGE',
},
@ -110,139 +96,167 @@ function getChartData({ item, priceChange }: { item: Position; priceChange: numb
};
}
const labelPlugin: ChartPluginsOptions = {};
const getBaseDashed = () => {
return JSON.parse(JSON.stringify(baseData.slice(Math.floor(baseData.length) / 2))) as { x: number, y: number }[];
}
function updateChartData({
item,
priceChange,
chartRef,
chart,
}: {
item: Position;
priceChange: number;
chartRef: React.RefObject<any>;
chart: Chart;
}) {
const data = getChartData({ item, priceChange });
chartRef.current.chartInstance.data = data;
chartRef.current.chartInstance.canvas.parentNode.style.width = '100%';
chartRef.current.chartInstance.canvas.parentNode.style.height = 'auto';
chartRef.current.chartInstance.update();
if (!chart?.data.datasets || chart?.data.datasets.length < 2) {
return;
}
labelPlugin.afterDraw = (instance: Chart) => {
drawLabels(instance, item.leverage, priceChange)
};
const baseDashed = getBaseDashed();
const leverage = item.leverage;
var leverageData = baseDashed.map((item: { x: number; y: number }, index: number) => {
if (index === 0) {
return { x: item.x, y: item.y };
}
const gain = (priceChange * leverage) / 100;
return { x: item.x, y: item.y * (1 + gain) };
});
chart.data.datasets[1].data = leverageData;
chart.data.datasets[1].borderColor = priceChange >= 0 ? 'rgb(51, 223, 204)' : 'rgb(255,79,79)';
baseDashed.forEach((item: { y: number; x: number }, index: number) => {
if (index !== 0) item.y += (item.y * priceChange) / 100;
});
chart.data.datasets[2].data = baseDashed;
// chart.chartInstance.canvas.parentNode.style.width = '100%';
// chart.chartInstance.canvas.parentNode.style.height = 'auto';
chart?.update()
}
function drawLabels(t: any, ctx: any, leverage: number, priceChange: number) {
function drawLabels(chart: Chart, leverage: number, priceChange: number) {
if (!chart.config || !chart.config.data || !chart.config.data.datasets || !chart.canvas) {
return;
}
const ctx = chart.ctx;
if (!ctx) {
return;
}
ctx.save();
ctx.font = 'normal normal bold 15px /1.5 Muli';
ctx.textBaseline = 'bottom';
const chartInstance = t.chart;
const datasets = chartInstance.config.data.datasets;
datasets.forEach(function (ds: { label: any; borderColor: any }, index: number) {
const datasets = chart.config.data.datasets;
const element = (chart?.canvas?.parentNode as HTMLElement);
datasets.forEach((ds, index) => {
const label = ds.label;
ctx.fillStyle = ds.borderColor;
ctx.fillStyle = ds.borderColor as string;
const meta = chartInstance.controller.getDatasetMeta(index);
const meta = chart.getDatasetMeta(index);
const len = meta.data.length - 1;
const pointPostition = Math.floor(len / 2) - Math.floor(0.2 * len);
const x = meta.data[pointPostition]._model.x;
const xOffset = x;
const y = meta.data[pointPostition]._model.y;
let yOffset;
if (label === 'HOLD') {
yOffset = leverage * priceChange > 0 ? y * 1.2 : y * 0.8;
} else {
yOffset = leverage * priceChange > 0 ? y * 0.8 : y * 1.2;
}
if (yOffset > chartInstance.canvas.parentNode.offsetHeight) {
if (yOffset > element.offsetHeight) {
// yOffset = 295;
chartInstance.canvas.parentNode.style.height = `${yOffset * 1.3}px`;
element.style.height = `${yOffset * 1.3}px`;
}
if (yOffset < 0) yOffset = 5;
if (label) ctx.fillText(label, xOffset, yOffset);
});
ctx.restore();
}
const debouncedUpdateChartData = debounce(updateChartData, 200);
export default function GainsChart({ item, priceChange }: { item: Position; priceChange: number }) {
const chartRef = useRef<any>();
const [booted, setBooted] = useState<boolean>(false);
const [canvas, setCanvas] = useState<any>();
useEffect(() => {
if (chartRef.current.chartInstance) debouncedUpdateChartData({ item, priceChange, chartRef });
}, [priceChange, item.leverage]);
const chartRef = useRef<Chart>();
const canvasRef = useRef<HTMLCanvasElement>();
useEffect(() => {
if (chartRef.current && !booted && canvas) {
//@ts-ignore
const originalController = window.Chart.controllers.line;
//@ts-ignore
window.Chart.controllers.line = Chart.controllers.line.extend({
draw: function () {
originalController.prototype.draw.call(this, arguments);
drawLabels(this, canvas.getContext('2d'), item.leverage, priceChange);
},
});
setBooted(true);
if (!canvasRef.current || chartRef.current) {
return;
}
}, [chartRef, canvas]);
const chart = useMemo(
() => (
<Line
ref={chartRef}
data={(canvas: any) => {
setCanvas(canvas);
return getChartData({ item, priceChange });
}}
options={{
responsive: true,
maintainAspectRatio: true,
scaleShowLabels: false,
layout: {
padding: {
top: 30,
bottom: 80,
chartRef.current = new Chart(canvasRef.current, {
type: 'line',
data: getChartData(),
plugins: [
labelPlugin
],
options: {
responsive: true,
maintainAspectRatio: true,
scaleShowLabels: false,
layout: {
padding: {
top: 30,
bottom: 80,
},
},
labels: {
render: 'title',
fontColor: ['green', 'white', 'red'],
precision: 2,
},
animation: {
easing: 'easeOutExpo',
duration: 500,
},
scales: {
xAxes: [
{
display: false,
gridLines: {
display: false,
},
type: 'linear',
position: 'bottom',
},
},
labels: {
render: 'title',
fontColor: ['green', 'white', 'red'],
precision: 2,
},
animation: {
easing: 'easeOutExpo',
duration: 500,
},
scales: {
xAxes: [
{
],
yAxes: [
{
display: false,
gridLines: {
display: false,
gridLines: {
display: false,
},
type: 'linear',
position: 'bottom',
},
],
yAxes: [
{
display: false,
gridLines: {
display: false,
},
},
],
},
legend: {
display: false,
},
}}
/>
),
[]
);
},
],
},
legend: {
display: false,
},
} as any
});
}, []);
useEffect(() => {
if (chartRef.current) {
updateChartData({ item, priceChange, chart: chartRef.current });
}
}, [priceChange, item]);
return (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'stretch', justifyContent: 'center' }}>
{chart}
<canvas ref={canvasRef as any} />
<div
style={{
display: 'flex',

View File

@ -13,7 +13,7 @@ export const NewPosition = () => {
const lendingReserve = useLendingReserve(id);
const [newPosition, setNewPosition] = useState<Position>({
id: null,
leverage: 1,
leverage: 5,
collateral: {},
asset: {},
});

View File

@ -72,5 +72,5 @@ export function useLeverage({
return;
}
setNewPosition({ ...newPosition, error: '' });
}, [collType, desiredType, desiredValue, leverage, enrichedPools, collValue, collateralDeposit?.info.amount]);
}, [collType, desiredType, desiredValue, leverage, enrichedPools, collValue, collateralDeposit]);
}