mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-07 07:16:09 +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": "8.1.0",
|
||||||
"vite-string-plugin": "2.0.4",
|
"vite-string-plugin": "2.0.4",
|
||||||
"vue": "3.5.38",
|
"vue": "3.5.38",
|
||||||
"vue-bar-graph": "2.2.0",
|
|
||||||
"vue-chartjs": "5.3.3"
|
"vue-chartjs": "5.3.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
Generated
-12
@@ -200,9 +200,6 @@ importers:
|
|||||||
vue:
|
vue:
|
||||||
specifier: 3.5.38
|
specifier: 3.5.38
|
||||||
version: 3.5.38(typescript@6.0.3)
|
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:
|
vue-chartjs:
|
||||||
specifier: 5.3.3
|
specifier: 5.3.3
|
||||||
version: 5.3.3(chart.js@4.5.1)(vue@3.5.38(typescript@6.0.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:
|
vscode-uri@3.1.0:
|
||||||
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
|
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:
|
vue-chartjs@5.3.3:
|
||||||
resolution: {integrity: sha512-jqxtL8KZ6YJ5NTv6XzrzLS7osyegOi28UGNZW0h9OkDL7Sh1396ht4Dorh04aKrl2LiSalQ84WtqiG0RIJb0tA==}
|
resolution: {integrity: sha512-jqxtL8KZ6YJ5NTv6XzrzLS7osyegOi28UGNZW0h9OkDL7Sh1396ht4Dorh04aKrl2LiSalQ84WtqiG0RIJb0tA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -8979,12 +8973,6 @@ snapshots:
|
|||||||
|
|
||||||
vscode-uri@3.1.0: {}
|
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)):
|
vue-chartjs@5.3.3(chart.js@4.5.1)(vue@3.5.38(typescript@6.0.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
chart.js: 4.5.1
|
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>.
|
<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>
|
||||||
<div class="ui attached segment">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
Vendored
-18
@@ -47,21 +47,3 @@ declare module '@citation-js/core' {
|
|||||||
declare module '@citation-js/plugin-software-formats' {}
|
declare module '@citation-js/plugin-software-formats' {}
|
||||||
declare module '@citation-js/plugin-bibtex' {}
|
declare module '@citation-js/plugin-bibtex' {}
|
||||||
declare module '@citation-js/plugin-csl' {}
|
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>
|
<script lang="ts" setup>
|
||||||
import {VueBarGraph} from 'vue-bar-graph';
|
import {onMounted, shallowRef, useTemplateRef, type ShallowRef} from 'vue';
|
||||||
import {computed, 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({
|
const colors = shallowRef({
|
||||||
barColor: 'green',
|
barColor: 'green',
|
||||||
@@ -18,26 +25,19 @@ type ActivityAuthorData = {
|
|||||||
|
|
||||||
const activityTopAuthors: Array<ActivityAuthorData> = window.config.pageData.repoActivityTopAuthors || [];
|
const activityTopAuthors: Array<ActivityAuthorData> = window.config.pageData.repoActivityTopAuthors || [];
|
||||||
|
|
||||||
const graphPoints = computed(() => {
|
const graphWidth = activityTopAuthors.length * barSlotWidth;
|
||||||
return activityTopAuthors.map((item) => {
|
const maxCommits = Math.max(...activityTopAuthors.map((author) => author.commits));
|
||||||
return {
|
|
||||||
value: item.commits,
|
|
||||||
label: item.name,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const graphAuthors = computed(() => {
|
const bars = activityTopAuthors.map((author, index) => {
|
||||||
return activityTopAuthors.map((item, idx: number) => {
|
const height = author.commits / maxCommits * innerChartHeight;
|
||||||
return {
|
return {
|
||||||
position: idx + 1,
|
author,
|
||||||
...item,
|
index,
|
||||||
};
|
x: index * barSlotWidth,
|
||||||
});
|
height,
|
||||||
});
|
yOffset: innerChartHeight - height,
|
||||||
|
labelInside: height >= labelInsideThreshold,
|
||||||
const graphWidth = computed(() => {
|
};
|
||||||
return activityTopAuthors.length * 40;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const styleElement = useTemplateRef('styleElement') as Readonly<ShallowRef<HTMLDivElement>>;
|
const styleElement = useTemplateRef('styleElement') as Readonly<ShallowRef<HTMLDivElement>>;
|
||||||
@@ -59,49 +59,34 @@ onMounted(() => {
|
|||||||
<div>
|
<div>
|
||||||
<div class="activity-bar-graph tw-w-0 tw-h-0" ref="styleElement"/>
|
<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"/>
|
<div class="activity-bar-graph-alt tw-w-0 tw-h-0" ref="altStyleElement"/>
|
||||||
<vue-bar-graph
|
<svg :width="graphWidth" :height="chartHeight">
|
||||||
:points="graphPoints"
|
<g v-for="bar in bars" :key="bar.index" :transform="`translate(${bar.x},0)`">
|
||||||
:show-x-axis="true"
|
<title>{{ bar.author.name }}</title>
|
||||||
:show-y-axis="false"
|
<rect :width="barWidth" :height="bar.height" :x="2" :y="bar.yOffset" :style="{fill: colors.barColor}"/>
|
||||||
:show-values="true"
|
<text
|
||||||
:width="graphWidth"
|
:x="barMidPoint"
|
||||||
:bar-color="colors.barColor"
|
:y="bar.yOffset"
|
||||||
:text-color="colors.textColor"
|
:dy="bar.labelInside ? '15px' : '-5px'"
|
||||||
:text-alt-color="colors.textAltColor"
|
text-anchor="middle"
|
||||||
:height="100"
|
:style="{fill: bar.labelInside ? colors.textAltColor : colors.textColor, font: '10px sans-serif'}"
|
||||||
:label-height="20"
|
>{{ bar.author.commits }}</text>
|
||||||
>
|
<a v-if="bar.author.home_link" :href="bar.author.home_link">
|
||||||
<template #label="opt">
|
<image :x="barMidPoint - avatarSize / 2" :y="innerChartHeight + 4" :height="avatarSize" :width="avatarSize" :href="bar.author.avatar_link"/>
|
||||||
<g v-for="(author, idx) in graphAuthors" :key="author.position">
|
</a>
|
||||||
<a
|
<image v-else :x="barMidPoint - avatarSize / 2" :y="innerChartHeight + 4" :height="avatarSize" :width="avatarSize" :href="bar.author.avatar_link"/>
|
||||||
v-if="opt.bar.index === idx && author.home_link"
|
<line class="axis-line" :x1="barMidPoint" :x2="barMidPoint" :y1="innerChartHeight + 3" :y2="innerChartHeight"/>
|
||||||
:href="author.home_link"
|
</g>
|
||||||
>
|
<line class="axis-line" :x1="2" :x2="graphWidth" :y1="innerChartHeight" :y2="innerChartHeight"/>
|
||||||
<image
|
</svg>
|
||||||
: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>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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