mirror of
https://github.com/go-gitea/gitea.git
synced 2026-07-02 02:50:22 +00:00
refactor: replace vue-bar-graph dependency with inlined SVG chart (#38292)
Inlines the small SVG bar graph into `RepoActivityTopAuthors.vue` (its only consumer) and drops the `vue-bar-graph` npm dependency. - Bars render at static height (dropped the grow animation). - Theme-aware axis color instead of a hardcoded `#555555`. - Removed the dangling `role="img"`/`aria-labelledby` on the `<svg>`. - Reserve the chart height so the page does not shift when the component mounts. <img width="416" height="110" alt="Screenshot 2026-07-01 at 11 15 25" src="https://github.com/user-attachments/assets/b2db4d0c-20f1-4345-9951-32a908abfaba" /> <img width="419" height="110" alt="Screenshot 2026-07-01 at 11 15 35" src="https://github.com/user-attachments/assets/853305a5-575f-4a26-ba3b-12fc51081324" /> fyi @lafriks --------- Signed-off-by: silverwind <me@silverwind.io>
This commit is contained in:
@@ -70,7 +70,6 @@
|
||||
"vite": "8.1.0",
|
||||
"vite-string-plugin": "2.0.4",
|
||||
"vue": "3.5.38",
|
||||
"vue-bar-graph": "2.2.0",
|
||||
"vue-chartjs": "5.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -200,9 +200,6 @@ importers:
|
||||
vue:
|
||||
specifier: 3.5.38
|
||||
version: 3.5.38(typescript@6.0.3)
|
||||
vue-bar-graph:
|
||||
specifier: 2.2.0
|
||||
version: 2.2.0(typescript@6.0.3)
|
||||
vue-chartjs:
|
||||
specifier: 5.3.3
|
||||
version: 5.3.3(chart.js@4.5.1)(vue@3.5.38(typescript@6.0.3))
|
||||
@@ -4306,9 +4303,6 @@ packages:
|
||||
vscode-uri@3.1.0:
|
||||
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
|
||||
|
||||
vue-bar-graph@2.2.0:
|
||||
resolution: {integrity: sha512-1xFPho2nM6nFDziExLu48vKO+Q90gjxz1NyHfc+MhgfYDSxR9BMyhOIXUO5EmwKIVEX5dBoP2n3Ius8SjKRD4g==}
|
||||
|
||||
vue-chartjs@5.3.3:
|
||||
resolution: {integrity: sha512-jqxtL8KZ6YJ5NTv6XzrzLS7osyegOi28UGNZW0h9OkDL7Sh1396ht4Dorh04aKrl2LiSalQ84WtqiG0RIJb0tA==}
|
||||
peerDependencies:
|
||||
@@ -8979,12 +8973,6 @@ snapshots:
|
||||
|
||||
vscode-uri@3.1.0: {}
|
||||
|
||||
vue-bar-graph@2.2.0(typescript@6.0.3):
|
||||
dependencies:
|
||||
vue: 3.5.38(typescript@6.0.3)
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
|
||||
vue-chartjs@5.3.3(chart.js@4.5.1)(vue@3.5.38(typescript@6.0.3)):
|
||||
dependencies:
|
||||
chart.js: 4.5.1
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
<strong class="tw-text-red">{{ctx.Locale.TrN .Activity.Code.Deletions "repo.activity.git_stats_deletion_1" "repo.activity.git_stats_deletion_n" .Activity.Code.Deletions}}</strong>.
|
||||
</div>
|
||||
<div class="ui attached segment">
|
||||
<div id="repo-activity-top-authors-chart"></div>
|
||||
<div id="repo-activity-top-authors-chart" class="tw-h-[100px]"></div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
18
types.d.ts
vendored
18
types.d.ts
vendored
@@ -47,21 +47,3 @@ declare module '@citation-js/core' {
|
||||
declare module '@citation-js/plugin-software-formats' {}
|
||||
declare module '@citation-js/plugin-bibtex' {}
|
||||
declare module '@citation-js/plugin-csl' {}
|
||||
|
||||
declare module 'vue-bar-graph' {
|
||||
import type {DefineComponent} from 'vue';
|
||||
|
||||
interface BarGraphPoint {
|
||||
value: number;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export const VueBarGraph: DefineComponent<{
|
||||
points?: Array<BarGraphPoint>;
|
||||
barColor?: string;
|
||||
textColor?: string;
|
||||
textAltColor?: string;
|
||||
height?: number;
|
||||
labelHeight?: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
<script lang="ts" setup>
|
||||
import {VueBarGraph} from 'vue-bar-graph';
|
||||
import {computed, onMounted, shallowRef, useTemplateRef, type ShallowRef} from 'vue';
|
||||
import {onMounted, shallowRef, useTemplateRef, type ShallowRef} from 'vue';
|
||||
|
||||
const barSlotWidth = 40; // horizontal space allotted per author
|
||||
const chartHeight = 100; // keep in sync with reserved height in template
|
||||
const innerChartHeight = chartHeight - 28; // 28 = avatar/x-axis label row (20) + 8px padding
|
||||
const barMidPoint = barSlotWidth / 2;
|
||||
const barWidth = barSlotWidth - 2; // 2px gap between bars
|
||||
const avatarSize = 20;
|
||||
const labelInsideThreshold = 22; // bars at least this tall carry the commit count inside them
|
||||
|
||||
const colors = shallowRef({
|
||||
barColor: 'green',
|
||||
@@ -18,26 +25,19 @@ type ActivityAuthorData = {
|
||||
|
||||
const activityTopAuthors: Array<ActivityAuthorData> = window.config.pageData.repoActivityTopAuthors || [];
|
||||
|
||||
const graphPoints = computed(() => {
|
||||
return activityTopAuthors.map((item) => {
|
||||
return {
|
||||
value: item.commits,
|
||||
label: item.name,
|
||||
};
|
||||
});
|
||||
});
|
||||
const graphWidth = activityTopAuthors.length * barSlotWidth;
|
||||
const maxCommits = Math.max(...activityTopAuthors.map((author) => author.commits));
|
||||
|
||||
const graphAuthors = computed(() => {
|
||||
return activityTopAuthors.map((item, idx: number) => {
|
||||
return {
|
||||
position: idx + 1,
|
||||
...item,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const graphWidth = computed(() => {
|
||||
return activityTopAuthors.length * 40;
|
||||
const bars = activityTopAuthors.map((author, index) => {
|
||||
const height = author.commits / maxCommits * innerChartHeight;
|
||||
return {
|
||||
author,
|
||||
index,
|
||||
x: index * barSlotWidth,
|
||||
height,
|
||||
yOffset: innerChartHeight - height,
|
||||
labelInside: height >= labelInsideThreshold,
|
||||
};
|
||||
});
|
||||
|
||||
const styleElement = useTemplateRef('styleElement') as Readonly<ShallowRef<HTMLDivElement>>;
|
||||
@@ -59,49 +59,34 @@ onMounted(() => {
|
||||
<div>
|
||||
<div class="activity-bar-graph tw-w-0 tw-h-0" ref="styleElement"/>
|
||||
<div class="activity-bar-graph-alt tw-w-0 tw-h-0" ref="altStyleElement"/>
|
||||
<vue-bar-graph
|
||||
:points="graphPoints"
|
||||
:show-x-axis="true"
|
||||
:show-y-axis="false"
|
||||
:show-values="true"
|
||||
:width="graphWidth"
|
||||
:bar-color="colors.barColor"
|
||||
:text-color="colors.textColor"
|
||||
:text-alt-color="colors.textAltColor"
|
||||
:height="100"
|
||||
:label-height="20"
|
||||
>
|
||||
<template #label="opt">
|
||||
<g v-for="(author, idx) in graphAuthors" :key="author.position">
|
||||
<a
|
||||
v-if="opt.bar.index === idx && author.home_link"
|
||||
:href="author.home_link"
|
||||
>
|
||||
<image
|
||||
:x="`${opt.bar.midPoint - 10}px`"
|
||||
:y="`${opt.bar.yLabel}px`"
|
||||
height="20"
|
||||
width="20"
|
||||
:href="author.avatar_link"
|
||||
/>
|
||||
</a>
|
||||
<image
|
||||
v-else-if="opt.bar.index === idx"
|
||||
:x="`${opt.bar.midPoint - 10}px`"
|
||||
:y="`${opt.bar.yLabel}px`"
|
||||
height="20"
|
||||
width="20"
|
||||
:href="author.avatar_link"
|
||||
/>
|
||||
</g>
|
||||
</template>
|
||||
<template #title="opt">
|
||||
<tspan v-for="(author, idx) in graphAuthors" :key="author.position">
|
||||
<tspan v-if="opt.bar.index === idx">
|
||||
{{ author.name }}
|
||||
</tspan>
|
||||
</tspan>
|
||||
</template>
|
||||
</vue-bar-graph>
|
||||
<svg :width="graphWidth" :height="chartHeight">
|
||||
<g v-for="bar in bars" :key="bar.index" :transform="`translate(${bar.x},0)`">
|
||||
<title>{{ bar.author.name }}</title>
|
||||
<rect :width="barWidth" :height="bar.height" :x="2" :y="bar.yOffset" :style="{fill: colors.barColor}"/>
|
||||
<text
|
||||
:x="barMidPoint"
|
||||
:y="bar.yOffset"
|
||||
:dy="bar.labelInside ? '15px' : '-5px'"
|
||||
text-anchor="middle"
|
||||
:style="{fill: bar.labelInside ? colors.textAltColor : colors.textColor, font: '10px sans-serif'}"
|
||||
>{{ bar.author.commits }}</text>
|
||||
<a v-if="bar.author.home_link" :href="bar.author.home_link">
|
||||
<image :x="barMidPoint - avatarSize / 2" :y="innerChartHeight + 4" :height="avatarSize" :width="avatarSize" :href="bar.author.avatar_link"/>
|
||||
</a>
|
||||
<image v-else :x="barMidPoint - avatarSize / 2" :y="innerChartHeight + 4" :height="avatarSize" :width="avatarSize" :href="bar.author.avatar_link"/>
|
||||
<line class="axis-line" :x1="barMidPoint" :x2="barMidPoint" :y1="innerChartHeight + 3" :y2="innerChartHeight"/>
|
||||
</g>
|
||||
<line class="axis-line" :x1="2" :x2="graphWidth" :y1="innerChartHeight" :y2="innerChartHeight"/>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
svg {
|
||||
display: block; /* avoid the inline-baseline gap so the reserved container height matches exactly */
|
||||
}
|
||||
.axis-line {
|
||||
stroke: var(--color-secondary-alpha-60);
|
||||
stroke-width: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user