feat: add more props

This commit is contained in:
zernonia 2023-11-13 16:55:21 +08:00
parent bfccb78a4f
commit 91c7d0fbd0
13 changed files with 309 additions and 36 deletions

View File

@ -8,7 +8,6 @@ const data = [
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
{ name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
]
</script>

View File

@ -96,7 +96,6 @@ function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
<VisAxis
v-if="showXAxis"
type="x"
:num-ticks="data.length"
:tick-format="xFormatter ?? ((v: number) => data[v]?.[index])"
:grid-line="false"
:tick-line="false"
@ -105,7 +104,6 @@ function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
<VisAxis
v-if="showYAxis"
type="y"
:num-ticks="data.length"
:tick-line="false"
:tick-format="yFormatter"
:domain-line="false"

View File

@ -76,7 +76,6 @@ const selectorsBar = computed(() => props.type === 'grouped' ? GroupedBar.select
<VisAxis
v-if="showXAxis"
type="x"
:num-ticks="data.length"
:tick-format="xFormatter ?? ((v: number) => data[v]?.[index])"
:grid-line="false"
:tick-line="false"
@ -85,7 +84,6 @@ const selectorsBar = computed(() => props.type === 'grouped' ? GroupedBar.select
<VisAxis
v-if="showYAxis"
type="y"
:num-ticks="data.length"
:tick-line="false"
:tick-format="yFormatter"
:domain-line="false"

View File

@ -4,7 +4,7 @@ import { VisAxis, VisLine, VisXYContainer } from '@unovis/vue'
import { Axis, Line } from '@unovis/ts'
import { ref } from 'vue'
import { useMounted } from '@vueuse/core'
import { ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/new-york/ui/chart'
import { ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/default/ui/chart'
import { cn } from '@/lib/utils'
const props = withDefaults(defineProps<{
@ -49,7 +49,11 @@ function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
<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">
<VisXYContainer
:margin="{ left: 20, right: 20 }"
:data="data"
:style="{ height: isMounted ? '100%' : 'auto' }"
>
<ChartCrosshair v-if="showTooltip" :colors="colors" :items="legendItems" :index="index" />
<template v-for="(category, i) in categories" :key="category">
@ -68,7 +72,6 @@ function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
<VisAxis
v-if="showXAxis"
type="x"
:num-ticks="data.length"
:tick-format="xFormatter ?? ((v: number) => data[v]?.[index])"
:grid-line="false"
:tick-line="false"
@ -77,7 +80,6 @@ function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
<VisAxis
v-if="showYAxis"
type="y"
:num-ticks="data.length"
:tick-line="false"
:tick-format="yFormatter"
:domain-line="false"

View File

@ -9,7 +9,10 @@ const props = withDefaults(defineProps<{
selector: string
index: string
items?: BulletLegendItemInterface[]
}>(), {})
valueFormatter?: (tick: number, i?: number, ticks?: number[]) => string
}>(), {
valueFormatter: (tick: number) => `${tick}`,
})
// Use weakmap to store reference to each datapoint for Tooltip
const wm = new WeakMap()
@ -22,7 +25,7 @@ function template(d: any, i: number, elements: (HTMLElement | SVGElement)[]) {
const componentDiv = document.createElement('div')
const omittedData = Object.entries(omit(d, [props.index])).map(([key, value]) => {
const legendReference = props.items?.find(i => i.name === key)
return { ...legendReference, value }
return { ...legendReference, value: props.valueFormatter(value) }
})
createApp(ChartTooltip, { title: d[props.index], data: omittedData }).mount(componentDiv)
wm.set(d, componentDiv.innerHTML)
@ -37,7 +40,7 @@ function template(d: any, i: number, elements: (HTMLElement | SVGElement)[]) {
}
else {
const style = getComputedStyle(elements[i])
const omittedData = [{ name: data.name, value: data[props.index], color: style.fill }]
const omittedData = [{ name: data.name, value: props.valueFormatter(data[props.index]), color: style.fill }]
const componentDiv = document.createElement('div')
createApp(ChartTooltip, { title: d[props.index], data: omittedData }).mount(componentDiv)
wm.set(d, componentDiv.innerHTML)

View File

@ -8,8 +8,9 @@ const data = [
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
{ name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
]
const valueFormatter = (tick: number | Date) => typeof tick === 'number' ? `$ ${new Intl.NumberFormat('us').format(tick).toString()}` : ''
</script>
<template>
@ -17,5 +18,6 @@ const data = [
index="name"
:category="'total'"
:data="data"
:value-formatter="valueFormatter"
/>
</template>

View File

@ -2,22 +2,275 @@
import { LineChart } from '@/lib/registry/new-york/ui/chart-line'
const data = [
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500 },
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500 },
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500 },
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500 },
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500 },
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500 },
{ name: 'Jul', total: Math.floor(Math.random() * 2000) + 500 },
{
'year': 1970,
'Export Growth Rate': 2.04,
'Import Growth Rate': 1.53,
},
{
'year': 1971,
'Export Growth Rate': 1.96,
'Import Growth Rate': 1.58,
},
{
'year': 1972,
'Export Growth Rate': 1.96,
'Import Growth Rate': 1.61,
},
{
'year': 1973,
'Export Growth Rate': 1.93,
'Import Growth Rate': 1.61,
},
{
'year': 1974,
'Export Growth Rate': 1.88,
'Import Growth Rate': 1.67,
},
{
'year': 1975,
'Export Growth Rate': 1.79,
'Import Growth Rate': 1.64,
},
{
'year': 1976,
'Export Growth Rate': 1.77,
'Import Growth Rate': 1.62,
},
{
'year': 1977,
'Export Growth Rate': 1.74,
'Import Growth Rate': 1.69,
},
{
'year': 1978,
'Export Growth Rate': 1.74,
'Import Growth Rate': 1.7,
},
{
'year': 1979,
'Export Growth Rate': 1.77,
'Import Growth Rate': 1.67,
},
{
'year': 1980,
'Export Growth Rate': 1.79,
'Import Growth Rate': 1.7,
},
{
'year': 1981,
'Export Growth Rate': 1.81,
'Import Growth Rate': 1.72,
},
{
'year': 1982,
'Export Growth Rate': 1.84,
'Import Growth Rate': 1.73,
},
{
'year': 1983,
'Export Growth Rate': 1.77,
'Import Growth Rate': 1.73,
},
{
'year': 1984,
'Export Growth Rate': 1.78,
'Import Growth Rate': 1.78,
},
{
'year': 1985,
'Export Growth Rate': 1.78,
'Import Growth Rate': 1.81,
},
{
'year': 1986,
'Export Growth Rate': 1.82,
'Import Growth Rate': 1.89,
},
{
'year': 1987,
'Export Growth Rate': 1.82,
'Import Growth Rate': 1.91,
},
{
'year': 1988,
'Export Growth Rate': 1.77,
'Import Growth Rate': 1.94,
},
{
'year': 1989,
'Export Growth Rate': 1.76,
'Import Growth Rate': 1.94,
},
{
'year': 1990,
'Export Growth Rate': 1.75,
'Import Growth Rate': 1.97,
},
{
'year': 1991,
'Export Growth Rate': 1.62,
'Import Growth Rate': 1.99,
},
{
'year': 1992,
'Export Growth Rate': 1.56,
'Import Growth Rate': 2.12,
},
{
'year': 1993,
'Export Growth Rate': 1.5,
'Import Growth Rate': 2.13,
},
{
'year': 1994,
'Export Growth Rate': 1.46,
'Import Growth Rate': 2.15,
},
{
'year': 1995,
'Export Growth Rate': 1.43,
'Import Growth Rate': 2.17,
},
{
'year': 1996,
'Export Growth Rate': 1.4,
'Import Growth Rate': 2.2,
},
{
'year': 1997,
'Export Growth Rate': 1.37,
'Import Growth Rate': 2.15,
},
{
'year': 1998,
'Export Growth Rate': 1.34,
'Import Growth Rate': 2.07,
},
{
'year': 1999,
'Export Growth Rate': 1.32,
'Import Growth Rate': 2.05,
},
{
'year': 2000,
'Export Growth Rate': 1.33,
'Import Growth Rate': 2.07,
},
{
'year': 2001,
'Export Growth Rate': 1.31,
'Import Growth Rate': 2.08,
},
{
'year': 2002,
'Export Growth Rate': 1.29,
'Import Growth Rate': 2.1,
},
{
'year': 2003,
'Export Growth Rate': 1.27,
'Import Growth Rate': 2.15,
},
{
'year': 2004,
'Export Growth Rate': 1.27,
'Import Growth Rate': 2.21,
},
{
'year': 2005,
'Export Growth Rate': 1.26,
'Import Growth Rate': 2.23,
},
{
'year': 2006,
'Export Growth Rate': 1.26,
'Import Growth Rate': 2.29,
},
{
'year': 2007,
'Export Growth Rate': 1.27,
'Import Growth Rate': 2.34,
},
{
'year': 2008,
'Export Growth Rate': 1.26,
'Import Growth Rate': 2.36,
},
{
'year': 2009,
'Export Growth Rate': 1.26,
'Import Growth Rate': 2.36,
},
{
'year': 2010,
'Export Growth Rate': 1.25,
'Import Growth Rate': 2.35,
},
{
'year': 2011,
'Export Growth Rate': 1.24,
'Import Growth Rate': 2.34,
},
{
'year': 2012,
'Export Growth Rate': 1.25,
'Import Growth Rate': 2.39,
},
{
'year': 2013,
'Export Growth Rate': 1.22,
'Import Growth Rate': 2.3,
},
{
'year': 2014,
'Export Growth Rate': 1.2,
'Import Growth Rate': 2.35,
},
{
'year': 2015,
'Export Growth Rate': 1.17,
'Import Growth Rate': 2.39,
},
{
'year': 2016,
'Export Growth Rate': 1.16,
'Import Growth Rate': 2.41,
},
{
'year': 2017,
'Export Growth Rate': 1.13,
'Import Growth Rate': 2.44,
},
{
'year': 2018,
'Export Growth Rate': 1.07,
'Import Growth Rate': 2.45,
},
{
'year': 2019,
'Export Growth Rate': 1.03,
'Import Growth Rate': 2.47,
},
{
'year': 2020,
'Export Growth Rate': 0.92,
'Import Growth Rate': 2.48,
},
{
'year': 2021,
'Export Growth Rate': 0.82,
'Import Growth Rate': 2.51,
},
]
const categories = ['total']
const categories = ['Export Growth Rate', 'Import Growth Rate']
</script>
<template>
<LineChart
:data="data"
index="name"
index="year"
:categories="categories"
:y-formatter="(tick, i) => {
return typeof tick === 'number'

View File

@ -96,7 +96,6 @@ function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
<VisAxis
v-if="showXAxis"
type="x"
:num-ticks="data.length"
:tick-format="xFormatter ?? ((v: number) => data[v]?.[index])"
:grid-line="false"
:tick-line="false"
@ -105,7 +104,6 @@ function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
<VisAxis
v-if="showYAxis"
type="y"
:num-ticks="data.length"
:tick-line="false"
:tick-format="yFormatter"
:domain-line="false"

View File

@ -76,7 +76,6 @@ const selectorsBar = computed(() => props.type === 'grouped' ? GroupedBar.select
<VisAxis
v-if="showXAxis"
type="x"
:num-ticks="data.length"
:tick-format="xFormatter ?? ((v: number) => data[v]?.[index])"
:grid-line="false"
:tick-line="false"
@ -85,7 +84,6 @@ const selectorsBar = computed(() => props.type === 'grouped' ? GroupedBar.select
<VisAxis
v-if="showYAxis"
type="y"
:num-ticks="data.length"
:tick-line="false"
:tick-format="yFormatter"
:domain-line="false"

View File

@ -13,11 +13,14 @@ const props = withDefaults(defineProps<{
colors?: string[]
type?: 'donut' | 'pie'
filterOpacity?: number
valueFormatter?: (tick: number | Date, i: number, ticks: number[] | Date[]) => string
sortFunction?: (a: any, b: any) => number | undefined
valueFormatter?: (tick: number, i?: number, ticks?: number[]) => string
showTooltip?: boolean
showLegend?: boolean
}>(), {
colors: () => defaultColors,
sortFunction: () => undefined,
valueFormatter: (tick: number) => `${tick}`,
type: 'donut',
filterOpacity: 0.2,
showTooltip: true,
@ -34,19 +37,29 @@ const legendItems = computed(() => props.data.map((item, i) => ({
color: props.colors[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" />
<ChartSingleTooltip
:selector="Donut.selectors.segment"
:index="category"
:items="legendItems"
:value-formatter="valueFormatter"
/>
<VisDonut
:value="(d: Data) => d[category]"
:sort-function="(a: Data, b: Data) => (a[category] - b[category])"
:sort-function="sortFunction"
:color="colors"
:arc-width="type === 'donut' ? 20 : 0"
:show-background="false"
:central-label="valueFormatter(totalValue)"
:events="{
[Donut.selectors.segment]: {
click: (d: any, ev: PointerEvent, i: number, elements: HTMLElement[]) => {

View File

@ -49,7 +49,11 @@ function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
<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">
<VisXYContainer
:margin="{ left: 20, right: 20 }"
:data="data"
:style="{ height: isMounted ? '100%' : 'auto' }"
>
<ChartCrosshair v-if="showTooltip" :colors="colors" :items="legendItems" :index="index" />
<template v-for="(category, i) in categories" :key="category">
@ -68,7 +72,6 @@ function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
<VisAxis
v-if="showXAxis"
type="x"
:num-ticks="data.length"
:tick-format="xFormatter ?? ((v: number) => data[v]?.[index])"
:grid-line="false"
:tick-line="false"
@ -77,7 +80,6 @@ function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
<VisAxis
v-if="showYAxis"
type="y"
:num-ticks="data.length"
:tick-line="false"
:tick-format="yFormatter"
:domain-line="false"

View File

@ -9,7 +9,10 @@ const props = withDefaults(defineProps<{
selector: string
index: string
items?: BulletLegendItemInterface[]
}>(), {})
valueFormatter?: (tick: number, i?: number, ticks?: number[]) => string
}>(), {
valueFormatter: (tick: number) => `${tick}`,
})
// Use weakmap to store reference to each datapoint for Tooltip
const wm = new WeakMap()
@ -22,7 +25,7 @@ function template(d: any, i: number, elements: (HTMLElement | SVGElement)[]) {
const componentDiv = document.createElement('div')
const omittedData = Object.entries(omit(d, [props.index])).map(([key, value]) => {
const legendReference = props.items?.find(i => i.name === key)
return { ...legendReference, value }
return { ...legendReference, value: props.valueFormatter(value) }
})
createApp(ChartTooltip, { title: d[props.index], data: omittedData }).mount(componentDiv)
wm.set(d, componentDiv.innerHTML)
@ -37,7 +40,7 @@ function template(d: any, i: number, elements: (HTMLElement | SVGElement)[]) {
}
else {
const style = getComputedStyle(elements[i])
const omittedData = [{ name: data.name, value: data[props.index], color: style.fill }]
const omittedData = [{ name: data.name, value: props.valueFormatter(data[props.index]), color: style.fill }]
const componentDiv = document.createElement('div')
createApp(ChartTooltip, { title: d[props.index], data: omittedData }).mount(componentDiv)
wm.set(d, componentDiv.innerHTML)

View File

@ -3,4 +3,8 @@ export { default as ChartSingleTooltip } from './ChartSingleTooltip.vue'
export { default as ChartLegend } from './ChartLegend.vue'
export { default as ChartCrosshair } from './ChartCrosshair.vue'
export const defaultColors = ['hsl(var(--primary))', 'hsl(var(--muted))']
const COLOR_COUNT = 3
export const defaultColors = [
...Array.from(Array(COLOR_COUNT).keys()).map(i => `hsl(var(--primary) / ${1 - (1 / COLOR_COUNT) * i})`),
...Array.from(Array(COLOR_COUNT).keys()).map(i => `hsl(var(--secondary) / ${1 - (1 / COLOR_COUNT) * i})`),
]