Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/i18n/message/popup/header-resource.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"en": {
"rate": "Rate Us",
"showSiteName": "Display site name",
"showTopN": "Display top {n}"
"showTopN": "Display top {n}",
"donutChart": "Displayed as donut charts"
},
"zh_CN": {
"rate": "评分",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/message/popup/header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import resource from './header-resource.json'

export type HeaderMessage = {
donutChart: string
rate: string
showSiteName: string
showTopN: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { type DimensionEntry, type ValueFormatter } from "@app/components/Analysis/util"
import { useEcharts } from "@hooks/useEcharts"
import { defineComponent, watch } from "vue"
import { defineComponent } from "vue"
import Wrapper from "./Wrapper"

type Props = {
Expand All @@ -18,14 +18,15 @@ type Props = {
}

const _default = defineComponent<Props>(props => {
const { elRef, refresh } = useEcharts(Wrapper, () => ({
const { elRef } = useEcharts(Wrapper, () => ({
entries: props.data,
preEntries: props.previous,
title: props.title,
valueFormatter: props.valueFormatter,
}))
}), {
deps: [() => props.data, () => props.valueFormatter],
})

watch([() => props.data, () => props.valueFormatter], refresh)
return () => <div ref={elRef} style={{ width: '100%', height: '100%' }} />
}, { props: ['data', 'previous', 'title', 'valueFormatter'] })

Expand Down
17 changes: 8 additions & 9 deletions src/pages/hooks/useEcharts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { processAnimation, processAria, processFont, processRtl } from "@util/ec
import { type AriaComponentOption, type ComposeOption, SeriesOption, TitleComponentOption } from "echarts"
import { type ECharts, init } from "echarts/core"
import { ElLoading } from "element-plus"
import { type Ref, isRef, onMounted, ref, watch } from "vue"
import { type Ref, type WatchSource, isRef, onMounted, ref, watch } from "vue"
import { useElementSize } from './useElementSize'
import { useWindowSize } from "./useWindowSize"

Expand Down Expand Up @@ -86,7 +86,6 @@ export abstract class EchartsWrapper<BizOption, EchartsOption> {


type WrapperResult<BizOption, EchartsOption, EW extends EchartsWrapper<BizOption, EchartsOption>> = {
refresh: () => Promise<void>
elRef: Ref<HTMLDivElement | undefined>
wrapper: EW
}
Expand All @@ -97,15 +96,17 @@ export const useEcharts = <BizOption, EchartsOption, EW extends EchartsWrapper<B
option?: {
hideLoading?: boolean
manual?: boolean
afterInit?: (ew: EW) => void
afterInit?: ArgCallback<EW>,
deps?: WatchSource | WatchSource[],
}): WrapperResult<BizOption, EchartsOption, EW> => {
const elRef = ref<HTMLDivElement>()
const wrapperInstance = new Wrapper()
const {
hideLoading = false,
manual = false,
afterInit,
} = option || {}
deps,
} = option ?? {}

let refresh = async () => {
const loading = hideLoading ? null : ElLoading.service({ target: elRef.value })
Expand All @@ -124,13 +125,11 @@ export const useEcharts = <BizOption, EchartsOption, EW extends EchartsWrapper<B
isRef(fetch) && watch(fetch, refresh)
})

deps && watch(deps, refresh)

const { width: winW, height: winH } = useWindowSize()
const { width: elW, height: elH } = useElementSize(elRef, { debounce: 50 })
watch([winW, winH, elW, elH], () => wrapperInstance?.resize?.())

return {
refresh,
elRef,
wrapper: wrapperInstance,
}
return { elRef, wrapper: wrapperInstance }
}
13 changes: 10 additions & 3 deletions src/pages/popup/components/Header/Option.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { ROUTE_PERCENTAGE } from '@popup/router'
import { ElCheckbox, ElIcon, ElInputNumber, ElPopover, ElText, useNamespace } from "element-plus"
import { computed, defineComponent, type StyleValue } from "vue"
import { useRoute } from 'vue-router'
// import "./option.sass"

const reference = () => (
<ElIcon size="large" style={{ cursor: 'pointer' } satisfies StyleValue}>
Expand All @@ -20,12 +19,16 @@ const reference = () => (
const Option = defineComponent(() => {
const option = useOption()
const route = useRoute()
const showTopN = computed(() => !!route.path?.endsWith(ROUTE_PERCENTAGE))
const isPercentage = computed(() => !!route.path?.endsWith(ROUTE_PERCENTAGE))

const toggleName = () => {
option.showName = !option.showName
close()
}
const toggleDonutChart = () => {
option.donutChart = !option.donutChart
close()
}
const handleSiteChange = (v: number | undefined) => {
if (!v) return
option.topN = v
Expand Down Expand Up @@ -59,7 +62,11 @@ const Option = defineComponent(() => {
<ElCheckbox size='small' modelValue={option.showName} />
<ElText size='small'>{t(msg => msg.header.showSiteName)}</ElText>
</Flex>
<Flex v-show={showTopN.value}>
<Flex v-show={isPercentage.value} gap={4} align='center' cursor="pointer" onClick={toggleDonutChart}>
<ElCheckbox size='small' modelValue={!!option.donutChart} />
<ElText size='small'>{t(msg => msg.header.donutChart)}</ElText>
</Flex>
<Flex v-show={isPercentage.value}>
<ElText size='small'>
{tN(msg => msg.header.showTopN, {
n: <ElInputNumber
Expand Down
10 changes: 6 additions & 4 deletions src/pages/popup/components/Percentage/Cate/Wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import {
} from "echarts/components"
import { type ComposeOption, type ECElementEvent } from "echarts/core"
import {
formatTooltip, generateSiteSeriesOption, generateTitleOption, generateToolboxOption, handleClick, isOther,
type PieSeriesItemOption,
adaptDonutSeries, formatTooltip, generateSiteSeriesOption, generateTitleOption, generateToolboxOption,
handleClick, isOther, type PieSeriesItemOption,
} from "../chart"
import { type PercentageResult } from "../query"

Expand Down Expand Up @@ -85,7 +85,7 @@ export default class SiteWrapper extends EchartsWrapper<PercentageResult, EcOpti

if (!result) return {}

const { rows, query } = result
const { rows, query, donutChart } = result
const { dimension } = query
const selected: timer.stat.Row | undefined = this.selectedCache
? rows.filter(isCate).filter(r => r.cateKey === this.selectedCache)[0]
Expand Down Expand Up @@ -113,8 +113,8 @@ export default class SiteWrapper extends EchartsWrapper<PercentageResult, EcOpti
const series: PieSeriesOption[] = [{
type: "pie",
center: selected ? ['15%', '28%'] : ['58%', '56%'],
radius: selected ? '30%' : '55%',
selectedMode: 'single',
selectedOffset: 5,
startAngle: 180,
data: rows.filter(isCate).map(row => ({
value: row[dimension], row,
Expand All @@ -135,6 +135,8 @@ export default class SiteWrapper extends EchartsWrapper<PercentageResult, EcOpti
position: selected ? 'inner' : 'outer',
fontSize: selected ? 10 : undefined,
},
// not display as donut chart if selected
...adaptDonutSeries(donutChart, selected ? '30%' : '55%', selected ? 0.3 : undefined),
}]
if (selected) {
let mergedRows = (selected?.mergedRows || []).sort((a, b) => (b[dimension] ?? 0) - (a[dimension] ?? 0))
Expand Down
5 changes: 2 additions & 3 deletions src/pages/popup/components/Percentage/Cate/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEcharts } from "@hooks/useEcharts"
import { usePopupContext } from "@popup/context"
import { defineComponent, toRef, watch } from "vue"
import { defineComponent, toRef } from "vue"
import { type PercentageResult } from "../query"
import Wrapper from "./Wrapper"

Expand All @@ -11,8 +11,7 @@ type Props = {
const Cate = defineComponent<Props>(props => {
const { darkMode } = usePopupContext()
const data = toRef(props, 'value')
const { elRef, refresh } = useEcharts(Wrapper, data)
watch(darkMode, refresh)
const { elRef } = useEcharts(Wrapper, data, { deps: darkMode })

return () => <div ref={elRef} style={{ width: '100%', height: '100%' }} />
}, { props: ['value'] })
Expand Down
5 changes: 2 additions & 3 deletions src/pages/popup/components/Percentage/Site/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEcharts } from "@hooks/useEcharts"
import { usePopupContext } from "@popup/context"
import { defineComponent, toRef, watch } from "vue"
import { defineComponent, toRef } from "vue"
import { type PercentageResult } from "../query"
import Wrapper from "./Wrapper"

Expand All @@ -11,8 +11,7 @@ type Props = {
const Site = defineComponent<Props>(props => {
const { darkMode } = usePopupContext()
const data = toRef(props, 'value')
const { elRef, refresh } = useEcharts(Wrapper, data)
watch(darkMode, refresh)
const { elRef } = useEcharts(Wrapper, data, { deps: darkMode })

return () => <div ref={elRef} style={{ width: '100%', height: '100%' }} />
}, { props: ['value'] })
Expand Down
43 changes: 41 additions & 2 deletions src/pages/popup/components/Percentage/chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ type CustomOption = Pick<
>

export function generateSiteSeriesOption(rows: timer.stat.Row[], result: PercentageResult, customOption: CustomOption): PieSeriesOption {
const { displaySiteName, query: { dimension }, itemCount, groups } = result || {}
const { displaySiteName, query: { dimension }, itemCount, groups, donutChart } = result
const groupMap = toMap(groups, g => g.id)

const chartRows = cvt2ChartRows(rows, dimension, itemCount)
Expand Down Expand Up @@ -268,7 +268,46 @@ export function generateSiteSeriesOption(rows: timer.stat.Row[], result: Percent
},
},
avoidLabelOverlap: true,
...customOption || {},
...customOption,
...adaptDonutSeries(donutChart, customOption.radius),
}
}

export function adaptDonutSeries(
donutChart: boolean,
radius: CustomOption['radius'],
donutRadiusRatio: number = 0.5,
): Pick<PieSeriesOption, 'itemStyle' | 'padAngle' | 'radius' | 'minAngle'> {
return {
...donutChart ? {
itemStyle: { borderRadius: 5 },
padAngle: 1,
minAngle: 1.5,
} : {
itemStyle: { borderRadius: 0 },
padAngle: 0,
minAngle: 0,
},
radius: calcRealRadius(donutChart, radius, donutRadiusRatio),
}
}

function calcRealRadius(
donutChart: boolean,
radius: CustomOption['radius'],
donutRadiusRatio: number = 0.5,
): CustomOption['radius'] {
if (!donutChart) return radius
if (!radius) return radius
if (Array.isArray(radius)) return radius
if (typeof radius === 'number') return [radius * donutRadiusRatio, radius]
// String
try {
const percent = parseFloat(radius.replace('%', ''))
const inner = percent * donutRadiusRatio
return [`${inner}%`, radius]
} catch {
return radius
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/pages/popup/components/Percentage/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type PercentageResult = {
itemCount: number
dateLength: number
groups: chrome.tabGroups.TabGroup[]
donutChart: boolean
}

const findAllDates = (row: timer.stat.Row): Set<string> => {
Expand Down Expand Up @@ -68,5 +69,6 @@ export const doQuery = async (query: PopupQuery, option: PopupOption): Promise<P
chartTitle: t(msg => msg.content.percentage.title[query?.duration], { n: query?.durationNum }),
itemCount,
groups,
donutChart: !!option.donutChart,
} satisfies PercentageResult
}
2 changes: 2 additions & 0 deletions src/pages/popup/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export type PopupQuery = {
export type PopupOption = {
showName: boolean
topN: number
donutChart: boolean
}

type PopupContextValue = {
Expand Down Expand Up @@ -79,6 +80,7 @@ const initOption = () => {
const [optionCache, setOptionCache] = useLocalStorage<PopupOption>('popup-option', {
showName: true,
topN: 10,
donutChart: false,
})

const option = reactive(optionCache)
Expand Down