Skip to content

Commit

Permalink
feat(chart): BarChart
Browse files Browse the repository at this point in the history
  • Loading branch information
jiwangyihao committed Oct 5, 2024
1 parent 9948c7b commit ac744af
Show file tree
Hide file tree
Showing 14 changed files with 1,271 additions and 44 deletions.
5 changes: 4 additions & 1 deletion extensions/chart/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@
"dependencies": {
"@vue-motion/core": "workspace:*",
"@vue-motion/lib": "workspace:*",
"vue": "^3.4.26"
"vue": "^3.4.26",
"@types/luxon": "^3.4.2",
"luxon": "^3.4.4",
"string-width": "^7.1.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.4",
Expand Down
165 changes: 165 additions & 0 deletions extensions/chart/src/barChart.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
<script setup lang="ts">
import { defineWidget } from '@vue-motion/core'
import { onMounted, provide, ref, watchEffect } from 'vue'
import type { DateTime } from 'luxon'
import type { Growable } from '@vue-motion/lib'
import { Rect } from '@vue-motion/lib'
import type { ChartLayoutConfig } from './chartLayout.vue'
import BaseSimpleChart from './baseSimpleChart.vue'
import type { BaseChartDataSet, BaseChartOptions, BaseChartStyle, ChartStyle, Color } from '.'
import { ColorEnum, DataUtil } from '.'
/**
* BarChart style.
* @public
* @interface
* @category BarChart
* @property borderRadius - The border radius of the bar. Default is 0. Not supported yet.
*/
export interface BarChartStyle extends BaseChartStyle {
borderRadius?: number
}
export interface BarChartOptions extends BaseChartOptions, Growable {
labels?: string[] | DateTime[]
categoryPercentage?: number
barPercentage?: number
style?: BarChartStyle
}
export interface BaseChartData {
/**
* @property string[] labels
* @description
* labels is an array of strings that represent the labels of the chart, which are displayed on the index-axis.
* It is optional.
* If not provided, the labels will be generated automatically.
*/
labels?: string[] | DateTime[]
datasets: BaseChartDataSet<BarChartStyle>[]
style?: ChartStyle
}
const props = withDefaults(defineProps<BarChartOptions>(), {
gridAlign: true,
categoryPercentage: 0.8,
barPercentage: 0.8,
})
const options = defineWidget<BarChartOptions>(props)
const data = ref<BaseChartData>({
labels: options.labels,
datasets: [],
})
provide('chartData', data)
const layoutConfig = ref<ChartLayoutConfig>({})
provide('chartLayoutConfig', layoutConfig)
const barSets = ref<{
width: number
height: number
x: number
y: number
style: {
fillColor: Color
borderColor: Color
borderWidth: number
radius: number
}
}[][]>([])
onMounted(() => {
watchEffect(() => {
barSets.value = data.value.datasets.map((set, setIndex) => {
set.style ??= {}
set.style.backgroundColor ??= data.value.style?.backgroundColor ?? ColorEnum.WHITE
set.style.borderColor ??= data.value.style?.borderColor ?? ColorEnum.WHITE
set.style.borderWidth ??= data.value.style?.borderWidth ?? 1
set.style.borderRadius ??= data.value.style?.borderRadius ?? 0
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 categorySize = gridSize * options.categoryPercentage!
const barSize = (categorySize / data.value.datasets.length) * options.barPercentage!
return {
width: barSize,
height: -unit.cross * (options.progress ?? 1) * layoutConfig.value.height!
/ (layoutConfig.value.cross!.max - layoutConfig.value.cross!.min),
x: (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.width!
+ (gridSize - categorySize) / 2 + (setIndex * categorySize) / data.value.datasets.length
+ (categorySize / data.value.datasets.length - barSize) / 2,
y: layoutConfig.value.height! - (0 - layoutConfig.value.cross!.min)
/ (layoutConfig.value.cross!.max - layoutConfig.value.cross!.min) * layoutConfig.value.height!,
style: {
fillColor: unit.style?.backgroundColor ?? set.style!.backgroundColor!,
borderColor: unit.style?.borderColor ?? set.style!.borderColor!,
borderWidth: unit.style?.borderWidth ?? set.style!.borderWidth!,
radius: unit.style?.borderRadius ?? set.style!.borderRadius!,
},
}
})
}
else {
return set.data.map((unit) => {
const gridSize = (DataUtil.indexDuration(unit) ?? layoutConfig.value.index!.interval)
/ (layoutConfig.value.index!.max - layoutConfig.value.index!.min) * layoutConfig.value.height!
const categorySize = gridSize * options.categoryPercentage!
const barSize = (categorySize / data.value.datasets.length) * options.barPercentage!
return {
width: unit.cross * (options.progress ?? 1) * layoutConfig.value.width!
/ (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)
/ (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,
style: {
fillColor: unit.style?.backgroundColor ?? set.style!.backgroundColor!,
borderColor: unit.style?.borderColor ?? set.style!.borderColor!,
borderWidth: unit.style?.borderWidth ?? set.style!.borderWidth!,
radius: unit.style?.borderRadius ?? set.style!.borderRadius!,
},
}
})
}
})
})
})
</script>

<template>
<BaseSimpleChart
v-bind="{
gridAlign: true,
edgeOffset: !(options.gridAlign),
...options,
}"
>
<slot />
<!-- BarSets -->
<g v-for="(barSet, setIndex) in barSets" :key="setIndex">
<g v-for="(bar, barIndex) in barSet" :key="barIndex">
<Rect
:x="bar.x"
:y="bar.height < 0 ? bar.y + bar.height : bar.y"
:width="bar.width"
:height="bar.height < 0 ? -bar.height : bar.height"
:fill="bar.style.fillColor"
fill-opacity="0.2"
:stroke="bar.style.borderColor"
:stroke-width="bar.style.borderWidth"
:radius="bar.style.radius"
/>
</g>
</g>
</BaseSimpleChart>
</template>

<style scoped>
</style>
Loading

0 comments on commit ac744af

Please sign in to comment.