chore: add feature

This commit is contained in:
zernonia 2024-04-26 00:35:26 +08:00
parent 1f6398f8d1
commit 4159f56616
13 changed files with 140 additions and 35 deletions

View File

@ -41,3 +41,36 @@ Add the following tooltip styling to your `tailwind.css` file:
``` ```
</Steps> </Steps>
## Colors
By default, we construct the primary theme color, and secondary (`--vis-secondary-color`) color with different opacity for the graph.
However, you can always pass in the desired `color` into each chart.
```vue
<template>
<AreaChart
:data="data"
:colors="['blue', 'pink', 'orange', 'red']"
/>
</template>
```
## Custom tooltip
If you want to customize the `Tooltip` for the chart, you can pass `customTooltip` prop with a custom Vue component.
The custom component would receive `title` and `data` props, check out [ChartTooltip.vue component](https://github.com/radix-vue/shadcn-vue/tree/dev/apps/www/src/lib/registry/default/ui/chart/ChartTooltip.vue) for example.
The expecting prop definition would be
```ts
defineProps<{
title?: string
data: {
name: string
color: string
value: any
}[]
}>()
```

View File

@ -1,19 +1,28 @@
<script setup lang="ts" generic="T extends Record<string, any>"> <script setup lang="ts" generic="T extends Record<string, any>">
import type { BulletLegendItemInterface } from '@unovis/ts' import { type BulletLegendItemInterface, CurveType } from '@unovis/ts'
import { VisArea, VisAxis, VisLine, VisXYContainer } from '@unovis/vue' import { VisArea, VisAxis, VisLine, VisXYContainer } from '@unovis/vue'
import { Area, Axis, Line } from '@unovis/ts' import { Area, Axis, Line } from '@unovis/ts'
import { computed, ref } from 'vue' import { type Component, computed, ref } from 'vue'
import { useMounted } from '@vueuse/core' import { useMounted } from '@vueuse/core'
import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/default/ui/chart' import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/default/ui/chart'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = withDefaults(defineProps<BaseChartProps<T> & { const props = withDefaults(defineProps<BaseChartProps<T> & {
/**
* Render custom tooltip component.
*/
customTooltip?: Component
/**
* Type of curve
*/
curveType?: CurveType
/** /**
* Controls the visibility of gradient. * Controls the visibility of gradient.
* @default true * @default true
*/ */
showGradiant?: boolean showGradiant?: boolean
}>(), { }>(), {
curveType: CurveType.Basis,
filterOpacity: 0.2, filterOpacity: 0.2,
margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }), margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
showXAxis: true, showXAxis: true,
@ -66,13 +75,14 @@ function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
</defs> </defs>
</svg> </svg>
<ChartCrosshair v-if="showTooltip" :colors="colors" :items="legendItems" :index="index" /> <ChartCrosshair v-if="showTooltip" :colors="colors" :items="legendItems" :index="index" :custom-tooltip="customTooltip" />
<template v-for="(category, i) in categories" :key="category"> <template v-for="(category, i) in categories" :key="category">
<VisArea <VisArea
:x="(d: Data, i: number) => i" :x="(d: Data, i: number) => i"
:y="(d: Data) => d[category]" :y="(d: Data) => d[category]"
color="auto" color="auto"
:curve-type="curveType"
:attributes="{ :attributes="{
[Area.selectors.area]: { [Area.selectors.area]: {
fill: `url(#color-${i})`, fill: `url(#color-${i})`,
@ -87,6 +97,7 @@ function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
:x="(d: Data, i: number) => i" :x="(d: Data, i: number) => i"
:y="(d: Data) => d[category]" :y="(d: Data) => d[category]"
:color="colors[i]" :color="colors[i]"
:curve-type="curveType"
:attributes="{ :attributes="{
[Line.selectors.line]: { [Line.selectors.line]: {
opacity: legendItems.find(item => item.name === category)?.inactive ? filterOpacity : 1, opacity: legendItems.find(item => item.name === category)?.inactive ? filterOpacity : 1,

View File

@ -2,12 +2,16 @@
import type { BulletLegendItemInterface, Spacing } from '@unovis/ts' import type { BulletLegendItemInterface, Spacing } from '@unovis/ts'
import { VisAxis, VisGroupedBar, VisStackedBar, VisXYContainer } from '@unovis/vue' import { VisAxis, VisGroupedBar, VisStackedBar, VisXYContainer } from '@unovis/vue'
import { Axis, GroupedBar, StackedBar } from '@unovis/ts' import { Axis, GroupedBar, StackedBar } from '@unovis/ts'
import { computed, ref } from 'vue' import { type Component, computed, ref } from 'vue'
import { useMounted } from '@vueuse/core' import { useMounted } from '@vueuse/core'
import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/default/ui/chart' import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/default/ui/chart'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = withDefaults(defineProps<BaseChartProps<T> & { const props = withDefaults(defineProps<BaseChartProps<T> & {
/**
* Render custom tooltip component.
*/
customTooltip?: Component
/** /**
* Change the type of the chart * Change the type of the chart
* @default "grouped" * @default "grouped"
@ -57,7 +61,7 @@ const selectorsBar = computed(() => props.type === 'grouped' ? GroupedBar.select
<template> <template>
<div :class="cn('w-full h-[400px] flex flex-col items-end', $attrs.class ?? '')"> <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" /> <ChartLegend v-if="showLegend" v-model:items="legendItems" :custom-tooltip="customTooltip" @legend-item-click="handleLegendItemClick" />
<VisXYContainer <VisXYContainer
:data="data" :data="data"

View File

@ -1,7 +1,7 @@
<script setup lang="ts" generic="T extends Record<string, any>"> <script setup lang="ts" generic="T extends Record<string, any>">
import { VisDonut, VisSingleContainer } from '@unovis/vue' import { VisDonut, VisSingleContainer } from '@unovis/vue'
import { Donut } from '@unovis/ts' import { Donut } from '@unovis/ts'
import { computed, ref } from 'vue' import { type DefineComponent, computed, ref } from 'vue'
import { useMounted } from '@vueuse/core' import { useMounted } from '@vueuse/core'
import { type BaseChartProps, ChartSingleTooltip, defaultColors } from '@/lib/registry/default/ui/chart' import { type BaseChartProps, ChartSingleTooltip, defaultColors } from '@/lib/registry/default/ui/chart'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
@ -24,7 +24,10 @@ const props = withDefaults(defineProps<Pick<BaseChartProps<T>, 'data' | 'colors'
* Controls the formatting for the label. * Controls the formatting for the label.
*/ */
valueFormatter?: (tick: number, i?: number, ticks?: number[]) => string valueFormatter?: (tick: number, i?: number, ticks?: number[]) => string
/**
* Render custom tooltip component.
*/
customTooltip?: DefineComponent
}>(), { }>(), {
margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }), margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
sortFunction: () => undefined, sortFunction: () => undefined,
@ -63,6 +66,7 @@ const totalValue = computed(() => props.data.reduce((prev, curr) => {
:index="category" :index="category"
:items="legendItems" :items="legendItems"
:value-formatter="valueFormatter" :value-formatter="valueFormatter"
:custom-tooltip="customTooltip"
/> />
<VisDonut <VisDonut

View File

@ -1,13 +1,23 @@
<script setup lang="ts" generic="T extends Record<string, any>"> <script setup lang="ts" generic="T extends Record<string, any>">
import type { BulletLegendItemInterface } from '@unovis/ts' import { type BulletLegendItemInterface, CurveType } from '@unovis/ts'
import { VisAxis, VisLine, VisXYContainer } from '@unovis/vue' import { VisAxis, VisLine, VisXYContainer } from '@unovis/vue'
import { Axis, Line } from '@unovis/ts' import { Axis, Line } from '@unovis/ts'
import { computed, ref } from 'vue' import { type Component, computed, ref } from 'vue'
import { useMounted } from '@vueuse/core' import { useMounted } from '@vueuse/core'
import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/default/ui/chart' import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/default/ui/chart'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = withDefaults(defineProps<BaseChartProps<T>>(), { const props = withDefaults(defineProps<BaseChartProps<T> & {
/**
* Render custom tooltip component.
*/
customTooltip?: Component
/**
* Type of curve
*/
curveType?: CurveType
}>(), {
curveType: CurveType.Basis,
filterOpacity: 0.2, filterOpacity: 0.2,
margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }), margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
showXAxis: true, showXAxis: true,
@ -49,12 +59,13 @@ function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
:data="data" :data="data"
:style="{ height: isMounted ? '100%' : 'auto' }" :style="{ height: isMounted ? '100%' : 'auto' }"
> >
<ChartCrosshair v-if="showTooltip" :colors="colors" :items="legendItems" :index="index" /> <ChartCrosshair v-if="showTooltip" :colors="colors" :items="legendItems" :index="index" :custom-tooltip="customTooltip" />
<template v-for="(category, i) in categories" :key="category"> <template v-for="(category, i) in categories" :key="category">
<VisLine <VisLine
:x="(d: Data, i: number) => i" :x="(d: Data, i: number) => i"
:y="(d: Data) => d[category]" :y="(d: Data) => d[category]"
:curve-type="curveType"
:color="colors[i]" :color="colors[i]"
:attributes="{ :attributes="{
[Line.selectors.line]: { [Line.selectors.line]: {

View File

@ -2,13 +2,14 @@
import { VisCrosshair, VisTooltip } from '@unovis/vue' import { VisCrosshair, VisTooltip } from '@unovis/vue'
import type { BulletLegendItemInterface } from '@unovis/ts' import type { BulletLegendItemInterface } from '@unovis/ts'
import { omit } from '@unovis/ts' import { omit } from '@unovis/ts'
import { createApp, watch } from 'vue' import { type Component, createApp } from 'vue'
import { ChartTooltip } from '@/lib/registry/default/ui/chart' import { ChartTooltip } from '@/lib/registry/default/ui/chart'
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
colors: string[] colors: string[]
index: string index: string
items: BulletLegendItemInterface[] items: BulletLegendItemInterface[]
customTooltip?: Component
}>(), { }>(), {
colors: () => [], colors: () => [],
}) })
@ -25,7 +26,8 @@ function template(d: any) {
const legendReference = props.items.find(i => i.name === key) const legendReference = props.items.find(i => i.name === key)
return { ...legendReference, value } return { ...legendReference, value }
}) })
createApp(ChartTooltip, { title: d[props.index].toString(), data: omittedData }).mount(componentDiv) const TooltipComponent = props.customTooltip ?? ChartTooltip
createApp(TooltipComponent, { title: d[props.index].toString(), data: omittedData }).mount(componentDiv)
wm.set(d, componentDiv.innerHTML) wm.set(d, componentDiv.innerHTML)
return componentDiv.innerHTML return componentDiv.innerHTML
} }

View File

@ -2,7 +2,7 @@
import { VisTooltip } from '@unovis/vue' import { VisTooltip } from '@unovis/vue'
import type { BulletLegendItemInterface } from '@unovis/ts' import type { BulletLegendItemInterface } from '@unovis/ts'
import { omit } from '@unovis/ts' import { omit } from '@unovis/ts'
import { createApp } from 'vue' import { type Component, createApp } from 'vue'
import { ChartTooltip } from '@/lib/registry/default/ui/chart' import { ChartTooltip } from '@/lib/registry/default/ui/chart'
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
@ -10,6 +10,7 @@ const props = withDefaults(defineProps<{
index: string index: string
items?: BulletLegendItemInterface[] items?: BulletLegendItemInterface[]
valueFormatter?: (tick: number, i?: number, ticks?: number[]) => string valueFormatter?: (tick: number, i?: number, ticks?: number[]) => string
customTooltip?: Component
}>(), { }>(), {
valueFormatter: (tick: number) => `${tick}`, valueFormatter: (tick: number) => `${tick}`,
}) })
@ -27,11 +28,13 @@ function template(d: any, i: number, elements: (HTMLElement | SVGElement)[]) {
const legendReference = props.items?.find(i => i.name === key) const legendReference = props.items?.find(i => i.name === key)
return { ...legendReference, value: props.valueFormatter(value) } return { ...legendReference, value: props.valueFormatter(value) }
}) })
createApp(ChartTooltip, { title: d[props.index], data: omittedData }).mount(componentDiv) const TooltipComponent = props.customTooltip ?? ChartTooltip
createApp(TooltipComponent, { title: d[props.index], data: omittedData }).mount(componentDiv)
wm.set(d, componentDiv.innerHTML) wm.set(d, componentDiv.innerHTML)
return componentDiv.innerHTML return componentDiv.innerHTML
} }
} }
else { else {
const data = d.data const data = d.data
@ -42,7 +45,8 @@ function template(d: any, i: number, elements: (HTMLElement | SVGElement)[]) {
const style = getComputedStyle(elements[i]) const style = getComputedStyle(elements[i])
const omittedData = [{ name: data.name, value: props.valueFormatter(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') const componentDiv = document.createElement('div')
createApp(ChartTooltip, { title: d[props.index], data: omittedData }).mount(componentDiv) const TooltipComponent = props.customTooltip ?? ChartTooltip
createApp(TooltipComponent, { title: d[props.index], data: omittedData }).mount(componentDiv)
wm.set(d, componentDiv.innerHTML) wm.set(d, componentDiv.innerHTML)
return componentDiv.innerHTML return componentDiv.innerHTML
} }

View File

@ -1,19 +1,28 @@
<script setup lang="ts" generic="T extends Record<string, any>"> <script setup lang="ts" generic="T extends Record<string, any>">
import type { BulletLegendItemInterface, Spacing } from '@unovis/ts' import { type BulletLegendItemInterface, CurveType } from '@unovis/ts'
import { VisArea, VisAxis, VisLine, VisXYContainer } from '@unovis/vue' import { VisArea, VisAxis, VisLine, VisXYContainer } from '@unovis/vue'
import { Area, Axis, Line } from '@unovis/ts' import { Area, Axis, Line } from '@unovis/ts'
import { computed, ref } from 'vue' import { type Component, computed, ref } from 'vue'
import { useMounted } from '@vueuse/core' import { useMounted } from '@vueuse/core'
import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/new-york/ui/chart' import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/new-york/ui/chart'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = withDefaults(defineProps<BaseChartProps<T> & { const props = withDefaults(defineProps<BaseChartProps<T> & {
/**
* Render custom tooltip component.
*/
customTooltip?: Component
/**
* Type of curve
*/
curveType?: CurveType
/** /**
* Controls the visibility of gradient. * Controls the visibility of gradient.
* @default true * @default true
*/ */
showGradiant?: boolean showGradiant?: boolean
}>(), { }>(), {
curveType: CurveType.Basis,
filterOpacity: 0.2, filterOpacity: 0.2,
margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }), margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
showXAxis: true, showXAxis: true,
@ -66,13 +75,14 @@ function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
</defs> </defs>
</svg> </svg>
<ChartCrosshair v-if="showTooltip" :colors="colors" :items="legendItems" :index="index" /> <ChartCrosshair v-if="showTooltip" :colors="colors" :items="legendItems" :index="index" :custom-tooltip="customTooltip" />
<template v-for="(category, i) in categories" :key="category"> <template v-for="(category, i) in categories" :key="category">
<VisArea <VisArea
:x="(d: Data, i: number) => i" :x="(d: Data, i: number) => i"
:y="(d: Data) => d[category]" :y="(d: Data) => d[category]"
color="auto" color="auto"
:curve-type="curveType"
:attributes="{ :attributes="{
[Area.selectors.area]: { [Area.selectors.area]: {
fill: `url(#color-${i})`, fill: `url(#color-${i})`,
@ -87,6 +97,7 @@ function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
:x="(d: Data, i: number) => i" :x="(d: Data, i: number) => i"
:y="(d: Data) => d[category]" :y="(d: Data) => d[category]"
:color="colors[i]" :color="colors[i]"
:curve-type="curveType"
:attributes="{ :attributes="{
[Line.selectors.line]: { [Line.selectors.line]: {
opacity: legendItems.find(item => item.name === category)?.inactive ? filterOpacity : 1, opacity: legendItems.find(item => item.name === category)?.inactive ? filterOpacity : 1,

View File

@ -2,12 +2,16 @@
import type { BulletLegendItemInterface, Spacing } from '@unovis/ts' import type { BulletLegendItemInterface, Spacing } from '@unovis/ts'
import { VisAxis, VisGroupedBar, VisStackedBar, VisXYContainer } from '@unovis/vue' import { VisAxis, VisGroupedBar, VisStackedBar, VisXYContainer } from '@unovis/vue'
import { Axis, GroupedBar, StackedBar } from '@unovis/ts' import { Axis, GroupedBar, StackedBar } from '@unovis/ts'
import { computed, ref } from 'vue' import { type Component, computed, ref } from 'vue'
import { useMounted } from '@vueuse/core' import { useMounted } from '@vueuse/core'
import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/new-york/ui/chart' import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/new-york/ui/chart'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = withDefaults(defineProps<BaseChartProps<T> & { const props = withDefaults(defineProps<BaseChartProps<T> & {
/**
* Render custom tooltip component.
*/
customTooltip?: Component
/** /**
* Change the type of the chart * Change the type of the chart
* @default "grouped" * @default "grouped"
@ -57,7 +61,7 @@ const selectorsBar = computed(() => props.type === 'grouped' ? GroupedBar.select
<template> <template>
<div :class="cn('w-full h-[400px] flex flex-col items-end', $attrs.class ?? '')"> <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" /> <ChartLegend v-if="showLegend" v-model:items="legendItems" :custom-tooltip="customTooltip" @legend-item-click="handleLegendItemClick" />
<VisXYContainer <VisXYContainer
:data="data" :data="data"

View File

@ -1,7 +1,7 @@
<script setup lang="ts" generic="T extends Record<string, any>"> <script setup lang="ts" generic="T extends Record<string, any>">
import { VisDonut, VisSingleContainer } from '@unovis/vue' import { VisDonut, VisSingleContainer } from '@unovis/vue'
import { Donut, type Spacing } from '@unovis/ts' import { Donut } from '@unovis/ts'
import { computed, ref } from 'vue' import { type DefineComponent, computed, ref } from 'vue'
import { useMounted } from '@vueuse/core' import { useMounted } from '@vueuse/core'
import { type BaseChartProps, ChartSingleTooltip, defaultColors } from '@/lib/registry/new-york/ui/chart' import { type BaseChartProps, ChartSingleTooltip, defaultColors } from '@/lib/registry/new-york/ui/chart'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
@ -24,7 +24,10 @@ const props = withDefaults(defineProps<Pick<BaseChartProps<T>, 'data' | 'colors'
* Controls the formatting for the label. * Controls the formatting for the label.
*/ */
valueFormatter?: (tick: number, i?: number, ticks?: number[]) => string valueFormatter?: (tick: number, i?: number, ticks?: number[]) => string
/**
* Render custom tooltip component.
*/
customTooltip?: DefineComponent
}>(), { }>(), {
margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }), margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
sortFunction: () => undefined, sortFunction: () => undefined,
@ -63,6 +66,7 @@ const totalValue = computed(() => props.data.reduce((prev, curr) => {
:index="category" :index="category"
:items="legendItems" :items="legendItems"
:value-formatter="valueFormatter" :value-formatter="valueFormatter"
:custom-tooltip="customTooltip"
/> />
<VisDonut <VisDonut
@ -75,7 +79,6 @@ const totalValue = computed(() => props.data.reduce((prev, curr) => {
:events="{ :events="{
[Donut.selectors.segment]: { [Donut.selectors.segment]: {
click: (d: Data, ev: PointerEvent, i: number, elements: HTMLElement[]) => { click: (d: Data, ev: PointerEvent, i: number, elements: HTMLElement[]) => {
console.log(d, ev, i, elements)
if (d?.data?.[index] === activeSegmentKey) { if (d?.data?.[index] === activeSegmentKey) {
activeSegmentKey = undefined activeSegmentKey = undefined
elements.forEach(el => el.style.opacity = '1') elements.forEach(el => el.style.opacity = '1')

View File

@ -1,12 +1,23 @@
<script setup lang="ts" generic="T extends Record<string, any>"> <script setup lang="ts" generic="T extends Record<string, any>">
import { Axis, type BulletLegendItemInterface, Line, type Spacing } from '@unovis/ts' import { type BulletLegendItemInterface, CurveType } from '@unovis/ts'
import { VisAxis, VisLine, VisXYContainer } from '@unovis/vue' import { VisAxis, VisLine, VisXYContainer } from '@unovis/vue'
import { computed, ref } from 'vue' import { Axis, Line } from '@unovis/ts'
import { type Component, computed, ref } from 'vue'
import { useMounted } from '@vueuse/core' import { useMounted } from '@vueuse/core'
import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/new-york/ui/chart' import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/new-york/ui/chart'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = withDefaults(defineProps<BaseChartProps<T>>(), { const props = withDefaults(defineProps<BaseChartProps<T> & {
/**
* Render custom tooltip component.
*/
customTooltip?: Component
/**
* Type of curve
*/
curveType?: CurveType
}>(), {
curveType: CurveType.Basis,
filterOpacity: 0.2, filterOpacity: 0.2,
margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }), margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
showXAxis: true, showXAxis: true,
@ -44,16 +55,17 @@ function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
<ChartLegend v-if="showLegend" v-model:items="legendItems" @legend-item-click="handleLegendItemClick" /> <ChartLegend v-if="showLegend" v-model:items="legendItems" @legend-item-click="handleLegendItemClick" />
<VisXYContainer <VisXYContainer
:margin="margin" :margin="{ left: 20, right: 20 }"
:data="data" :data="data"
:style="{ height: isMounted ? '100%' : 'auto' }" :style="{ height: isMounted ? '100%' : 'auto' }"
> >
<ChartCrosshair v-if="showTooltip" :colors="colors" :items="legendItems" :index="index" /> <ChartCrosshair v-if="showTooltip" :colors="colors" :items="legendItems" :index="index" :custom-tooltip="customTooltip" />
<template v-for="(category, i) in categories" :key="category"> <template v-for="(category, i) in categories" :key="category">
<VisLine <VisLine
:x="(d: Data, i: number) => i" :x="(d: Data, i: number) => i"
:y="(d: Data) => d[category]" :y="(d: Data) => d[category]"
:curve-type="curveType"
:color="colors[i]" :color="colors[i]"
:attributes="{ :attributes="{
[Line.selectors.line]: { [Line.selectors.line]: {

View File

@ -2,13 +2,14 @@
import { VisCrosshair, VisTooltip } from '@unovis/vue' import { VisCrosshair, VisTooltip } from '@unovis/vue'
import type { BulletLegendItemInterface } from '@unovis/ts' import type { BulletLegendItemInterface } from '@unovis/ts'
import { omit } from '@unovis/ts' import { omit } from '@unovis/ts'
import { createApp, watch } from 'vue' import { type Component, createApp } from 'vue'
import { ChartTooltip } from '@/lib/registry/new-york/ui/chart' import { ChartTooltip } from '@/lib/registry/new-york/ui/chart'
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
colors: string[] colors: string[]
index: string index: string
items: BulletLegendItemInterface[] items: BulletLegendItemInterface[]
customTooltip?: Component
}>(), { }>(), {
colors: () => [], colors: () => [],
}) })
@ -25,7 +26,8 @@ function template(d: any) {
const legendReference = props.items.find(i => i.name === key) const legendReference = props.items.find(i => i.name === key)
return { ...legendReference, value } return { ...legendReference, value }
}) })
createApp(ChartTooltip, { title: d[props.index].toString(), data: omittedData }).mount(componentDiv) const TooltipComponent = props.customTooltip ?? ChartTooltip
createApp(TooltipComponent, { title: d[props.index].toString(), data: omittedData }).mount(componentDiv)
wm.set(d, componentDiv.innerHTML) wm.set(d, componentDiv.innerHTML)
return componentDiv.innerHTML return componentDiv.innerHTML
} }

View File

@ -2,7 +2,7 @@
import { VisTooltip } from '@unovis/vue' import { VisTooltip } from '@unovis/vue'
import type { BulletLegendItemInterface } from '@unovis/ts' import type { BulletLegendItemInterface } from '@unovis/ts'
import { omit } from '@unovis/ts' import { omit } from '@unovis/ts'
import { createApp } from 'vue' import { type Component, createApp } from 'vue'
import { ChartTooltip } from '@/lib/registry/new-york/ui/chart' import { ChartTooltip } from '@/lib/registry/new-york/ui/chart'
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
@ -10,6 +10,7 @@ const props = withDefaults(defineProps<{
index: string index: string
items?: BulletLegendItemInterface[] items?: BulletLegendItemInterface[]
valueFormatter?: (tick: number, i?: number, ticks?: number[]) => string valueFormatter?: (tick: number, i?: number, ticks?: number[]) => string
customTooltip?: Component
}>(), { }>(), {
valueFormatter: (tick: number) => `${tick}`, valueFormatter: (tick: number) => `${tick}`,
}) })
@ -27,11 +28,13 @@ function template(d: any, i: number, elements: (HTMLElement | SVGElement)[]) {
const legendReference = props.items?.find(i => i.name === key) const legendReference = props.items?.find(i => i.name === key)
return { ...legendReference, value: props.valueFormatter(value) } return { ...legendReference, value: props.valueFormatter(value) }
}) })
createApp(ChartTooltip, { title: d[props.index], data: omittedData }).mount(componentDiv) const TooltipComponent = props.customTooltip ?? ChartTooltip
createApp(TooltipComponent, { title: d[props.index], data: omittedData }).mount(componentDiv)
wm.set(d, componentDiv.innerHTML) wm.set(d, componentDiv.innerHTML)
return componentDiv.innerHTML return componentDiv.innerHTML
} }
} }
else { else {
const data = d.data const data = d.data
@ -42,7 +45,8 @@ function template(d: any, i: number, elements: (HTMLElement | SVGElement)[]) {
const style = getComputedStyle(elements[i]) const style = getComputedStyle(elements[i])
const omittedData = [{ name: data.name, value: props.valueFormatter(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') const componentDiv = document.createElement('div')
createApp(ChartTooltip, { title: d[props.index], data: omittedData }).mount(componentDiv) const TooltipComponent = props.customTooltip ?? ChartTooltip
createApp(TooltipComponent, { title: d[props.index], data: omittedData }).mount(componentDiv)
wm.set(d, componentDiv.innerHTML) wm.set(d, componentDiv.innerHTML)
return componentDiv.innerHTML return componentDiv.innerHTML
} }