Skip to content

Commit

Permalink
Support more charts of habit (#207)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheepzh committed Jan 2, 2024
1 parent 7999e66 commit e075293
Show file tree
Hide file tree
Showing 24 changed files with 260 additions and 140 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* Copyright (c) 2023 Hengyang Zhang
*
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
Expand All @@ -20,7 +20,7 @@ const _default = defineComponent({
setup(props) {
const elRef: Ref<HTMLDivElement> = ref()
const wrapper: ChartWrapper = new ChartWrapper()
const render = () => wrapper.render(props.data, props.title, props.valueFormatter)
const render = () => wrapper.render({ entries: props.data, title: props.title, valueFormatter: props.valueFormatter })

watch(() => props.data, render)
watch(() => props.valueFormatter, render)
Expand Down
115 changes: 58 additions & 57 deletions src/app/components/analysis/components/trend/dimension/wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import type { DimensionEntry } from "../../../util"
import type { ECharts, ComposeOption } from "echarts/core"
import type { ComposeOption } from "echarts/core"
import type { LineSeriesOption } from "echarts/charts"
import type {
TitleComponentOption,
Expand All @@ -15,7 +15,7 @@ import type {
GridComponentOption,
} from "echarts/components"

import { init, use } from "@echarts/core"
import { use } from "@echarts/core"
import LineChart from "@echarts/chart/line"
import SVGRenderer from "@echarts/svg-renderer"
import TitleComponent from "@echarts/component/title"
Expand All @@ -24,6 +24,7 @@ import GridComponent from '@echarts/component/grid'

import { ValueFormatter } from "@app/components/analysis/util"
import { getSecondaryTextColor } from "@util/style"
import { EchartsWrapper } from "@app/components/common/echarts-wrapper"

use([
LineChart,
Expand All @@ -41,66 +42,66 @@ type EcOption = ComposeOption<
| GridComponentOption
>

class ChartWrapper {
instance: ECharts

init(container: HTMLDivElement) {
this.instance = init(container)
}
type BizOption = {
entries: DimensionEntry[]
title: string
valueFormatter: ValueFormatter
}

async render(entries: DimensionEntry[], title: string, valueFormatter: ValueFormatter) {
const xAxis = entries.map(r => r.date)
const yAxis = entries.map(r => r.value)
const generateOption = ({ entries, title, valueFormatter }: BizOption) => {
const xAxis = entries.map(r => r.date)
const yAxis = entries.map(r => r.value)

const secondaryTextColor = getSecondaryTextColor()
const option: EcOption = {
backgroundColor: 'rgba(0,0,0,0)',
title: {
text: title,
textStyle: {
color: secondaryTextColor,
fontSize: '14px',
fontWeight: 'normal',
},
left: 'center',
top: '9%',
},
grid: {
top: '30%',
bottom: '10px',
left: '5%',
right: '5%',
const secondaryTextColor = getSecondaryTextColor()
const option: EcOption = {
backgroundColor: 'rgba(0,0,0,0)',
title: {
text: title,
textStyle: {
color: secondaryTextColor,
fontSize: '14px',
fontWeight: 'normal',
},
tooltip: {
trigger: 'axis',
formatter(params: any) {
const format = params instanceof Array ? params[0] : params
const { name, value } = format
const valStr = valueFormatter?.(value) || value?.toString() || "NaN"
return `${name}<br/>${valStr}`
},
left: 'center',
top: '9%',
},
grid: {
top: '30%',
bottom: '10px',
left: '5%',
right: '5%',
},
tooltip: {
trigger: 'axis',
formatter(params: any) {
const format = params instanceof Array ? params[0] : params
const { name, value } = format
const valStr = valueFormatter?.(value) || value?.toString() || "NaN"
return `${name}<br/>${valStr}`
},
xAxis: {
type: 'category',
data: xAxis,
show: false,
},
yAxis: {
type: 'value',
axisLabel: { show: false },
axisTick: { show: false },
splitLine: { show: false },
},
series: {
data: yAxis,
type: 'line',
symbol: 'none',
areaStyle: {},
smooth: true,
}
},
xAxis: {
type: 'category',
data: xAxis,
show: false,
},
yAxis: {
type: 'value',
axisLabel: { show: false },
axisTick: { show: false },
splitLine: { show: false },
},
series: {
data: yAxis,
type: 'line',
symbol: 'none',
areaStyle: {},
smooth: true,
}
this.instance?.setOption(option)
}
return option
}

export default ChartWrapper
export default class ChartWrapper extends EchartsWrapper<BizOption, EcOption> {
generateOption = generateOption
}
28 changes: 28 additions & 0 deletions src/app/components/common/echarts-wrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Copyright (c) 2024 Hengyang Zhang
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/

import { init, type ECharts } from "echarts"

export abstract class EchartsWrapper<BizOption, EchartsOption> {
private instance: ECharts

init(container: HTMLDivElement) {
this.instance = init(container)
}

async render(biz: BizOption) {
if (!this.instance) return
const option = await this.generateOption(biz)
this.instance.setOption(option, { notMerge: false })
}

protected getDom(): HTMLElement {
return this.instance?.getDom?.()
}

protected abstract generateOption(biz: BizOption): Promise<EchartsOption> | EchartsOption
}
7 changes: 7 additions & 0 deletions src/app/components/common/kanban/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* Copyright (c) 2024 Hengyang Zhang
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/

import IndicatorCell from "./indicator-cell"
import IndicatorRow from "./indicator-row"
import Card from "./card"
Expand Down
7 changes: 7 additions & 0 deletions src/app/components/common/kanban/indicator-row.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* Copyright (c) 2024 Hengyang Zhang
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/

import { ElRow } from "element-plus"
import { defineComponent, h } from "vue"
import "./indicator-row.sass"
Expand Down
7 changes: 7 additions & 0 deletions src/app/components/common/time-format-filter-item.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* Copyright (c) 2024 Hengyang Zhang
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/

import { PropType, defineComponent, h, ref } from "vue"
import SelectFilterItem from "./select-filter-item"
import { t } from "@app/locale"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ function optionOf(data: _Value[], days: string[]): EcOption {

/**
* Click to jump to the report page
*
*
* @since 1.1.1
*/
function handleClick(value: _Value): void {
Expand Down
23 changes: 8 additions & 15 deletions src/app/components/dashboard/components/top-k-visit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
* https://opensource.org/licenses/MIT
*/

import type { ECharts, ComposeOption } from "echarts/core"
import type { ComposeOption } from "echarts/core"
import type { PieSeriesOption } from "echarts/charts"
import type { TitleComponentOption, TooltipComponentOption } from "echarts/components"
import type { Ref } from "vue"
import type { StatQueryParam } from "@service/stat-service"

import { init, use } from "@echarts/core"
import { use } from "@echarts/core"
import PieChart from "@echarts/chart/pie"
import TitleComponent from "@echarts/component/title"
import TooltipComponent from "@echarts/component/tooltip"
Expand All @@ -26,6 +26,7 @@ import { BASE_TITLE_OPTION } from "../common"
import { t } from "@app/locale"
import { getPrimaryTextColor } from "@util/style"
import { generateSiteLabel } from "@util/site"
import { EchartsWrapper } from "@app/components/common/echarts-wrapper"

const CONTAINER_ID = '__timer_dashboard_top_k_visit'
const TOP_NUM = 6
Expand All @@ -45,7 +46,7 @@ type _Value = {
alias?: string
}

function optionOf(data: _Value[]): EcOption {
function generateOption(data: _Value[]): EcOption {
const textColor = getPrimaryTextColor()
return {
title: {
Expand Down Expand Up @@ -83,17 +84,8 @@ function optionOf(data: _Value[]): EcOption {
}
}

class ChartWrapper {
instance: ECharts

init(container: HTMLDivElement) {
this.instance = init(container)
}
render(data: _Value[], loading: { close: () => void }) {
const option = optionOf(data)
this.instance.setOption(option)
loading.close()
}
class ChartWrapper extends EchartsWrapper<_Value[], EcOption> {
generateOption = generateOption
}

const _default = defineComponent({
Expand All @@ -120,7 +112,8 @@ const _default = defineComponent({
for (let realSize = top.length; realSize < TOP_NUM; realSize++) {
data.push({ name: '', host: '', value: 0 })
}
chartWrapper.render(data, loading)
await chartWrapper.render(data)
loading.close()
})
return () => h('div', {
id: CONTAINER_ID,
Expand Down
7 changes: 7 additions & 0 deletions src/app/components/habit/components/context.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* Copyright (c) 2024 Hengyang Zhang
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/

import { Ref } from "vue"
import { FilterOption } from "../type"
import { provideWithNs, useProviderWithNs } from "@app/util/provider"
Expand Down
29 changes: 11 additions & 18 deletions src/app/components/habit/components/period/bar-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
* https://opensource.org/licenses/MIT
*/

import type { ECharts } from "echarts/core"
import type { ComposeOption } from "echarts/core"
import type { BarSeriesOption } from "echarts/charts"
import type { GridComponentOption, TooltipComponentOption } from "echarts/components"

import { use, init } from "@echarts/core"
import { use } from "@echarts/core"
import BarChart from "@echarts/chart/bar"
import SVGRenderer from "@echarts/svg-renderer"
import TooltipComponent from "@echarts/component/tooltip"
Expand All @@ -19,6 +18,7 @@ import { formatPeriodCommon, formatTime } from "@util/time"
import { t } from "@app/locale"
import { getPrimaryTextColor } from "@util/style"
import { averageByDay } from "./common"
import { EchartsWrapper } from "@app/components/common/echarts-wrapper"

use([BarChart, SVGRenderer, TooltipComponent, GridComponent])

Expand All @@ -28,6 +28,12 @@ type EcOption = ComposeOption<
| TooltipComponentOption
>

type BizOption = {
data: timer.period.Row[]
averageByDate: boolean
periodSize: number
}

function formatXAxis(ts: number) {
const date = new Date(ts)
if (date.getHours() === 0 && date.getMinutes() === 0) {
Expand Down Expand Up @@ -71,7 +77,7 @@ const cvt2Item = (row: timer.period.Row, periodSize: number): BarItem => {
return [x, getYAxisValue(milliseconds, periodSize), startTime, endTime, milliseconds]
}

function generateOptions(data: timer.period.Row[], averageByDate: boolean, periodSize: number): EcOption {
function generateOption({ data, averageByDate, periodSize }: BizOption): EcOption {
const periodData: timer.period.Row[] = averageByDate ? averageByDay(data, periodSize) : data
const valueData: BarItem[] = periodData.map(i => cvt2Item(i, periodSize))
const xAxisMin = periodData[0]?.startTime?.getTime()
Expand Down Expand Up @@ -111,19 +117,6 @@ function generateOptions(data: timer.period.Row[], averageByDate: boolean, perio
}]
}
}

export default class ChartWrapper {
instance: ECharts

init(container: HTMLDivElement) {
this.instance = init(container)
}

render(data: timer.period.Row[], averageByDate: boolean, periodSize: number) {
if (!this.instance) {
throw new Error("Instance not initialized")
}
const option: EcOption = generateOptions(data, averageByDate, periodSize)
this.instance.setOption(option)
}
export default class ChartWrapper extends EchartsWrapper<BizOption, EcOption> {
generateOption = generateOption
}
2 changes: 1 addition & 1 deletion src/app/components/habit/components/period/bar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const _default = defineComponent({

watch([filter, rows], () => {
const { periodSize, average } = filter.value || {}
wrapper.render(rows.value, average, periodSize)
wrapper.render({ data: rows.value, averageByDate: average, periodSize })
})

return () => h('div', {
Expand Down
7 changes: 7 additions & 0 deletions src/app/components/habit/components/period/common.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* Copyright (c) 2024 Hengyang Zhang
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/

import { PERIODS_PER_DATE, after, keyOf, rowOf, startOrderOfRow } from "@util/period"
import { MILL_PER_DAY } from "@util/time"

Expand Down
Loading

0 comments on commit e075293

Please sign in to comment.