Files
better-auth/examples/nuxt-example/components/ui/chart-donut/DonutChart.vue
2024-09-26 17:09:00 +03:00

101 lines
3.3 KiB
Vue

<script setup lang="ts" generic="T extends Record<string, any>">
import { VisDonut, VisSingleContainer } from '@unovis/vue'
import { Donut } from '@unovis/ts'
import { type Component, computed, ref } from 'vue'
import { useMounted } from '@vueuse/core'
import type { BaseChartProps } from '.'
import { ChartSingleTooltip, defaultColors } from '@/components/ui/chart'
import { cn } from '@/lib/utils'
const props = withDefaults(defineProps<Pick<BaseChartProps<T>, 'data' | 'colors' | 'index' | 'margin' | 'showLegend' | 'showTooltip' | 'filterOpacity'> & {
/**
* Sets the name of the key containing the quantitative chart values.
*/
category: KeyOfT
/**
* Change the type of the chart
* @default "donut"
*/
type?: 'donut' | 'pie'
/**
* Function to sort the segment
*/
sortFunction?: (a: any, b: any) => number | undefined
/**
* Controls the formatting for the label.
*/
valueFormatter?: (tick: number, i?: number, ticks?: number[]) => string
/**
* Render custom tooltip component.
*/
customTooltip?: Component
}>(), {
margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
sortFunction: () => undefined,
valueFormatter: (tick: number) => `${tick}`,
type: 'donut',
filterOpacity: 0.2,
showTooltip: true,
showLegend: true,
})
type KeyOfT = Extract<keyof T, string>
type Data = typeof props.data[number]
const category = computed(() => props.category as KeyOfT)
const index = computed(() => props.index as KeyOfT)
const isMounted = useMounted()
const activeSegmentKey = ref<string>()
const colors = computed(() => props.colors?.length ? props.colors : defaultColors(props.data.filter(d => d[props.category]).filter(Boolean).length))
const legendItems = computed(() => props.data.map((item, i) => ({
name: item[props.index],
color: colors.value[i],
inactive: false,
})))
const totalValue = computed(() => props.data.reduce((prev, curr) => {
return prev + curr[props.category]
}, 0))
</script>
<template>
<div :class="cn('w-full h-48 flex flex-col items-end', $attrs.class ?? '')">
<VisSingleContainer :style="{ height: isMounted ? '100%' : 'auto' }" :margin="{ left: 20, right: 20 }" :data="data">
<ChartSingleTooltip
:selector="Donut.selectors.segment"
:index="category"
:items="legendItems"
:value-formatter="valueFormatter"
:custom-tooltip="customTooltip"
/>
<VisDonut
:value="(d: Data) => d[category]"
:sort-function="sortFunction"
:color="colors"
:arc-width="type === 'donut' ? 20 : 0"
:show-background="false"
:central-label="type === 'donut' ? valueFormatter(totalValue) : ''"
:events="{
[Donut.selectors.segment]: {
click: (d: Data, ev: PointerEvent, i: number, elements: HTMLElement[]) => {
if (d?.data?.[index] === activeSegmentKey) {
activeSegmentKey = undefined
elements.forEach(el => el.style.opacity = '1')
}
else {
activeSegmentKey = d?.data?.[index]
elements.forEach(el => el.style.opacity = `${filterOpacity}`)
elements[i].style.opacity = '1'
}
},
},
}"
/>
<slot />
</VisSingleContainer>
</div>
</template>