mirror of
https://github.com/go-gitea/gitea.git
synced 2026-07-03 08:03:34 +00:00
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>
93 lines
3.4 KiB
Vue
93 lines
3.4 KiB
Vue
<script lang="ts" setup>
|
|
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',
|
|
textColor: 'black',
|
|
textAltColor: 'white',
|
|
});
|
|
|
|
type ActivityAuthorData = {
|
|
avatar_link: string;
|
|
commits: number;
|
|
home_link: string;
|
|
login: string;
|
|
name: string;
|
|
}
|
|
|
|
const activityTopAuthors: Array<ActivityAuthorData> = window.config.pageData.repoActivityTopAuthors || [];
|
|
|
|
const graphWidth = activityTopAuthors.length * barSlotWidth;
|
|
const maxCommits = Math.max(...activityTopAuthors.map((author) => author.commits));
|
|
|
|
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>>;
|
|
const altStyleElement = useTemplateRef('altStyleElement') as Readonly<ShallowRef<HTMLDivElement>>;
|
|
|
|
onMounted(() => {
|
|
const refStyle = window.getComputedStyle(styleElement.value);
|
|
const refAltStyle = window.getComputedStyle(altStyleElement.value);
|
|
|
|
colors.value = {
|
|
barColor: refStyle.backgroundColor,
|
|
textColor: refStyle.color,
|
|
textAltColor: refAltStyle.color,
|
|
};
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<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"/>
|
|
<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>
|