mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-23 12:59:11 -05:00
72f41306c2
- Regression of #4571 - We aren't showing the ticks generated by chartjs, because we want to show the avatar of the person instead. You can't *realy* disable that tick, so instead I opted to make them transparent in #4571, however they still affected the generation of ticks so if enough authors were being shown, for some the ticks were being skipped. Adjust the settings to make sure they are always being shown. - Resolves https://codeberg.org/forgejo/forgejo/issues/4982
164 lines
4.4 KiB
Vue
164 lines
4.4 KiB
Vue
<script>
|
|
import {Bar} from 'vue-chartjs';
|
|
import {
|
|
Chart,
|
|
Tooltip,
|
|
BarElement,
|
|
CategoryScale,
|
|
LinearScale,
|
|
} from 'chart.js';
|
|
import {chartJsColors} from '../utils/color.js';
|
|
import {createApp} from 'vue';
|
|
|
|
Chart.defaults.color = chartJsColors.text;
|
|
Chart.defaults.borderColor = chartJsColors.border;
|
|
|
|
Chart.register(
|
|
CategoryScale,
|
|
LinearScale,
|
|
BarElement,
|
|
Tooltip,
|
|
);
|
|
|
|
const sfc = {
|
|
components: {Bar},
|
|
props: {
|
|
locale: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
},
|
|
data: () => ({
|
|
colors: {
|
|
barColor: 'green',
|
|
},
|
|
|
|
// possible keys:
|
|
// * avatar_link: (...)
|
|
// * commits: (...)
|
|
// * home_link: (...)
|
|
// * login: (...)
|
|
// * name: (...)
|
|
activityTopAuthors: window.config.pageData.repoActivityTopAuthors || [],
|
|
i18nCommitActivity: this,
|
|
}),
|
|
methods: {
|
|
graphPoints() {
|
|
return {
|
|
datasets: [{
|
|
label: this.locale.commitActivity,
|
|
data: this.activityTopAuthors.map((item) => item.commits),
|
|
backgroundColor: this.colors.barColor,
|
|
barThickness: 40,
|
|
borderWidth: 0,
|
|
tension: 0.3,
|
|
}],
|
|
labels: this.activityTopAuthors.map((item) => item.name),
|
|
};
|
|
},
|
|
getOptions() {
|
|
return {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
animation: true,
|
|
scales: {
|
|
x: {
|
|
type: 'category',
|
|
grid: {
|
|
display: false,
|
|
},
|
|
ticks: {
|
|
// Disable the drawing of the labels on the x-asis and force them all
|
|
// of them to be 'shown', this avoids them being internally skipped
|
|
// for some data points. We rely on the internally generated ticks
|
|
// to know where to draw our own ticks. Set rotation to 90 degree
|
|
// and disable autoSkip. autoSkip is disabled to ensure no ticks are
|
|
// skipped and rotation is set to avoid messing with the width of the chart.
|
|
color: 'transparent',
|
|
minRotation: 90,
|
|
maxRotation: 90,
|
|
autoSkip: false,
|
|
},
|
|
},
|
|
y: {
|
|
ticks: {
|
|
stepSize: 1,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
},
|
|
},
|
|
mounted() {
|
|
const refStyle = window.getComputedStyle(this.$refs.style);
|
|
this.colors.barColor = refStyle.backgroundColor;
|
|
|
|
for (const item of this.activityTopAuthors) {
|
|
const img = new Image();
|
|
img.src = item.avatar_link;
|
|
item.avatar_img = img;
|
|
}
|
|
|
|
Chart.register({
|
|
id: 'image_label',
|
|
afterDraw: (chart) => {
|
|
const xAxis = chart.boxes[0];
|
|
const yAxis = chart.boxes[1];
|
|
for (const [index] of xAxis.ticks.entries()) {
|
|
const x = xAxis.getPixelForTick(index);
|
|
const img = this.activityTopAuthors[index].avatar_img;
|
|
|
|
chart.ctx.save();
|
|
chart.ctx.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight, x - 10, yAxis.bottom + 10, 20, 20);
|
|
chart.ctx.restore();
|
|
}
|
|
},
|
|
beforeEvent: (chart, args) => {
|
|
const event = args.event;
|
|
if (event.type !== 'mousemove' && event.type !== 'click') return;
|
|
|
|
const yAxis = chart.boxes[1];
|
|
if (event.y < yAxis.bottom + 10 || event.y > yAxis.bottom + 30) {
|
|
chart.canvas.style.cursor = '';
|
|
return;
|
|
}
|
|
|
|
const xAxis = chart.boxes[0];
|
|
const pointIdx = xAxis.ticks.findIndex((_, index) => {
|
|
const x = xAxis.getPixelForTick(index);
|
|
return event.x >= x - 10 && event.x <= x + 10;
|
|
});
|
|
|
|
if (pointIdx === -1) {
|
|
chart.canvas.style.cursor = '';
|
|
return;
|
|
}
|
|
|
|
chart.canvas.style.cursor = 'pointer';
|
|
if (event.type === 'click' && this.activityTopAuthors[pointIdx].home_link) {
|
|
window.location.href = this.activityTopAuthors[pointIdx].home_link;
|
|
}
|
|
},
|
|
});
|
|
},
|
|
};
|
|
|
|
export function initRepoActivityTopAuthorsChart() {
|
|
const el = document.getElementById('repo-activity-top-authors-chart');
|
|
if (el) {
|
|
createApp(sfc, {
|
|
locale: {
|
|
commitActivity: el.getAttribute('data-locale-commit-activity'),
|
|
},
|
|
}).mount(el);
|
|
}
|
|
}
|
|
|
|
export default sfc; // activate the IDE's Vue plugin
|
|
</script>
|
|
<template>
|
|
<div>
|
|
<div class="activity-bar-graph" ref="style" style="width: 0; height: 0;"/>
|
|
<Bar height="150px" :data="graphPoints()" :options="getOptions()"/>
|
|
</div>
|
|
</template>
|