Skip to content

Commit

Permalink
feat(chart): add MixedChart
Browse files Browse the repository at this point in the history
  • Loading branch information
jiwangyihao committed Oct 6, 2024
1 parent cd43dde commit e9e6152
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 33 deletions.
20 changes: 14 additions & 6 deletions extensions/chart/src/barChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface BarChartOptions extends BaseSimpleChartOptions, Growable {
const props = withDefaults(defineProps<BarChartOptions>(), {
gridAlign: true,
edgeOffset: undefined,
categoryPercentage: 0.8,
barPercentage: 0.8,
})
Expand All @@ -49,6 +50,9 @@ const barSets = ref<{
onMounted(() => {
watchEffect(() => {
if (!layoutConfig.value.done)
return
barSets.value = data.value.datasets.map((set, setIndex) => {
set.style ??= {}
set.style.backgroundColor ??= data.value.style?.backgroundColor ?? ColorEnum.WHITE
Expand All @@ -58,8 +62,8 @@ onMounted(() => {
if (layoutConfig.value.indexAxis === 'x') {
return set.data.map((unit) => {
const gridSize = (DataUtil.indexDuration(unit) ?? layoutConfig.value.index!.interval)
/ (layoutConfig.value.index!.max - layoutConfig.value.index!.min) * layoutConfig.value.width!
const gridSize = (DataUtil.indexDuration(unit) ?? layoutConfig.value.index.interval)
/ (layoutConfig.value.index.max - layoutConfig.value.index.min) * layoutConfig.value.width!
const categorySize = gridSize * options.categoryPercentage!
const barSize = (categorySize / data.value.datasets.length) * options.barPercentage!
Expand Down Expand Up @@ -93,11 +97,11 @@ onMounted(() => {
/ (layoutConfig.value.cross!.max - layoutConfig.value.cross!.min),
height: barSize,
x: (0 - layoutConfig.value.cross!.min) / (layoutConfig.value.cross!.max - layoutConfig.value.cross!.min) * layoutConfig.value.width!,
y: (DataUtil.indexNumber(unit) - (DataUtil.indexDuration(unit) ?? layoutConfig.value.index!.interval) / 2 - layoutConfig.value.index!.min)
y: layoutConfig.value.height! - ((DataUtil.indexNumber(unit) - (DataUtil.indexDuration(unit) ?? layoutConfig.value.index!.interval) / 2 - layoutConfig.value.index!.min)
/ (layoutConfig.value.index!.max - layoutConfig.value.index!.min) * layoutConfig.value.height!
+ (gridSize - categorySize) / 2
+ (setIndex * categorySize) / data.value.datasets.length
+ (categorySize / data.value.datasets.length - barSize) / 2,
+ (gridSize + categorySize) / 2
- (setIndex * categorySize) / data.value.datasets.length
- (categorySize / data.value.datasets.length - barSize) / 2),
style: {
fillColor: unit.style?.backgroundColor ?? set.style!.backgroundColor!,
borderColor: unit.style?.borderColor ?? set.style!.borderColor!,
Expand All @@ -108,8 +112,12 @@ onMounted(() => {
})
}
})
}, {
flush: 'post',
})
})
options.edgeOffset ??= !(options.gridAlign)
</script>

<template>
Expand Down
2 changes: 1 addition & 1 deletion extensions/chart/src/chartData.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface DateTimeWithDuration extends DateTime {
const props = defineProps<ChartDataOptions>()
const options = defineWidget<ChartDataOptions>(props)
const dataset = inject<Ref<BaseChartDataSet>>('chartDataset')
const dataset = inject<Ref<BaseChartDataSet<ChartStyle>>>('chartDataset')
dataset?.value.data.push(options)
</script>

Expand Down
5 changes: 4 additions & 1 deletion extensions/chart/src/chartLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export interface ChartLayoutConfig {
dateFormatOptions?: DateTimeFormatOptions
gridAlign?: boolean
edgeOffset?: boolean
done?: boolean
}
const props = withDefaults(defineProps<ChartLayoutOptions>(), {
Expand All @@ -76,7 +77,7 @@ const props = withDefaults(defineProps<ChartLayoutOptions>(), {
})
const options = defineWidget(props)
const data = inject<Ref<BaseSimpleChartData>>('chartData', ref({ datasets: [] }))
const data = inject<Ref<BaseSimpleChartData>>('chartData', inject<Ref<BaseSimpleChartData>>('mixedData', ref({ datasets: [] })))
const layoutConfig = inject<Ref<ChartLayoutConfig>>('chartLayoutConfig', ref({}))
const legendWidthPrefix = ref([0])
Expand Down Expand Up @@ -288,6 +289,8 @@ watchEffect(() => {
dataUnit.index = layoutConfig.value.index!.pos[index]
})
})
layoutConfig.value.done = true
}, {
flush: 'post',
})
Expand Down
2 changes: 2 additions & 0 deletions extensions/chart/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ export * from './scatterChart.vue'
export { default as ScatterChart } from './scatterChart.vue'
export * from './bubbleChart.vue'
export { default as BubbleChart } from './bubbleChart.vue'
export * from './mixedChart.vue'
export { default as MixedChart } from './mixedChart.vue'
6 changes: 4 additions & 2 deletions extensions/chart/src/lineChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export interface LineChartOptions extends BaseSimpleChartOptions, Growable {
}
const props = withDefaults(defineProps<LineChartOptions>(), {
gridAlign: false,
gridAlign: undefined,
})
const {
options,
Expand All @@ -117,6 +117,8 @@ const paths = ref<string[]>([])
onMounted(() => {
watchEffect(() => {
if (!layoutConfig.value.done)
return
if (data.value.datasets.length === 0)
return
if (data.value.datasets.some(dataset => (dataset.data.length === 0)))
Expand Down Expand Up @@ -184,7 +186,7 @@ onMounted(() => {
<template>
<BaseSimpleChart
v-bind="{
gridAlign: true,
gridAlign: false,
edgeOffset: !(options.gridAlign),
...options,
}"
Expand Down
77 changes: 77 additions & 0 deletions extensions/chart/src/mixedChart.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<script setup lang="ts">
import { defineWidget } from '@vue-motion/core'
import { type Ref, inject, provide, ref, watchEffect } from 'vue'
import { widget } from '@vue-motion/lib'
import type { ChartLayoutConfig } from './chartLayout.vue'
import ChartLayout from './chartLayout.vue'
import type { BaseSimpleChartData, BaseSimpleChartOptions } from './baseSimpleChart.vue'
export interface MixedChartOptions extends BaseSimpleChartOptions {
}
export interface MixedChartData {
data: BaseSimpleChartData
options: BaseSimpleChartOptions
}
const props = withDefaults(defineProps<MixedChartOptions>(), {
gridAlign: true,
})
const options = defineWidget<MixedChartOptions>(props)
provide('mixedChartOptions', options)
const mixedChartData = inject<Ref<MixedChartData[]>>('mixedChartData', ref<MixedChartData[]>([]))
provide('mixedChartData', mixedChartData)
const mixedData = inject<Ref<BaseSimpleChartData>>('mixedData', ref({ datasets: [] }))
provide('mixedData', mixedData)
watchEffect(() => {
mixedData.value.labels = options.labels ?? mixedChartData.value.flatMap(data => data.data.labels)
mixedData.value.datasets = mixedChartData.value.flatMap(data => data.data.datasets)
}, {
flush: 'post',
})
const layoutConfig = inject<Ref<ChartLayoutConfig>>('chartLayoutConfig', ref<ChartLayoutConfig>({}))
provide('chartLayoutConfig', layoutConfig)
const mixedOptions = ref<BaseSimpleChartOptions>({})
watchEffect(() => {
mixedChartData.value.forEach((data) => {
for (const key in data.options) {
if (key as keyof BaseSimpleChartOptions)
mixedOptions.value[key as keyof BaseSimpleChartOptions] = data.options[key as keyof BaseSimpleChartOptions]
}
})
}, {
flush: 'post',
})
watchEffect(() => {
mixedChartData.value.forEach((data) => {
data.options.progress = options.progress
})
})
</script>

<template>
<g v-bind="widget(options)">
<ChartLayout
v-if="!options.layout"
v-bind="{
gridAlign: true,
edgeOffset: !(mixedOptions.gridAlign ?? options.gridAlign),
...mixedOptions,
...options,
x: 0,
y: 0,
}"
/>
<slot />
</g>
</template>
<style scoped>
</style>
25 changes: 19 additions & 6 deletions extensions/chart/src/utils/useSimpleChart.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
import { defineWidget } from '@vue-motion/core'
import type { Ref } from 'vue'
import { inject, provide, ref } from 'vue'
import { inject, provide, ref, unref } from 'vue'

import type { BaseSimpleChartData, BaseSimpleChartOptions } from '../baseSimpleChart.vue'

Check failure on line 5 in extensions/chart/src/utils/useSimpleChart.ts

View workflow job for this annotation

GitHub Actions / type-check

Module '"*.vue"' has no exported member 'BaseSimpleChartData'. Did you mean to use 'import BaseSimpleChartData from "*.vue"' instead?

Check failure on line 5 in extensions/chart/src/utils/useSimpleChart.ts

View workflow job for this annotation

GitHub Actions / type-check

Module '"*.vue"' has no exported member 'BaseSimpleChartOptions'. Did you mean to use 'import BaseSimpleChartOptions from "*.vue"' instead?
import type { ChartLayoutConfig } from '../chartLayout.vue'

Check failure on line 6 in extensions/chart/src/utils/useSimpleChart.ts

View workflow job for this annotation

GitHub Actions / type-check

Module '"*.vue"' has no exported member 'ChartLayoutConfig'. Did you mean to use 'import ChartLayoutConfig from "*.vue"' instead?
import type { MixedChartData } from '../mixedChart.vue'

Check failure on line 7 in extensions/chart/src/utils/useSimpleChart.ts

View workflow job for this annotation

GitHub Actions / type-check

Module '"*.vue"' has no exported member 'MixedChartData'. Did you mean to use 'import MixedChartData from "*.vue"' instead?

export function useSimpleChart<T extends BaseSimpleChartOptions>(props: T) {
const options = defineWidget<T>(props)

const mixedData = inject<Ref<MixedChartData[]>>('mixedChartData', ref<MixedChartData[]>([]))
let data = inject<Ref<BaseSimpleChartData>>('chartData')
data ??= ref<BaseSimpleChartData>({
labels: (options as BaseSimpleChartOptions).labels,
datasets: [],
})
if (!data) {
data = ref<BaseSimpleChartData>({
labels: (options as BaseSimpleChartOptions).labels,
datasets: [],
})
mixedData.value.push({
data: unref(data),
options,
})
}
provide('chartData', data)
let layoutConfig = inject<Ref<ChartLayoutConfig>>('chartLayoutConfig')
layoutConfig ??= ref<ChartLayoutConfig>({})
if (layoutConfig) {
options.layout = layoutConfig
}
else {
layoutConfig = ref<ChartLayoutConfig>({})
}
provide('chartLayoutConfig', layoutConfig)

data.value.style ??= {}
Expand Down
51 changes: 34 additions & 17 deletions test/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Motion, grow } from '@vue-motion/lib'
import { onMounted } from 'vue'
import type { MathFunction } from '@vue-motion/extension-math'
import type { BubbleChart } from '@vue-motion/extension-chart'
import { BarChart, ChartData, ChartDataset, ChartUtil } from '@vue-motion/extension-chart'
import { BarChart, ChartData, ChartDataset, ChartUtil, LineChart, MixedChart } from '@vue-motion/extension-chart'
import { DateTime, Duration } from 'luxon'
const fn1 = useWidget<InstanceType<typeof MathFunction>>('fn1')
Expand All @@ -22,7 +22,7 @@ onMounted(() => {
useAnimation(fn3)
.animate(grow, { duration: 1 })
useAnimation(chart1)
.animate(grow, { duration: 1 })
.animate(grow, { duration: 4 })
play()
})
Expand All @@ -39,7 +39,7 @@ onMounted(() => {
<!-- <Group> -->
<!-- <NumberPlane :ranges-x="[0, 10]" :ranges-y="[0, 10]" /> -->
<!-- </Group> -->
<BarChart
<MixedChart
:labels="ChartUtil.dateSequence(
DateTime.fromISO('2021-01-01').setLocale('en-US'),
Duration.fromObject({ months: 4 }),
Expand All @@ -49,19 +49,36 @@ onMounted(() => {
wid="chart1"
index-axis="y"
>
<ChartDataset label="test1" :style="{ borderColor: '#f00', backgroundColor: '#f00' }">
<ChartData :cross="1" />
<ChartData :cross="2" />
<ChartData :cross="0" />
<ChartData :cross="1" />
</ChartDataset>
<ChartDataset label="test2" :style="{ borderColor: '#ff0', backgroundColor: '#ff0', borderRadius: 4, tension: 0.2 }">
<ChartData :cross="2" />
<ChartData :cross="1" />
<ChartData :cross="3" />
<ChartData :cross="2" />
<!-- <ChartData :cross="1.5" :index="DateTime.fromISO('2021-03-15').setLocale('en-US')" :weight="10" /> -->
</ChartDataset>
</BarChart>
<BarChart>
<ChartDataset label="Bar1" :style="{ borderColor: '#f00', backgroundColor: '#f00' }">
<ChartData :cross="1" />
<ChartData :cross="2" />
<ChartData :cross="0" />
<ChartData :cross="1" />
</ChartDataset>
<ChartDataset label="Bar2" :style="{ borderColor: '#ff0', backgroundColor: '#ff0', borderRadius: 4, tension: 0.2 }">
<ChartData :cross="2" />
<ChartData :cross="1" />
<ChartData :cross="3" />
<ChartData :cross="2" />
<!-- <ChartData :cross="1.5" :index="DateTime.fromISO('2021-03-15').setLocale('en-US')" :weight="10" /> -->
</ChartDataset>
</BarChart>
<LineChart>
<ChartDataset label="Line1" :style="{ borderColor: '#00f', backgroundColor: '#00f' }">
<ChartData :cross="1" />
<ChartData :cross="2" />
<ChartData :cross="0" />
<ChartData :cross="1" />
</ChartDataset>
<ChartDataset label="Line2" :style="{ borderColor: '#0ff', backgroundColor: '#0ff', borderRadius: 4, tension: 0.2 }">
<ChartData :cross="2" />
<ChartData :cross="1" />
<ChartData :cross="3" />
<ChartData :cross="2" />
<ChartData :cross="1.5" :index="DateTime.fromISO('2021-03-15').setLocale('en-US')" :weight="10" />
</ChartDataset>
</LineChart>
</MixedChart>
</Motion>
</template>

0 comments on commit e9e6152

Please sign in to comment.