@@ -4,12 +4,31 @@ import { DateTime } from "luxon"
4
4
import * as d3 from " d3"
5
5
import { useDebounceFn } from " @vueuse/core"
6
6
7
+ /** UI */
8
+ import { Dropdown , DropdownItem } from " @/components/ui/Dropdown"
9
+ import Button from " @/components/ui/Button.vue"
10
+
7
11
/** Services */
8
12
import { truncate } from " @/services/utils"
9
13
10
14
/** API */
11
15
import { fetchGasPriceSeries } from " @/services/api/gas"
12
16
17
+ const selectedPeriodIdx = ref (0 )
18
+ const periods = ref ([
19
+ {
20
+ title: " 24 hours" ,
21
+ value: 24 ,
22
+ timeframe: " hour" ,
23
+ },
24
+ {
25
+ title: " 31 days" ,
26
+ value: 30 ,
27
+ timeframe: " day" ,
28
+ },
29
+ ])
30
+ const selectedPeriod = computed (() => periods .value [selectedPeriodIdx .value ])
31
+
13
32
/** Chart El */
14
33
const chartWrapperEl = ref ()
15
34
const gasPriceChartEl = ref ()
@@ -71,12 +90,15 @@ const buildChart = (chartEl, data, onEnter, onLeave) => {
71
90
}
72
91
}
73
92
74
- badgeText .value = DateTime .fromJSDate (data[idx].date ).toFormat (" hh:mm a" )
93
+ badgeText .value =
94
+ selectedPeriod .value .timeframe === " day"
95
+ ? DateTime .fromJSDate (data[idx].date ).toFormat (" LLL dd" )
96
+ : DateTime .fromJSDate (data[idx].date ).set ({ minutes: 0 }).toFormat (" hh:mm a" )
75
97
76
98
if (! badgeEl .value ) return
77
- if (idx === 0 ) {
99
+ if (idx < 1 ) {
78
100
badgeOffset .value = 0
79
- } else if (idx === 23 ) {
101
+ } else if (idx > selectedPeriod . value . value - 2 ) {
80
102
badgeOffset .value = badgeEl .value .getBoundingClientRect ().width
81
103
} else {
82
104
badgeOffset .value = badgeEl .value .getBoundingClientRect ().width / 2
@@ -126,15 +148,15 @@ const buildChart = (chartEl, data, onEnter, onLeave) => {
126
148
.attr (" stroke-width" , 2 )
127
149
.attr (" stroke-linecap" , " round" )
128
150
.attr (" stroke-linejoin" , " round" )
129
- .attr (" d" , line (data .slice (0 , 23 )))
151
+ .attr (" d" , line (data .slice (0 , data . length - 1 )))
130
152
svg .append (" path" )
131
153
.attr (" fill" , " none" )
132
154
.attr (" stroke" , " var(--green)" )
133
155
.attr (" stroke-width" , 2 )
134
156
.attr (" stroke-linecap" , " round" )
135
157
.attr (" stroke-linejoin" , " round" )
136
158
.attr (" stroke-dasharray" , " 4" )
137
- .attr (" d" , line (data .slice (22 , 24 )))
159
+ .attr (" d" , line (data .slice (data . length - 2 , data . length )))
138
160
139
161
svg .append (" circle" )
140
162
.attr (" cx" , x (data[data .length - 1 ].date ))
@@ -147,24 +169,37 @@ const buildChart = (chartEl, data, onEnter, onLeave) => {
147
169
}
148
170
149
171
const getGasPriceSeries = async () => {
172
+ gasPriceSeries .value = []
173
+
150
174
const sizeSeriesRawData = await fetchGasPriceSeries ({
151
- timeframe: " hour" ,
152
- from: parseInt (DateTime .now ().minus ({ hours: 26 }).ts / 1_000 ),
153
- to: parseInt (DateTime .now ().ts / 1_000 ),
175
+ timeframe: selectedPeriod .value .timeframe ,
176
+ to: parseInt (DateTime .now ().plus ({ minutes: 1 }).ts / 1_000 ),
177
+ from: parseInt (
178
+ DateTime .now ()
179
+ .set ({ minutes: 0 , seconds: 0 })
180
+ .minus ({
181
+ days: selectedPeriod .value .timeframe === " day" ? selectedPeriod .value .value : 0 ,
182
+ hours: selectedPeriod .value .timeframe === " hour" ? selectedPeriod .value .value - 1 : 0 ,
183
+ }).ts / 1_000 ,
184
+ ),
154
185
})
155
186
156
187
const gasPriceSeriesMap = {}
157
188
sizeSeriesRawData .forEach ((item ) => {
158
- gasPriceSeriesMap[DateTime .fromISO (item .time ).toFormat (" dd-HH" )] = item .value
189
+ gasPriceSeriesMap[DateTime .fromISO (item .time ).toFormat (selectedPeriod .value .timeframe === " day" ? " y-LL-dd" : " y-LL-dd-HH" )] =
190
+ item .value
159
191
})
160
192
161
- for (let i = 0 ; i < 24 ; i++ ) {
193
+ for (let i = 1 ; i < selectedPeriod . value . value + 1 ; i++ ) {
162
194
const dt = DateTime .now ()
163
- .minus ({ hours: 24 - i })
164
195
.set ({ minutes: 0 , seconds: 0 })
196
+ .minus ({
197
+ days: selectedPeriod .value .timeframe === " day" ? selectedPeriod .value .value - i : 0 ,
198
+ hours: selectedPeriod .value .timeframe === " hour" ? selectedPeriod .value .value - i : 0 ,
199
+ })
165
200
gasPriceSeries .value .push ({
166
201
date: dt .toJSDate (),
167
- value: parseFloat (gasPriceSeriesMap[dt .toFormat (" dd-HH" )]) || 0 ,
202
+ value: parseFloat (gasPriceSeriesMap[dt .toFormat (selectedPeriod . value . timeframe === " day " ? " y-LL-dd " : " y-LL- dd-HH" )]) || 0 ,
168
203
})
169
204
}
170
205
}
@@ -179,6 +214,13 @@ const buildGasTrackingCharts = async () => {
179
214
)
180
215
}
181
216
217
+ watch (
218
+ () => selectedPeriodIdx .value ,
219
+ () => {
220
+ buildGasTrackingCharts ()
221
+ },
222
+ )
223
+
182
224
const debouncedRedraw = useDebounceFn ((e ) => {
183
225
buildGasTrackingCharts ()
184
226
}, 500 )
@@ -195,10 +237,43 @@ onBeforeUnmount(() => {
195
237
</script >
196
238
197
239
<template >
198
- <Flex direction =" column" gap =" 16" wide >
199
- <Flex align =" center" gap =" 6" >
200
- <Icon name =" chart" size =" 13" color =" primary" />
201
- <Text size =" 13" weight =" 600" color =" primary" >Average Gas Price</Text >
240
+ <Flex direction =" column" gap =" 24" wide >
241
+ <Flex align =" start" justify =" between" >
242
+ <Flex align =" center" gap =" 6" >
243
+ <Icon name =" chart" size =" 13" color =" primary" />
244
+ <Text size =" 13" weight =" 600" color =" primary" >Average Gas Price</Text >
245
+ </Flex >
246
+
247
+ <Flex align =" center" gap =" 8" >
248
+ <Dropdown >
249
+ <Button size =" mini" type =" secondary" >
250
+ <Icon name =" chart" size =" 12" color =" primary" />
251
+ <Text color =" primary" >Line</Text >
252
+ <Icon name =" chevron" size =" 12" color =" secondary" />
253
+ </Button >
254
+
255
+ <template #popup >
256
+ <DropdownItem > Line Chart </DropdownItem >
257
+ <DropdownItem disabled > Heatmap </DropdownItem >
258
+ </template >
259
+ </Dropdown >
260
+
261
+ <Dropdown >
262
+ <Button size =" mini" type =" secondary" >
263
+ {{ selectedPeriod.title }}
264
+ <Icon name =" chevron" size =" 12" color =" secondary" />
265
+ </Button >
266
+
267
+ <template #popup >
268
+ <DropdownItem v-for =" (period, idx) in periods" @click =" selectedPeriodIdx = idx" >
269
+ <Flex align =" center" gap =" 8" >
270
+ <Icon :name =" idx === selectedPeriodIdx ? 'check' : ''" size =" 12" color =" secondary" />
271
+ {{ period.title }}
272
+ </Flex >
273
+ </DropdownItem >
274
+ </template >
275
+ </Dropdown >
276
+ </Flex >
202
277
</Flex >
203
278
204
279
<Flex ref =" chartWrapperEl" direction =" column" :class =" $style.chart_wrapper" >
@@ -219,10 +294,23 @@ onBeforeUnmount(() => {
219
294
220
295
<Flex :class =" [$style.axis, $style.x]" >
221
296
<Flex align =" end" justify =" between" wide >
222
- <Text size =" 12" weight =" 600" color =" tertiary" >
223
- {{ DateTime.now().minus({ hours: 24 }).toFormat("dd LLL") }}
297
+ <Text v-if =" selectedPeriod.timeframe === 'day'" size =" 12" weight =" 600" color =" tertiary" >
298
+ {{
299
+ DateTime.now()
300
+ .minus({ days: selectedPeriod.value - 1 })
301
+ .toFormat("LLL dd")
302
+ }}
224
303
</Text >
225
- <Text size =" 12" weight =" 600" color =" tertiary" >Now</Text >
304
+ <Text v-else size =" 12" weight =" 600" color =" tertiary" >
305
+ {{
306
+ DateTime.now()
307
+ .minus({ hours: selectedPeriod.value - 1 })
308
+ .set({ minutes: 0 })
309
+ .toFormat("hh:mm a")
310
+ }}
311
+ </Text >
312
+
313
+ <Text size =" 12" weight =" 600" color =" tertiary" >{{ selectedPeriod.timeframe === "day" ? "Today" : "Now" }}</Text >
226
314
</Flex >
227
315
</Flex >
228
316
@@ -244,7 +332,7 @@ onBeforeUnmount(() => {
244
332
>
245
333
<Flex align =" center" gap =" 16" >
246
334
<Text size =" 12" weight =" 600" color =" secondary" >Price</Text >
247
- <Text size =" 12" weight =" 600" color =" primary" > {{ truncate( tooltipText) }} UTIA</Text >
335
+ <Text size =" 12" weight =" 600" color =" primary" > {{ tooltipText.toFixed(6 ) }} UTIA</Text >
248
336
</Flex >
249
337
</Flex >
250
338
</div >
0 commit comments