diff options
Diffstat (limited to 'web_src/js/components/RepoActivityTopAuthors.vue')
-rw-r--r-- | web_src/js/components/RepoActivityTopAuthors.vue | 164 |
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 00000000..3752bc58 --- /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> |