mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-08 04:19:25 +00:00
140 lines
4.3 KiB
Vue
140 lines
4.3 KiB
Vue
<script setup lang="ts" generic="T extends Record<string, any>">
|
|
import { type BulletLegendItemInterface, CurveType } from '@unovis/ts'
|
|
import { VisArea, VisAxis, VisLine, VisXYContainer } from '@unovis/vue'
|
|
import { Area, Axis, Line } from '@unovis/ts'
|
|
import { type Component, computed, ref } from 'vue'
|
|
import { useMounted } from '@vueuse/core'
|
|
import { useId } from 'radix-vue'
|
|
import type { BaseChartProps } from '.'
|
|
import { ChartCrosshair, ChartLegend, defaultColors } from '@/components/ui/chart'
|
|
import { cn } from '@/lib/utils'
|
|
|
|
const props = withDefaults(defineProps<BaseChartProps<T> & {
|
|
/**
|
|
* Render custom tooltip component.
|
|
*/
|
|
customTooltip?: Component
|
|
/**
|
|
* Type of curve
|
|
*/
|
|
curveType?: CurveType
|
|
/**
|
|
* Controls the visibility of gradient.
|
|
* @default true
|
|
*/
|
|
showGradiant?: boolean
|
|
}>(), {
|
|
curveType: CurveType.MonotoneX,
|
|
filterOpacity: 0.2,
|
|
margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
|
|
showXAxis: true,
|
|
showYAxis: true,
|
|
showTooltip: true,
|
|
showLegend: true,
|
|
showGridLine: true,
|
|
showGradiant: true,
|
|
})
|
|
|
|
const emits = defineEmits<{
|
|
legendItemClick: [d: BulletLegendItemInterface, i: number]
|
|
}>()
|
|
|
|
type KeyOfT = Extract<keyof T, string>
|
|
type Data = typeof props.data[number]
|
|
|
|
const chartRef = useId()
|
|
|
|
const index = computed(() => props.index as KeyOfT)
|
|
const colors = computed(() => props.colors?.length ? props.colors : defaultColors(props.categories.length))
|
|
|
|
const legendItems = ref<BulletLegendItemInterface[]>(props.categories.map((category, i) => ({
|
|
name: category,
|
|
color: colors.value[i],
|
|
inactive: false,
|
|
})))
|
|
|
|
const isMounted = useMounted()
|
|
|
|
function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
|
|
emits('legendItemClick', d, i)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div :class="cn('w-full h-[400px] flex flex-col items-end', $attrs.class ?? '')">
|
|
<ChartLegend v-if="showLegend" v-model:items="legendItems" @legend-item-click="handleLegendItemClick" />
|
|
|
|
<VisXYContainer :style="{ height: isMounted ? '100%' : 'auto' }" :margin="{ left: 20, right: 20 }" :data="data">
|
|
<svg width="0" height="0">
|
|
<defs>
|
|
<linearGradient v-for="(color, i) in colors" :id="`${chartRef}-color-${i}`" :key="i" x1="0" y1="0" x2="0" y2="1">
|
|
<template v-if="showGradiant">
|
|
<stop offset="5%" :stop-color="color" stop-opacity="0.4" />
|
|
<stop offset="95%" :stop-color="color" stop-opacity="0" />
|
|
</template>
|
|
<template v-else>
|
|
<stop offset="0%" :stop-color="color" />
|
|
</template>
|
|
</linearGradient>
|
|
</defs>
|
|
</svg>
|
|
|
|
<ChartCrosshair v-if="showTooltip" :colors="colors" :items="legendItems" :index="index" :custom-tooltip="customTooltip" />
|
|
|
|
<template v-for="(category, i) in categories" :key="category">
|
|
<VisArea
|
|
:x="(d: Data, i: number) => i"
|
|
:y="(d: Data) => d[category]"
|
|
color="auto"
|
|
:curve-type="curveType"
|
|
:attributes="{
|
|
[Area.selectors.area]: {
|
|
fill: `url(#${chartRef}-color-${i})`,
|
|
},
|
|
}"
|
|
:opacity="legendItems.find(item => item.name === category)?.inactive ? filterOpacity : 1"
|
|
/>
|
|
</template>
|
|
|
|
<template v-for="(category, i) in categories" :key="category">
|
|
<VisLine
|
|
:x="(d: Data, i: number) => i"
|
|
:y="(d: Data) => d[category]"
|
|
:color="colors[i]"
|
|
:curve-type="curveType"
|
|
:attributes="{
|
|
[Line.selectors.line]: {
|
|
opacity: legendItems.find(item => item.name === category)?.inactive ? filterOpacity : 1,
|
|
},
|
|
}"
|
|
/>
|
|
</template>
|
|
|
|
<VisAxis
|
|
v-if="showXAxis"
|
|
type="x"
|
|
:tick-format="xFormatter ?? ((v: number) => data[v]?.[index])"
|
|
:grid-line="false"
|
|
:tick-line="false"
|
|
tick-text-color="hsl(var(--vis-text-color))"
|
|
/>
|
|
<VisAxis
|
|
v-if="showYAxis"
|
|
type="y"
|
|
:tick-line="false"
|
|
:tick-format="yFormatter"
|
|
:domain-line="false"
|
|
:grid-line="showGridLine"
|
|
:attributes="{
|
|
[Axis.selectors.grid]: {
|
|
class: 'text-muted',
|
|
},
|
|
}"
|
|
tick-text-color="hsl(var(--vis-text-color))"
|
|
/>
|
|
|
|
<slot />
|
|
</VisXYContainer>
|
|
</div>
|
|
</template>
|