summaryrefslogtreecommitdiffstats
path: root/web_src/js/components/RepoActivityTopAuthors.vue
diff options
context:
space:
mode:
authorDaniel Baumann <daniel@debian.org>2024-10-18 20:33:49 +0200
committerDaniel Baumann <daniel@debian.org>2024-10-18 20:33:49 +0200
commitdd136858f1ea40ad3c94191d647487fa4f31926c (patch)
tree58fec94a7b2a12510c9664b21793f1ed560c6518 /web_src/js/components/RepoActivityTopAuthors.vue
parentInitial commit. (diff)
downloadforgejo-upstream.tar.xz
forgejo-upstream.zip
Adding upstream version 9.0.0.HEADupstream/9.0.0upstreamdebian
Signed-off-by: Daniel Baumann <daniel@debian.org>
Diffstat (limited to 'web_src/js/components/RepoActivityTopAuthors.vue')
-rw-r--r--web_src/js/components/RepoActivityTopAuthors.vue164
1 files changed, 164 insertions, 0 deletions
diff --git a/web_src/js/components/RepoActivityTopAuthors.vue b/web_src/js/components/RepoActivityTopAuthors.vue
new file mode 100644
index 0000000..3752bc5
--- /dev/null
+++ b/web_src/js/components/RepoActivityTopAuthors.vue
@@ -0,0 +1,164 @@
+<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>