first commit
This commit is contained in:
121
app/components/home/HomeChart.client.vue
Normal file
121
app/components/home/HomeChart.client.vue
Normal file
@@ -0,0 +1,121 @@
|
||||
<script setup lang="ts">
|
||||
import { eachDayOfInterval, eachWeekOfInterval, eachMonthOfInterval, format } from 'date-fns'
|
||||
import { VisXYContainer, VisLine, VisAxis, VisArea, VisCrosshair, VisTooltip } from '@unovis/vue'
|
||||
import type { Period, Range } from '~/types'
|
||||
|
||||
const cardRef = useTemplateRef<HTMLElement | null>('cardRef')
|
||||
|
||||
const props = defineProps<{
|
||||
period: Period
|
||||
range: Range
|
||||
}>()
|
||||
|
||||
type DataRecord = {
|
||||
date: Date
|
||||
amount: number
|
||||
}
|
||||
|
||||
const { width } = useElementSize(cardRef)
|
||||
|
||||
const data = ref<DataRecord[]>([])
|
||||
|
||||
watch([() => props.period, () => props.range], () => {
|
||||
const dates = ({
|
||||
daily: eachDayOfInterval,
|
||||
weekly: eachWeekOfInterval,
|
||||
monthly: eachMonthOfInterval
|
||||
} as Record<Period, typeof eachDayOfInterval>)[props.period](props.range)
|
||||
|
||||
const min = 1000
|
||||
const max = 10000
|
||||
|
||||
data.value = dates.map(date => ({ date, amount: Math.floor(Math.random() * (max - min + 1)) + min }))
|
||||
}, { immediate: true })
|
||||
|
||||
const x = (_: DataRecord, i: number) => i
|
||||
const y = (d: DataRecord) => d.amount
|
||||
|
||||
const total = computed(() => data.value.reduce((acc: number, { amount }) => acc + amount, 0))
|
||||
|
||||
const formatNumber = new Intl.NumberFormat('en', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }).format
|
||||
|
||||
const formatDate = (date: Date): string => {
|
||||
return ({
|
||||
daily: format(date, 'd MMM'),
|
||||
weekly: format(date, 'd MMM'),
|
||||
monthly: format(date, 'MMM yyy')
|
||||
})[props.period]
|
||||
}
|
||||
|
||||
const xTicks = (i: number) => {
|
||||
if (i === 0 || i === data.value.length - 1 || !data.value[i]) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return formatDate(data.value[i].date)
|
||||
}
|
||||
|
||||
const template = (d: DataRecord) => `${formatDate(d.date)}: ${formatNumber(d.amount)}`
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCard ref="cardRef" :ui="{ root: 'overflow-visible', body: '!px-0 !pt-0 !pb-3' }">
|
||||
<template #header>
|
||||
<div>
|
||||
<p class="text-xs text-muted uppercase mb-1.5">
|
||||
Revenue
|
||||
</p>
|
||||
<p class="text-3xl text-highlighted font-semibold">
|
||||
{{ formatNumber(total) }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<VisXYContainer
|
||||
:data="data"
|
||||
:padding="{ top: 40 }"
|
||||
class="h-96"
|
||||
:width="width"
|
||||
>
|
||||
<VisLine
|
||||
:x="x"
|
||||
:y="y"
|
||||
color="var(--ui-primary)"
|
||||
/>
|
||||
<VisArea
|
||||
:x="x"
|
||||
:y="y"
|
||||
color="var(--ui-primary)"
|
||||
:opacity="0.1"
|
||||
/>
|
||||
|
||||
<VisAxis
|
||||
type="x"
|
||||
:x="x"
|
||||
:tick-format="xTicks"
|
||||
/>
|
||||
|
||||
<VisCrosshair
|
||||
color="var(--ui-primary)"
|
||||
:template="template"
|
||||
/>
|
||||
|
||||
<VisTooltip />
|
||||
</VisXYContainer>
|
||||
</UCard>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.unovis-xy-container {
|
||||
--vis-crosshair-line-stroke-color: var(--ui-primary);
|
||||
--vis-crosshair-circle-stroke-color: var(--ui-bg);
|
||||
|
||||
--vis-axis-grid-color: var(--ui-border);
|
||||
--vis-axis-tick-color: var(--ui-border);
|
||||
--vis-axis-tick-label-color: var(--ui-text-dimmed);
|
||||
|
||||
--vis-tooltip-background-color: var(--ui-bg);
|
||||
--vis-tooltip-border-color: var(--ui-border);
|
||||
--vis-tooltip-text-color: var(--ui-text-highlighted);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user