Skip to content

Commit f40849a

Browse files
committed
update charts on DAT company pages
1 parent cd44744 commit f40849a

File tree

4 files changed

+344
-51
lines changed

4 files changed

+344
-51
lines changed
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
import { useCallback, useEffect, useId, useMemo } from 'react'
2+
import { BarChart, CandlestickChart } from 'echarts/charts'
3+
import {
4+
DataZoomComponent,
5+
GraphicComponent,
6+
GridComponent,
7+
MarkLineComponent,
8+
TooltipComponent
9+
} from 'echarts/components'
10+
import * as echarts from 'echarts/core'
11+
import { CanvasRenderer } from 'echarts/renderers'
12+
import { oldBlue } from '~/constants/colors'
13+
import { useDarkModeManager } from '~/contexts/LocalStorage'
14+
import { useMedia } from '~/hooks/useMedia'
15+
import type { ICandlestickChartProps } from '../types'
16+
import { formatChartEmphasisDate, formatTooltipChartDate, formatTooltipValue } from '../useDefaults'
17+
18+
echarts.use([
19+
CanvasRenderer,
20+
CandlestickChart,
21+
BarChart,
22+
TooltipComponent,
23+
GridComponent,
24+
DataZoomComponent,
25+
GraphicComponent,
26+
MarkLineComponent
27+
])
28+
29+
export default function CandleStickAndVolumeChart({ data }: ICandlestickChartProps) {
30+
const id = useId()
31+
32+
const [isThemeDark] = useDarkModeManager()
33+
const isSmall = useMedia(`(max-width: 37.5rem)`)
34+
35+
const defaultChartSettings = useMemo(() => {
36+
return {
37+
graphic: {
38+
type: 'image',
39+
z: 0,
40+
style: {
41+
image: isThemeDark ? '/icons/defillama-light-neutral.webp' : '/icons/defillama-dark-neutral.webp',
42+
height: 40,
43+
opacity: 0.3
44+
},
45+
left: isSmall ? '40%' : '45%',
46+
top: '130px'
47+
},
48+
tooltip: {
49+
trigger: 'axis',
50+
axisPointer: {
51+
type: 'line'
52+
},
53+
formatter: (params) => {
54+
let chartdate = formatTooltipChartDate(params[0].value[0], 'daily')
55+
56+
let vals = ''
57+
vals += '<li style="list-style:none">' + params[0].marker + '</li>'
58+
for (const param of params) {
59+
vals += `<li style="list-style:none">Open<span style="float: right; margin-left: 20px"><b>${formatTooltipValue(param.value[1], '$')}</b></span></li>`
60+
vals += `<li style="list-style:none">Highest<span style="float: right; margin-left: 20px"><b>${formatTooltipValue(param.value[4], '$')}</b></span></li>`
61+
vals += `<li style="list-style:none">Lowest<span style="float: right; margin-left: 20px"><b>${formatTooltipValue(param.value[3], '$')}</b></span></li>`
62+
vals += `<li style="list-style:none">Close<span style="float: right; margin-left: 20px"><b>${formatTooltipValue(param.value[2], '$')}</b></span></li>`
63+
}
64+
65+
return chartdate + vals
66+
}
67+
},
68+
grid: [
69+
{
70+
left: 12,
71+
bottom: 132,
72+
top: 12,
73+
right: 12,
74+
outerBoundsMode: 'same',
75+
outerBoundsContain: 'axisLabel'
76+
},
77+
{
78+
height: 80,
79+
left: 54,
80+
right: 12,
81+
bottom: 52,
82+
outerBoundsMode: 'same',
83+
outerBoundsContain: 'axisLabel'
84+
}
85+
],
86+
xAxis: [
87+
{
88+
type: 'time',
89+
boundaryGap: false,
90+
axisLine: {
91+
onZero: false,
92+
lineStyle: {
93+
color: isThemeDark ? 'rgba(255, 255, 255, 1)' : 'rgba(0, 0, 0, 1)',
94+
opacity: 0.2
95+
}
96+
},
97+
splitLine: { show: false },
98+
min: 'dataMin',
99+
max: 'dataMax',
100+
nameTextStyle: {
101+
fontFamily: 'sans-serif',
102+
fontSize: 14,
103+
fontWeight: 400
104+
},
105+
axisLabel: {
106+
color: isThemeDark ? 'rgba(255, 255, 255, 1)' : 'rgba(0, 0, 0, 1)'
107+
}
108+
},
109+
{
110+
type: 'time',
111+
gridIndex: 1,
112+
boundaryGap: false,
113+
axisLine: {
114+
onZero: false,
115+
lineStyle: {
116+
color: isThemeDark ? 'rgba(255, 255, 255, 1)' : 'rgba(0, 0, 0, 1)',
117+
opacity: 0.2
118+
}
119+
},
120+
axisTick: { show: false },
121+
splitLine: { show: false },
122+
axisLabel: { show: false },
123+
min: 'dataMin',
124+
max: 'dataMax',
125+
nameTextStyle: {
126+
fontFamily: 'sans-serif',
127+
fontSize: 14,
128+
fontWeight: 400
129+
}
130+
}
131+
],
132+
yAxis: [
133+
{
134+
scale: true,
135+
splitArea: {
136+
show: true,
137+
areaStyle: {
138+
color: [isThemeDark ? 'rgba(255,255,255,0.02)' : 'rgba(0,0,0,0.02)', '']
139+
}
140+
},
141+
nameTextStyle: {
142+
fontFamily: 'sans-serif',
143+
fontSize: 14,
144+
fontWeight: 400
145+
},
146+
axisLabel: {
147+
formatter: (value) => formatTooltipValue(value, '$'),
148+
color: isThemeDark ? 'rgba(255, 255, 255, 1)' : 'rgba(0, 0, 0, 1)'
149+
},
150+
axisLine: {
151+
lineStyle: {
152+
color: isThemeDark ? 'rgba(255, 255, 255, 1)' : 'rgba(0, 0, 0, 1)',
153+
opacity: 0.1
154+
}
155+
},
156+
splitLine: {
157+
lineStyle: {
158+
color: isThemeDark ? '#ffffff' : '#000000',
159+
opacity: isThemeDark ? 0.02 : 0.03
160+
}
161+
}
162+
},
163+
{
164+
scale: true,
165+
gridIndex: 1,
166+
splitNumber: 2,
167+
axisLabel: { show: false },
168+
axisLine: { show: false },
169+
axisTick: { show: false },
170+
splitLine: { show: false },
171+
nameTextStyle: {
172+
fontFamily: 'sans-serif',
173+
fontSize: 14,
174+
fontWeight: 400
175+
}
176+
}
177+
],
178+
dataZoom: [
179+
{
180+
type: 'inside',
181+
xAxisIndex: [0, 1],
182+
start: 10,
183+
end: 100
184+
},
185+
{
186+
show: true,
187+
xAxisIndex: [0, 1],
188+
type: 'slider',
189+
bottom: 10,
190+
start: 10,
191+
end: 100,
192+
textStyle: {
193+
color: isThemeDark ? 'rgba(255, 255, 255, 1)' : 'rgba(0, 0, 0, 1)'
194+
},
195+
borderColor: isThemeDark ? 'rgba(255, 255, 255, 0.4)' : 'rgba(0, 0, 0, 0.4)',
196+
handleStyle: {
197+
borderColor: isThemeDark ? 'rgba(255, 255, 255, 0.9)' : 'rgba(0, 0, 0, 0.9)',
198+
color: isThemeDark ? 'rgba(0, 0, 0, 0.4)' : 'rgba(255, 255, 255, 0.4)'
199+
},
200+
moveHandleStyle: {
201+
color: isThemeDark ? 'rgba(255, 255, 255, 0.4)' : 'rgba(0, 0, 0, 0.4)'
202+
},
203+
selectedDataBackground: {
204+
lineStyle: {
205+
color: oldBlue
206+
},
207+
areaStyle: {
208+
color: oldBlue
209+
}
210+
},
211+
emphasis: {
212+
handleStyle: {
213+
borderColor: isThemeDark ? 'rgba(255, 255, 255, 1)' : '#000',
214+
color: isThemeDark ? 'rgba(255, 255, 255, 0.9)' : 'rgba(0, 0, 0, 0.9)'
215+
},
216+
moveHandleStyle: {
217+
borderColor: isThemeDark ? 'rgba(255, 255, 255, 1)' : '#000',
218+
color: isThemeDark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'
219+
}
220+
},
221+
fillerColor: isThemeDark ? 'rgba(0, 0, 0, 0.1)' : 'rgba(255, 255, 255, 0.1)',
222+
labelFormatter: formatChartEmphasisDate
223+
}
224+
],
225+
visualMap: {
226+
show: false,
227+
seriesIndex: 1,
228+
dimension: 6,
229+
pieces: [
230+
{
231+
value: 1,
232+
color: isThemeDark ? '#3eb84f' : '#018a13'
233+
},
234+
{
235+
value: -1,
236+
color: isThemeDark ? '#e24a42' : '#e60d02'
237+
}
238+
]
239+
}
240+
}
241+
}, [isSmall, isThemeDark])
242+
243+
const series = useMemo(() => {
244+
const series = [
245+
{
246+
type: 'candlestick',
247+
symbol: 'none',
248+
large: true,
249+
data: data.map((item) => [item[0], item[1], item[2], item[3], item[4]]),
250+
itemStyle: {
251+
color: isThemeDark ? '#3eb84f' : '#018a13',
252+
color0: isThemeDark ? '#e24a42' : '#e60d02',
253+
borderColor: isThemeDark ? '#3eb84f' : '#018a13',
254+
borderColor0: isThemeDark ? '#e24a42' : '#e60d02'
255+
},
256+
encode: {
257+
x: 0,
258+
y: [1, 2, 3, 4]
259+
}
260+
},
261+
{
262+
name: 'Volume',
263+
type: 'bar',
264+
symbol: 'none',
265+
large: true,
266+
data: data.map((item) => [item[0], item[5]]),
267+
xAxisIndex: 1,
268+
yAxisIndex: 1,
269+
encode: { x: 0, y: 5 },
270+
itemStyle: { color: oldBlue }
271+
}
272+
]
273+
274+
return series
275+
}, [data, isThemeDark])
276+
277+
const createInstance = useCallback(() => {
278+
const instance = echarts.getInstanceByDom(document.getElementById(id))
279+
280+
return instance || echarts.init(document.getElementById(id))
281+
}, [id])
282+
283+
useEffect(() => {
284+
// create instance
285+
const chartInstance = createInstance()
286+
287+
chartInstance.setOption({
288+
...defaultChartSettings,
289+
dataset: {
290+
source: data
291+
},
292+
series
293+
})
294+
295+
function resize() {
296+
chartInstance.resize()
297+
}
298+
299+
window.addEventListener('resize', resize)
300+
301+
return () => {
302+
window.removeEventListener('resize', resize)
303+
chartInstance.dispose()
304+
}
305+
}, [createInstance, series, data, defaultChartSettings])
306+
307+
return <div id={id} className="h-[480px]"></div>
308+
}

src/components/ECharts/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ export interface ILineAndBarChartProps {
9898
hideDefaultLegend?: boolean
9999
}
100100

101+
export interface ICandlestickChartProps {
102+
data: Array<[number, number, number, number, number, number]>
103+
}
104+
101105
export interface IMultiSeriesChartProps {
102106
series?: Array<{
103107
data: Array<[number, number]>

src/components/ECharts/useDefaults.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ export function formatTooltipChartDate(
508508
: `${date.getUTCDate().toString().padStart(2, '0')} ${monthNames[date.getUTCMonth()]} ${date.getUTCFullYear()}`
509509
}
510510

511-
function formatChartEmphasisDate(value: number) {
511+
export function formatChartEmphasisDate(value: number) {
512512
const date = new Date(value)
513513
return date.toLocaleDateString(undefined, {
514514
year: 'numeric',

0 commit comments

Comments
 (0)