-
-
Notifications
You must be signed in to change notification settings - Fork 7.1k
Expand file tree
/
Copy pathmisc-interactive.vue
More file actions
103 lines (85 loc) · 2.97 KB
/
misc-interactive.vue
File metadata and controls
103 lines (85 loc) · 2.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<template>
<v-container>
<v-card class="pa-4 pb-2 mx-auto" rounded="lg" width="500">
<div class="d-flex align-center ga-2 mb-1">
<v-icon icon="mdi-chevron-down" size="18"></v-icon>
<span class="text-uppercase text-body-small font-weight-bold">Weekly Downloads</span>
<v-spacer></v-spacer>
<div class="text-body-small text-medium-emphasis mb-2">
{{ hoveredWeek ?? lastWeek }}
</div>
</div>
<div class="d-flex align-end ga-4 pl-1">
<span class="text-headline-large font-weight-bold mb-1" style="min-width: 140px">
{{ hoveredValue ?? lastValue }}
</span>
<v-sparkline
:gradient="['rgba(var(--v-theme-surface-variant), .2)', 'rgba(var(--v-theme-surface-variant), .1)']"
:model-value="weeklyValues"
class="mr-n2"
color="medium-emphasis"
height="60"
line-width="1.5"
marker-size="12"
marker-stroke="rgb(var(--v-theme-surface))"
min="0"
padding="6"
smooth="2"
stroke-linecap="round"
style="flex: 1"
width="300"
fill
interactive
@update:current-index="hoveredIdx = $event"
></v-sparkline>
</div>
</v-card>
</v-container>
</template>
<script setup>
import { computed, onMounted, ref } from 'vue'
import { useDate } from 'vuetify'
const adapter = useDate()
const weeks = ref([])
const hoveredIdx = ref(null)
const weeklyValues = computed(() => weeks.value.map(w => w.total))
const lastWeek = computed(() => {
const w = weeks.value[weeks.value.length - 1]
return w ? `${w.start} to ${w.end}` : ''
})
const lastValue = computed(() => weeks.value.at(-1)?.total?.toLocaleString())
const hoveredWeek = computed(() => {
if (hoveredIdx.value == null) return null
const w = weeks.value[hoveredIdx.value]
return w ? `${w.start} to ${w.end}` : null
})
const hoveredValue = computed(() => {
if (hoveredIdx.value == null) return null
return weeks.value[hoveredIdx.value]?.total?.toLocaleString()
})
onMounted(async () => {
const today = adapter.date()
const range = [
adapter.startOfWeek(adapter.addMonths(today, -12), 1),
today,
].map(v => adapter.toISO(v)).join(':')
const url = `https://api.npmjs.org/downloads/range/${range}/vuetify`
const res = await fetch(url)
const data = await res.json()
const result = []
const currentWeekday = adapter.toJsDate(today).getDay()
let week = null
for (const d of data.downloads.slice(0, (-currentWeekday))) {
const parsed = adapter.parseISO(d.day)
const isMonday = adapter.isSameDay(parsed, adapter.startOfWeek(parsed, 1))
if (!week || isMonday) {
if (week) result.push(week)
week = { start: d.day, end: d.day, total: 0 }
}
week.end = d.day
week.total += d.downloads
}
if (week) result.push(week)
weeks.value = result
})
</script>