UNPKG

financial-chart-made-simple

Version:

The package that allows you to build responsive financial charts styled your way. The chart functionality is similar to that on coinmarketcap.io.

871 lines (870 loc) 49.3 kB
//import { useColorModeValue } from "@chakra-ui/react"; import React, { useEffect, useRef } from "react"; import { Chart as ChartJS, Filler, LinearScale, CategoryScale, BarElement, PointElement, LineElement, Legend, Tooltip, LineController, BarController, TimeScale, } from "chart.js"; import { Chart as ChartRJS } from "react-chartjs-2"; import zoomPlugin from "chartjs-plugin-zoom"; import "chartjs-adapter-date-fns"; ChartJS.register(LinearScale, CategoryScale, BarElement, PointElement, LineElement, Legend, Tooltip, LineController, BarController, zoomPlugin, Filler, TimeScale); export const defaultColors = { above: "rgb(106, 173, 56)", below: "rgb(255, 63, 63)", barChart: "#ccc", tooltip: "rgb(102, 102, 102)", tooltipDate: "white", tooltipTime: "lightgrey", tooltipData: "lightgrey", xLabel: "rgb(102, 102, 102)", yLabel: "rgb(102, 102, 102)", openLabel: "rgba(102, 102, 102, 0.7)", minChartLine: "rgb(54, 162, 235)", minChartBackground: "rgba(54, 162, 235, 0.2)", slide: "rgba(54, 162, 235, 0.4)", }; export const Chart = ({ colors = { above: "rgb(106, 173, 56)", below: "rgb(255, 63, 63)", barChart: "#ccc", tooltip: "rgb(102, 102, 102)", tooltipDate: "white", tooltipTime: "lightgrey", tooltipData: "lightgrey", xLabel: "rgb(102, 102, 102)", yLabel: "rgb(102, 102, 102)", openLabel: "rgba(102, 102, 102, 0.7)", minChartLine: "rgb(54, 162, 235)", minChartBackground: "rgba(54, 162, 235, 0.2)", slide: "rgba(54, 162, 235, 0.4)", }, font = "Arial", dataset, currency = "USD", }) => { const chartRef = useRef(null); const chartRef2 = useRef(null); const barChartColor = colors.barChart; const slideColor = colors.slide; ChartJS.defaults.font.family = `'FontAwesome', '${font}', 'Arial', 'sans-serif'`; const getGradient = (ctx, chartArea, data, scales) => { const { bottom } = chartArea; const { y } = scales; const gradientBorder = ctx.createLinearGradient(0, 0, 0, bottom); let shift = y.getPixelForValue(dataset[0].Open) / bottom; if (shift > 1) shift = 1; if (shift < 0) shift = 0; try { gradientBorder.addColorStop(0, colors.above); gradientBorder.addColorStop(shift, colors.above); gradientBorder.addColorStop(shift, colors.below); gradientBorder.addColorStop(1, colors.below); } catch (_a) { } return gradientBorder; }; const belowGradient = (ctx, chartArea, data, scales) => { const { bottom } = chartArea; const { y } = scales; const gradientBackground = ctx.createLinearGradient(0, y.getPixelForValue(data.datasets[0].data[0]), 0, bottom); try { gradientBackground.addColorStop(0, "rgba(255, 63, 63, 0)"); gradientBackground.addColorStop(1, "rgba(255, 63, 63, 0.5)"); } catch (_a) { } return gradientBackground; }; const aboveGradient = (ctx, chartArea, data, scales) => { const { top } = chartArea; const { y } = scales; const gradientBackground = ctx.createLinearGradient(0, y.getPixelForValue(data.datasets[0].data[0]), 0, top); try { gradientBackground.addColorStop(0, "rgba(106, 173, 56, 0)"); gradientBackground.addColorStop(1, "rgba(106, 173, 56, 0.5)"); } catch (_a) { } return gradientBackground; }; const crosshairLine = (chart, event) => { const { ctx, chartArea: { left, right, top, bottom }, } = chart; const coorX = event.offsetX; const coorY = event.offsetY; chart.update("none"); ctx.restore(); ctx.strokeStyle = "#666"; ctx.lineWidth = 1; ctx.setLineDash([3, 3]); if (coorY >= top && coorY <= bottom && coorX >= left && coorX <= right) { ctx.beginPath(); ctx.moveTo(left, coorY); ctx.lineTo(right, coorY); ctx.stroke(); ctx.closePath(); ctx.beginPath(); ctx.moveTo(coorX, top); ctx.lineTo(coorX, bottom); ctx.stroke(); ctx.closePath(); crosshairLabel(chart, event); crosshairPoint(chart, event); } ctx.setLineDash([]); }; const crosshairLabel = (chart, event) => { const { ctx, chartArea: { bottom, left, right }, scales: { x, y }, } = chart; const coorX = event.offsetX; const coorY = event.offsetY; const textWidth = ctx.measureText(new Date(x.getValueForPixel(coorX)).toLocaleString()) .width + 10; //yLabel ctx.beginPath(); ctx.fillStyle = colors.yLabel; ctx.fillRect(0, coorY - 8, left, 16); ctx.closePath(); ctx.font = "12px Sora"; ctx.fillStyle = "white"; ctx.textBaseLine = "middle"; ctx.textAlign = "center"; ctx.fillText(y.getValueForPixel(coorY).toFixed(2), left / 2, coorY + 4); //xLabel let xPosition; if (coorX > right - textWidth / 2) xPosition = right - textWidth; else if (coorX < left + textWidth / 2) xPosition = left; else xPosition = coorX - textWidth / 2; ctx.beginPath(); ctx.fillStyle = colors.xLabel; ctx.fillRect(xPosition, bottom, textWidth, 16); ctx.closePath(); ctx.fillStyle = "white"; ctx.fillText(new Date(x.getValueForPixel(coorX)).toLocaleString(), xPosition + textWidth / 2, bottom + 12); }; const crosshairPoint = (chart, event) => { const { ctx, data, chartArea: { left, right, width }, scales: { x, y }, } = chart; const coorX = event.offsetX; ctx.beginPath(); ctx.strokeStyle = "rgba(0, 0, 0, 0)"; ctx.lineWidth = 0; ctx.setLineDash([]); const min = chart.config.options.scales.x.min; const max = chart.config.options.scales.x.max; const angle = Math.PI / 180; const leftOffset = x.getPixelForValue(x.min) - left; const rightOffset = right - x.getPixelForValue(x.max); const width2 = width - (leftOffset + rightOffset); const segments = width2 / (dataset.map((e) => e.Date).indexOf(max) - dataset.map((e) => e.Date).indexOf(min)); const yOpening = y.getPixelForValue(data.datasets[0].data[0]); let index = Math.floor((coorX - (left + leftOffset)) / segments) + dataset.map((e) => e.Date).indexOf(min); let yStart = y.getPixelForValue(data.datasets[0].data[index]); let yEnd = y.getPixelForValue(data.datasets[0].data[index + 1]); let yInterpolation = yStart + ((yEnd - yStart) / segments) * (coorX - x.getPixelForValue(data.labels[index])); if (yInterpolation >= yOpening) ctx.fillStyle = colors.below; else ctx.fillStyle = colors.above; //draw the circle ctx.arc(coorX, yInterpolation, 4, angle * 0, angle * 360, false); ctx.fill(); ctx.stroke(); }; const zoom = (chart, event) => { const min = chart.config.options.scales.x.min; const max = chart.config.options.scales.x.max; const minIndex = dataset.map((e) => e.Date).indexOf(min); const maxIndex = dataset.map((e) => e.Date).indexOf(max); const timestamp = chart.scales.x.getValueForPixel(event.offsetX); const dayTimestamp = new Date(timestamp).setHours(0, 0, 0, 0); const scrollPoint = dataset .map((e) => new Date(e.Date).setHours(0, 0, 0, 0).toLocaleString()) .indexOf(dayTimestamp.toLocaleString()); //zoom in if (event.wheelDeltaY >= 0) { if (minIndex + 1 < maxIndex - 1) { chart.config.options.scales.x.min = dataset[minIndex + 1].Date; chart.config.options.scales.x.max = dataset[maxIndex - 1].Date; } if (minIndex >= scrollPoint - 8 && minIndex <= scrollPoint) chart.config.options.scales.x.min = min; if (maxIndex <= scrollPoint + 8 && maxIndex >= scrollPoint) chart.config.options.scales.x.max = max; } //zoom out if (event.wheelDeltaY < 0) { if (minIndex - 1 !== -1) chart.config.options.scales.x.min = dataset[minIndex - 1].Date; if (maxIndex + 1 !== dataset.length) chart.config.options.scales.x.max = dataset[maxIndex + 1].Date; const weekms = 86400000 * 14; const range = max - min; if (range >= weekms) { if (minIndex >= scrollPoint - 8 && minIndex <= scrollPoint) chart.config.options.scales.x.min = min; if (maxIndex <= scrollPoint + 8 && maxIndex >= scrollPoint) chart.config.options.scales.x.max = max; } } chart.update("none"); }; const zoomBox = (min, max) => { var _a; (_a = chartRef2.current) === null || _a === void 0 ? void 0 : _a.update("none"); const { ctx, canvas, chartArea: { top, left, right, height }, scales: { x }, } = chartRef2.current; const radius = 7; if (min === undefined) min = dataset[0].Date; if (max === undefined) max = dataset[dataset.length - 1].Date; //this blue slider const zoomBoxItem = (min, max) => { if (min === undefined || min === -1) min = dataset[0].Date; ctx.save(); ctx.beginPath(); ctx.fillStyle = slideColor; ctx.fillRect(x.getPixelForValue(new Date(min)), top, x.getPixelForValue(new Date(max)) - x.getPixelForValue(new Date(min)), height); ctx.closePath(); ctx.restore(); const angle = Math.PI / 180; const slideDot = (radius, xPosition) => { ctx.beginPath(); ctx.strokeStyle = colors.minChartLine; ctx.fillStyle = "#fff"; ctx.arc(x.getPixelForValue(xPosition), height / 2, radius, angle * 0, angle * 360, false); ctx.fill(); ctx.stroke(); ctx.closePath(); ctx.restore(); ctx.strokeStyle = colors.minChartLine; ctx.lineWidth = 1.5; ctx.beginPath(); ctx.moveTo(x.getPixelForValue(xPosition) - 2, height / 2 - radius / 2); ctx.lineTo(x.getPixelForValue(xPosition) - 2, height / 2 + radius / 2); ctx.stroke(); ctx.restore(); ctx.strokeStyle = colors.minChartLine; ctx.lineWidth = 1.5; ctx.beginPath(); ctx.moveTo(x.getPixelForValue(xPosition) + 2, height / 2 - radius / 2); ctx.lineTo(x.getPixelForValue(xPosition) + 2, height / 2 + radius / 2); ctx.stroke(); ctx.restore(); }; slideDot(radius, new Date(min)); slideDot(radius, new Date(max)); }; zoomBoxItem(min, max); canvas.addEventListener("mousemove", (e) => { //changing cursor on slider mouseCursor(e); }); //changing cursor on slider const mouseCursor = (event) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5; if (((_e = (_d = (_c = (_b = (_a = chartRef.current) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.options) === null || _c === void 0 ? void 0 : _c.scales) === null || _d === void 0 ? void 0 : _d.x) === null || _e === void 0 ? void 0 : _e.min) !== undefined && ((_k = (_j = (_h = (_g = (_f = chartRef.current) === null || _f === void 0 ? void 0 : _f.config) === null || _g === void 0 ? void 0 : _g.options) === null || _h === void 0 ? void 0 : _h.scales) === null || _j === void 0 ? void 0 : _j.x) === null || _k === void 0 ? void 0 : _k.max) !== undefined) { let minChart1 = (_q = (_p = (_o = (_m = (_l = chartRef.current) === null || _l === void 0 ? void 0 : _l.config) === null || _m === void 0 ? void 0 : _m.options) === null || _o === void 0 ? void 0 : _o.scales) === null || _p === void 0 ? void 0 : _p.x) === null || _q === void 0 ? void 0 : _q.min; if (minChart1 === undefined || minChart1 === -1) minChart1 = dataset[0].Date; if ((event.offsetX > x.getPixelForValue(new Date(minChart1)) - radius && event.offsetX <= x.getPixelForValue(new Date(minChart1)) + radius) || (event.offsetX > x.getPixelForValue(new Date((_v = (_u = (_t = (_s = (_r = chartRef.current) === null || _r === void 0 ? void 0 : _r.config) === null || _s === void 0 ? void 0 : _s.options) === null || _t === void 0 ? void 0 : _t.scales) === null || _u === void 0 ? void 0 : _u.x) === null || _v === void 0 ? void 0 : _v.max)) - radius && event.offsetX <= x.getPixelForValue(new Date((_0 = (_z = (_y = (_x = (_w = chartRef.current) === null || _w === void 0 ? void 0 : _w.config) === null || _x === void 0 ? void 0 : _x.options) === null || _y === void 0 ? void 0 : _y.scales) === null || _z === void 0 ? void 0 : _z.x) === null || _0 === void 0 ? void 0 : _0.max)) + radius)) canvas.style.cursor = "ew-resize"; else if (event.offsetX > x.getPixelForValue(new Date(minChart1)) + radius && event.offsetX < x.getPixelForValue(new Date((_5 = (_4 = (_3 = (_2 = (_1 = chartRef.current) === null || _1 === void 0 ? void 0 : _1.config) === null || _2 === void 0 ? void 0 : _2.options) === null || _3 === void 0 ? void 0 : _3.scales) === null || _4 === void 0 ? void 0 : _4.x) === null || _5 === void 0 ? void 0 : _5.max)) - radius) canvas.style.cursor = "move"; else canvas.style.cursor = "default"; } }; canvas.addEventListener("mousedown", (e) => { dragStart(e); }); window.addEventListener("mouseup", (e) => { canvas.onmousemove = null; }); //making it slide const dragStart = (event) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; if (((_e = (_d = (_c = (_b = (_a = chartRef.current) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.options) === null || _c === void 0 ? void 0 : _c.scales) === null || _d === void 0 ? void 0 : _d.x) === null || _e === void 0 ? void 0 : _e.min) !== undefined && ((_k = (_j = (_h = (_g = (_f = chartRef.current) === null || _f === void 0 ? void 0 : _f.config) === null || _g === void 0 ? void 0 : _g.options) === null || _h === void 0 ? void 0 : _h.scales) === null || _j === void 0 ? void 0 : _j.x) === null || _k === void 0 ? void 0 : _k.max) !== undefined) { let minChart1 = (_q = (_p = (_o = (_m = (_l = chartRef.current) === null || _l === void 0 ? void 0 : _l.config) === null || _m === void 0 ? void 0 : _m.options) === null || _o === void 0 ? void 0 : _o.scales) === null || _p === void 0 ? void 0 : _p.x) === null || _q === void 0 ? void 0 : _q.min; if (minChart1 === undefined || minChart1 === -1) minChart1 = dataset[0].Date; let maxChart1 = (_v = (_u = (_t = (_s = (_r = chartRef.current) === null || _r === void 0 ? void 0 : _r.config) === null || _s === void 0 ? void 0 : _s.options) === null || _t === void 0 ? void 0 : _t.scales) === null || _u === void 0 ? void 0 : _u.x) === null || _v === void 0 ? void 0 : _v.max; if (maxChart1 === undefined || maxChart1 === -1) maxChart1 = dataset[dataset.length - 1].Date; //left side if (event.offsetX >= x.getPixelForValue(new Date(minChart1)) - radius && event.offsetX <= x.getPixelForValue(new Date(minChart1)) + radius) { canvas.onmousemove = (e) => { dragMove(e); }; const dragMove = (event) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5; const timestamp = x.getValueForPixel(event.offsetX); const dayTimestamp = new Date(timestamp).setHours(0, 0, 0, 0); let scrollPoint = dataset .map((e) => new Date(e.Date).setHours(0, 0, 0, 0).toLocaleString()) .indexOf(dayTimestamp.toLocaleString()); //if too much on left if (event.offsetX < left && scrollPoint === -1) scrollPoint = 0; //if too much on right if (event.offsetX > right && scrollPoint === -1 && ((_e = (_d = (_c = (_b = (_a = chartRef.current) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.options) === null || _c === void 0 ? void 0 : _c.scales) === null || _d === void 0 ? void 0 : _d.x) === null || _e === void 0 ? void 0 : _e.max) !== undefined) { scrollPoint = dataset .map((e) => new Date(e.Date).setHours(0, 0, 0, 0).toLocaleString()) .indexOf(new Date((_k = (_j = (_h = (_g = (_f = chartRef.current) === null || _f === void 0 ? void 0 : _f.config) === null || _g === void 0 ? void 0 : _g.options) === null || _h === void 0 ? void 0 : _h.scales) === null || _j === void 0 ? void 0 : _j.x) === null || _k === void 0 ? void 0 : _k.max) .setHours(0, 0, 0, 0) .toLocaleString()) - 4; } //if left one is beyond the right one if (((_p = (_o = (_m = (_l = chartRef.current) === null || _l === void 0 ? void 0 : _l.config.options) === null || _m === void 0 ? void 0 : _m.scales) === null || _o === void 0 ? void 0 : _o.x) === null || _p === void 0 ? void 0 : _p.max) !== undefined) { if (scrollPoint > dataset .map((e) => new Date(e.Date).setHours(0, 0, 0, 0).toLocaleString()) .indexOf(new Date((_r = (_q = chartRef.current) === null || _q === void 0 ? void 0 : _q.config.options) === null || _r === void 0 ? void 0 : _r.scales.x.max) .setHours(0, 0, 0, 0) .toLocaleString()) - 4) { scrollPoint = dataset .map((e) => new Date(e.Date).setHours(0, 0, 0, 0).toLocaleString()) .indexOf(new Date((_t = (_s = chartRef.current) === null || _s === void 0 ? void 0 : _s.config.options) === null || _t === void 0 ? void 0 : _t.scales.x.max) .setHours(0, 0, 0, 0) .toLocaleString()) - 4; } } if (((_y = (_x = (_w = (_v = (_u = chartRef.current) === null || _u === void 0 ? void 0 : _u.config) === null || _v === void 0 ? void 0 : _v.options) === null || _w === void 0 ? void 0 : _w.scales) === null || _x === void 0 ? void 0 : _x.x) === null || _y === void 0 ? void 0 : _y.min) !== undefined) { //updating the chart chartRef.current.config.options.scales.x.min = dataset[scrollPoint].Date; } (_z = chartRef.current) === null || _z === void 0 ? void 0 : _z.update("none"); (_0 = chartRef2.current) === null || _0 === void 0 ? void 0 : _0.update("none"); //updating the slider zoomBoxItem(dataset[scrollPoint].Date, (_5 = (_4 = (_3 = (_2 = (_1 = chartRef.current) === null || _1 === void 0 ? void 0 : _1.config) === null || _2 === void 0 ? void 0 : _2.options) === null || _3 === void 0 ? void 0 : _3.scales) === null || _4 === void 0 ? void 0 : _4.x) === null || _5 === void 0 ? void 0 : _5.max); }; } //right side if (event.offsetX >= x.getPixelForValue(new Date((_0 = (_z = (_y = (_x = (_w = chartRef.current) === null || _w === void 0 ? void 0 : _w.config) === null || _x === void 0 ? void 0 : _x.options) === null || _y === void 0 ? void 0 : _y.scales) === null || _z === void 0 ? void 0 : _z.x) === null || _0 === void 0 ? void 0 : _0.max)) - radius && event.offsetX <= x.getPixelForValue(new Date((_5 = (_4 = (_3 = (_2 = (_1 = chartRef.current) === null || _1 === void 0 ? void 0 : _1.config) === null || _2 === void 0 ? void 0 : _2.options) === null || _3 === void 0 ? void 0 : _3.scales) === null || _4 === void 0 ? void 0 : _4.x) === null || _5 === void 0 ? void 0 : _5.max)) + radius) { canvas.onmousemove = (e) => { dragMove(chartRef.current, e); }; const dragMove = (chart, event) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10; const timestamp = x.getValueForPixel(event.offsetX); const dayTimestamp = new Date(timestamp).setHours(0, 0, 0, 0); let scrollPoint = dataset .map((e) => new Date(e.Date).setHours(0, 0, 0, 0).toLocaleString()) .indexOf(dayTimestamp.toLocaleString()); //too much on right if (event.offsetX > right && scrollPoint === -1 && ((_e = (_d = (_c = (_b = (_a = chartRef.current) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.options) === null || _c === void 0 ? void 0 : _c.scales) === null || _d === void 0 ? void 0 : _d.x) === null || _e === void 0 ? void 0 : _e.min) !== undefined) scrollPoint = dataset.length - 1; //too much on left if (event.offsetX < left && scrollPoint === -1 && ((_k = (_j = (_h = (_g = (_f = chartRef.current) === null || _f === void 0 ? void 0 : _f.config) === null || _g === void 0 ? void 0 : _g.options) === null || _h === void 0 ? void 0 : _h.scales) === null || _j === void 0 ? void 0 : _j.x) === null || _k === void 0 ? void 0 : _k.min) !== undefined) { scrollPoint = dataset .map((e) => new Date(e.Date).setHours(0, 0, 0, 0).toLocaleString()) .indexOf(new Date((_q = (_p = (_o = (_m = (_l = chartRef.current) === null || _l === void 0 ? void 0 : _l.config) === null || _m === void 0 ? void 0 : _m.options) === null || _o === void 0 ? void 0 : _o.scales) === null || _p === void 0 ? void 0 : _p.x) === null || _q === void 0 ? void 0 : _q.min) .setHours(0, 0, 0, 0) .toLocaleString()) + 4; } //the right slider is beyond the left one if (((_u = (_t = (_s = (_r = chartRef.current) === null || _r === void 0 ? void 0 : _r.config.options) === null || _s === void 0 ? void 0 : _s.scales) === null || _t === void 0 ? void 0 : _t.x) === null || _u === void 0 ? void 0 : _u.min) !== undefined) { if (scrollPoint < dataset .map((e) => new Date(e.Date).setHours(0, 0, 0, 0).toLocaleString()) .indexOf(new Date((_w = (_v = chartRef.current) === null || _v === void 0 ? void 0 : _v.config.options) === null || _w === void 0 ? void 0 : _w.scales.x.min) .setHours(0, 0, 0, 0) .toLocaleString()) + 4) scrollPoint = dataset .map((e) => new Date(e.Date).setHours(0, 0, 0, 0).toLocaleString()) .indexOf(new Date((_y = (_x = chartRef.current) === null || _x === void 0 ? void 0 : _x.config.options) === null || _y === void 0 ? void 0 : _y.scales.x.min) .setHours(0, 0, 0, 0) .toLocaleString()) + 4; } if (((_3 = (_2 = (_1 = (_0 = (_z = chartRef.current) === null || _z === void 0 ? void 0 : _z.config) === null || _0 === void 0 ? void 0 : _0.options) === null || _1 === void 0 ? void 0 : _1.scales) === null || _2 === void 0 ? void 0 : _2.x) === null || _3 === void 0 ? void 0 : _3.max) !== undefined) { //updating the chart chartRef.current.config.options.scales.x.max = dataset[scrollPoint].Date; } (_4 = chartRef.current) === null || _4 === void 0 ? void 0 : _4.update("none"); (_5 = chartRef2.current) === null || _5 === void 0 ? void 0 : _5.update("none"); //updating the slider zoomBoxItem((_10 = (_9 = (_8 = (_7 = (_6 = chartRef.current) === null || _6 === void 0 ? void 0 : _6.config) === null || _7 === void 0 ? void 0 : _7.options) === null || _8 === void 0 ? void 0 : _8.scales) === null || _9 === void 0 ? void 0 : _9.x) === null || _10 === void 0 ? void 0 : _10.min, dataset[scrollPoint].Date); }; } if (event.offsetX > x.getPixelForValue(new Date((_10 = (_9 = (_8 = (_7 = (_6 = chartRef.current) === null || _6 === void 0 ? void 0 : _6.config) === null || _7 === void 0 ? void 0 : _7.options) === null || _8 === void 0 ? void 0 : _8.scales) === null || _9 === void 0 ? void 0 : _9.x) === null || _10 === void 0 ? void 0 : _10.min)) + radius + 1 && event.offsetX < x.getPixelForValue(new Date((_15 = (_14 = (_13 = (_12 = (_11 = chartRef.current) === null || _11 === void 0 ? void 0 : _11.config) === null || _12 === void 0 ? void 0 : _12.options) === null || _13 === void 0 ? void 0 : _13.scales) === null || _14 === void 0 ? void 0 : _14.x) === null || _15 === void 0 ? void 0 : _15.max)) - radius + 1) { canvas.onmousemove = (e) => { try { dragMoveCenter(chartRef.current, e, minChart1, maxChart1); } catch (_a) { } }; } const dragMoveCenter = (chart, e, staticScaleMin, staticScaleMax) => { var _a, _b; //starting point const dragStartingPoint = x.getValueForPixel(event.offsetX); const dayDragStartingPoint = new Date(dragStartingPoint).setHours(0, 0, 0, 0); let startingPointIndex = dataset .map((e) => new Date(e.Date).setHours(0, 0, 0, 0).toLocaleString()) .indexOf(dayDragStartingPoint.toLocaleString()); //difference const timestamp = x.getValueForPixel(e.offsetX); const dayTimestamp = new Date(timestamp).setHours(0, 0, 0, 0); let scrollPoint = dataset .map((e) => new Date(e.Date).setHours(0, 0, 0, 0).toLocaleString()) .indexOf(dayTimestamp.toLocaleString()); const difference = scrollPoint - startingPointIndex; if (scrollPoint === -1 && e.offsetX >= right) { scrollPoint = dataset.length - 1; } const range = dataset .map((e) => new Date(e.Date).setHours(0, 0, 0, 0).toLocaleString()) .indexOf(new Date(staticScaleMin).setHours(0, 0, 0, 0).toLocaleString()) - dataset .map((e) => new Date(e.Date).setHours(0, 0, 0, 0).toLocaleString()) .indexOf(new Date(staticScaleMax).setHours(0, 0, 0, 0).toLocaleString()); const minVal = dataset .map((e) => new Date(e.Date).setHours(0, 0, 0, 0).toLocaleString()) .indexOf(new Date(staticScaleMax).setHours(0, 0, 0, 0).toLocaleString()) + difference - range; const maxVal = dataset .map((e) => new Date(e.Date).setHours(0, 0, 0, 0).toLocaleString()) .indexOf(new Date(staticScaleMax).setHours(0, 0, 0, 0).toLocaleString()) + difference; let minChart1; let maxChart1; if (minVal < 0 && e.offsetX < right) { minChart1 = dataset[0].Date; maxChart1 = dataset[range].Date; } else if (maxVal > dataset.length - 1 || (difference < 0 && e.offsetX >= right)) { minChart1 = dataset[dataset.length - 1 - range].Date; maxChart1 = dataset[dataset.length - 1].Date; } else { minChart1 = dataset[dataset .map((e) => new Date(e.Date).setHours(0, 0, 0, 0).toLocaleString()) .indexOf(new Date(staticScaleMin) .setHours(0, 0, 0, 0) .toLocaleString()) + difference].Date; maxChart1 = dataset[dataset .map((e) => new Date(e.Date).setHours(0, 0, 0, 0).toLocaleString()) .indexOf(new Date(staticScaleMax) .setHours(0, 0, 0, 0) .toLocaleString()) + difference].Date; } if (maxChart1 === undefined) maxChart1 = dataset[dataset.length - 1].Date; if (minChart1 === undefined) maxChart1 = dataset[0].Date; chart.config.options.scales.x.min = minChart1; chart.config.options.scales.x.max = maxChart1; (_a = chartRef.current) === null || _a === void 0 ? void 0 : _a.update("none"); (_b = chartRef2.current) === null || _b === void 0 ? void 0 : _b.update("none"); //updating the slider zoomBoxItem(minChart1, maxChart1); }; } }; }; const tooltipPosition = (e) => { const { ctx, chartArea: { top, bottom, left, right }, scales: { x }, } = chartRef.current; let xTooltip; let yTooltip; const rightSide = right - e.offsetX; const margin = 20; const tooltipWidth = 200; const tooltipHeight = 100; const xPadding = 10; const yPadding = 20; const gap = 30; if (rightSide <= tooltipWidth + margin) { xTooltip = e.offsetX - (tooltipWidth + margin); } else xTooltip = e.offsetX + margin; if (e.offsetY <= tooltipHeight + 2 * margin) yTooltip = e.offsetY + margin; else yTooltip = e.offsetY - (tooltipHeight + margin); if (e.offsetX >= left && e.offsetX <= right && e.offsetY >= top && e.offsetY <= bottom) { ctx.beginPath(); ctx.fillStyle = colors.tooltip; ctx.strokeStyle = colors.tooltip; ctx.lineJoin = "round"; ctx.lineWidth = 5; ctx.fillRect(xTooltip, yTooltip, tooltipWidth, tooltipHeight); ctx.strokeRect(xTooltip, yTooltip, tooltipWidth, tooltipHeight); ctx.closePath(); ctx.restore(); //header const dateCursor = new Date(x.getValueForPixel(e.offsetX)); const dateIndex = dataset .map((el) => new Date(el.Date).setHours(0, 0, 0, 0).toLocaleString()) .indexOf(dateCursor.setHours(0, 0, 0, 0).toLocaleString()); const drawText = (align, baseLine, fillStyle, font, text, xPosition, yPosition) => { ctx.textAlign = align; ctx.textBaseLine = baseLine; ctx.fillStyle = fillStyle; ctx.font = font; ctx.fillText(text, xPosition, yPosition); ctx.restore(); }; //date drawText("left", "middle", colors.tooltipDate, "bolder 12px Sora", dateCursor.toLocaleDateString(), xTooltip + xPadding, yTooltip + yPadding); //time drawText("right", "middle", colors.tooltipTime, "bolder 10px Sora", new Date(x.getValueForPixel(e.offsetX)).toLocaleTimeString(), xTooltip + tooltipWidth - xPadding, yTooltip + yPadding); let dotColor; if (dataset[dateIndex].Open < dataset[0].Open) { dotColor = colors.below; } else dotColor = colors.above; //colored dot 1 const dotSpace = 20; drawText("left", "middle", dotColor, "10px FontAwesome", "\uf111", xTooltip + xPadding, yTooltip + yPadding + gap); //colored dot 1 text const priceText = "Price: "; const priceTextWidth = ctx.measureText(priceText).width + 13; drawText("left", "middle", colors.tooltipData, "12px Sora", priceText, xTooltip + xPadding + dotSpace, yTooltip + yPadding + gap); //colored dot 1 value drawText("left", "middle", colors.tooltipData, "12px Sora", new Intl.NumberFormat("en-US", { style: "currency", currency: currency, minimumFractionDigits: 2, }).format(dataset[dateIndex].Open), xTooltip + dotSpace + xPadding + priceTextWidth, yTooltip + yPadding + gap); //colored dot 2 drawText("left", "middle", "white", "12px FontAwesome", "\uf080", xTooltip + xPadding, yTooltip + yPadding + 2 * gap); //colored dot 2 text const volumeText = "Volume: "; const volumeTextWidth = ctx.measureText(volumeText).width + 8; drawText("left", "middle", colors.tooltipData, "12px Sora", volumeText, xTooltip + xPadding + dotSpace, yTooltip + yPadding + 2 * gap); //colored dot 2 value drawText("left", "middle", colors.tooltipData, "12px Sora", new Intl.NumberFormat("en-US", { style: "currency", currency: currency, minimumFractionDigits: 2, notation: "compact", }).format(dataset[dateIndex].Volume), xTooltip + dotSpace + xPadding + volumeTextWidth, yTooltip + yPadding + 2 * gap); } }; useEffect(() => { if (chartRef.current) { chartRef.current.canvas.addEventListener("mousemove", (e) => { crosshairLine(chartRef.current, e); tooltipPosition(e); }); chartRef.current.canvas.addEventListener("wheel", (e) => { var _a, _b, _c, _d, _e, _f, _g, _h; zoom(chartRef.current, e); zoomBox((_d = (_c = (_b = (_a = chartRef.current) === null || _a === void 0 ? void 0 : _a.config.options) === null || _b === void 0 ? void 0 : _b.scales) === null || _c === void 0 ? void 0 : _c.x) === null || _d === void 0 ? void 0 : _d.min, (_h = (_g = (_f = (_e = chartRef.current) === null || _e === void 0 ? void 0 : _e.config.options) === null || _f === void 0 ? void 0 : _f.scales) === null || _g === void 0 ? void 0 : _g.x) === null || _h === void 0 ? void 0 : _h.max); e.preventDefault(); }); zoomBox(dataset[0].Date, dataset[dataset.length - 1].Date); window.addEventListener("resize", (e) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l; (_a = chartRef2.current) === null || _a === void 0 ? void 0 : _a.resize(); if (((_e = (_d = (_c = (_b = chartRef.current) === null || _b === void 0 ? void 0 : _b.config.options) === null || _c === void 0 ? void 0 : _c.scales) === null || _d === void 0 ? void 0 : _d.x) === null || _e === void 0 ? void 0 : _e.min) !== undefined) zoomBox((_h = (_g = (_f = chartRef.current) === null || _f === void 0 ? void 0 : _f.config.options) === null || _g === void 0 ? void 0 : _g.scales.x) === null || _h === void 0 ? void 0 : _h.min, (_l = (_k = (_j = chartRef.current) === null || _j === void 0 ? void 0 : _j.config.options) === null || _k === void 0 ? void 0 : _k.scales.x) === null || _l === void 0 ? void 0 : _l.max); }); } }); const options = { animations: false, layout: { padding: { left: 10, //right: 50 right: 0, }, }, scales: { y: { beginAtZero: true, ticks: { callback: function (value, index, ticks) { return (new Intl.NumberFormat("en-US", { style: "currency", currency: currency, minimumFractionDigits: 0, notation: "compact", }).format(value) + " "); }, }, max: Math.ceil(Math.max(...dataset.map((el) => el.Open + 1000)) / 1000) * 1000, }, x: { type: "time", time: { unit: "day" }, grid: { drawOnChartArea: false, drawTicks: true, drawBorder: false, offset: false, }, min: dataset[0].Date, max: dataset[dataset.length - 1].Date, ticks: { callback: (value, index, values) => { const totalTicks = values.length - 2; const monthArray = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ]; const currentTick = new Date(values[index].value); if (currentTick.getDate() === 1) { return monthArray[currentTick.getMonth()]; } if (currentTick.getDate() === 10 || currentTick.getDate() === 20) { return currentTick.getDate(); } if (totalTicks < 60) { return currentTick.getDate(); } }, font: { weight: (values) => { if (values.tick.label.length === 3) return "bold"; }, }, }, }, volume: { type: "linear", position: "right", min: 0, max: Math.max(...dataset.map((el) => el.Volume)) * 10, grid: { display: false }, ticks: { display: false }, }, }, plugins: { legend: { display: false }, tooltip: { enabled: false } }, }; const data = { labels: dataset.map((el) => { const date = new Date(el.Date); date.setHours(0, 0, 0, 0); return date; }), datasets: [ { label: "Weekly Sales", data: dataset.map((el) => el.Open), fill: { target: { value: dataset[0].Open }, below: (context) => { const chart = context.chart; const { ctx, chartArea, data, scales } = chart; if (!chartArea) return null; return belowGradient(ctx, chartArea, data, scales); }, above: (context) => { const chart = context.chart; const { ctx, chartArea, data, scales } = chart; if (!chartArea) return null; return aboveGradient(ctx, chartArea, data, scales); }, }, borderColor: (context) => { const chart = context.chart; const { ctx, chartArea, data, scales } = chart; if (!chartArea) return null; return getGradient(ctx, chartArea, data, scales); }, tension: 0, pointRadius: 0, pointHitRadius: 0, pointHoverRadius: 0, borderWidth: 2, }, { label: "Stock Volume", type: "bar", data: dataset.map((el) => el.Volume), backgroundColor: barChartColor, pointRadius: 0, pointHitRadius: 0, pointHoverRadius: 0, yAxisID: "volume", barPercentage: 0.9, categoryPercentage: 1, }, ], }; const options2 = { layout: { padding: { left: 50, //right: 58, right: 8, }, }, aspectRatio: 10, animation: false, scales: { y: { beginAtZero: true, grid: { display: false, drawBorder: false }, ticks: { display: false }, max: Math.ceil(Math.max(...dataset.map((el) => el.Open + 1000)) / 1000) * 1000, }, x: { type: "time", time: { unit: "day" }, grid: { drawBorder: false, drawTicks: false }, ticks: { callback: (value, index, values) => { const totalTicks = values.length - 2; const monthArray = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ]; const currentTick = new Date(values[index].value); if (currentTick.getDate() === 1) { return monthArray[currentTick.getMonth()]; } if (currentTick.getDate() === 10 || currentTick.getDate() === 20) { return currentTick.getDate(); } if (totalTicks < 60) { return currentTick.getDate(); } }, font: { weight: (values) => { if (values.tick.label.length === 3) return "bold"; }, }, mirror: true, }, min: dataset[0].Date, max: dataset[dataset.length - 1].Date, }, }, plugins: { legend: { display: false }, tooltip: { enabled: false } }, }; const data2 = { labels: dataset.map((el) => { const date = new Date(el.Date); date.setHours(0, 0, 0, 0); return date; }), datasets: [ { data: dataset.map((el) => el.Open), borderColor: colors.minChartLine, backgroundColor: colors.minChartBackground, fill: true, tension: 0, pointRadius: 0, pointHitRadius: 0, pointHoverRadius: 0, borderWidth: 1, width: "100%", }, ], }; const dottedLine = { id: "dottedLine", beforeDatasetsDraw(chart, args, pluginOptions) { const { ctx, data, chartArea: { left, right }, scales: { y }, } = chart; const height = 16; const startingPoint = data.datasets[0].data[0]; ctx.save(); ctx.beginPath(); ctx.lineWidth = 1; ctx.setLineDash([1, 5]); ctx.strokeStyle = "rgba(102, 102, 102, 1)"; ctx.moveTo(left, y.getPixelForValue(startingPoint)); ctx.lineTo(right, y.getPixelForValue(startingPoint)); ctx.stroke(); ctx.closePath(); ctx.setLineDash([]); ctx.beginPath(); ctx.fillStyle = colors.openLabel; ctx.fillRect(0, y.getPixelForValue(startingPoint) - height / 2, left, height); ctx.closePath(); ctx.font = "12px Sora"; ctx.fillStyle = "white"; ctx.baseLine = "middle"; ctx.textAlign = "center"; ctx.fillText(startingPoint.toFixed(2), left / 2, y.getPixelForValue(startingPoint) + 4); }, }; return (React.createElement(React.Fragment, null, React.createElement(ChartRJS, { type: "line", data: data, options: options, plugins: [dottedLine], ref: chartRef }), React.createElement("div", { style: { fontSize: "12px", fontFamily: font, color: "rgb(82, 82, 82)", fontWeight: "bold", marginLeft: "20px", marginTop: "-10px", } }, currency.toUpperCase()), React.createElement(ChartRJS, { type: "line", data: data2, options: options2, plugins: [], ref: chartRef2 }))); };