gitea/web_src/js/components/RepoCodeFrequency.vue
Kerwin Bryant 02e49a0f47 Fix vertical centering of file tree icons and use entryIcon for submodules/symlinks (#34137)
In the file tree, the icons are not vertically centered, which affects
the overall visual consistency.
Currently, the icons of submodules and symlinks do not adopt the value
of entryIcon, resulting in inconsistent icon display.

before:
![3000-gogitea-gitea-y4ulxr46c4k ws-us118 gitpod io_test_test
gitea_src_branch_main_README md
(3)](https://github.com/user-attachments/assets/d521b89f-909a-43f9-8f39-787b0243b159)

after:
![3000-gogitea-gitea-y4ulxr46c4k ws-us118 gitpod io_test_test
gitea_src_branch_main_README md
(2)](https://github.com/user-attachments/assets/4866807f-c890-4709-b595-7086011e5231)

---------

Co-authored-by: silverwind <me@silverwind.io>
2025-04-09 18:10:16 -07:00

174 lines
4.0 KiB
Vue

<script lang="ts" setup>
import {SvgIcon} from '../svg.ts';
import {
Chart,
Legend,
LinearScale,
TimeScale,
PointElement,
LineElement,
Filler,
type ChartOptions,
type ChartData,
} from 'chart.js';
import {GET} from '../modules/fetch.ts';
import {Line as ChartLine} from 'vue-chartjs';
import {
startDaysBetween,
firstStartDateAfterDate,
fillEmptyStartDaysWithZeroes,
type DayData,
type DayDataObject,
} from '../utils/time.ts';
import {chartJsColors} from '../utils/color.ts';
import {sleep} from '../utils.ts';
import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm';
import {onMounted, ref} from 'vue';
const {pageData} = window.config;
Chart.defaults.color = chartJsColors.text;
Chart.defaults.borderColor = chartJsColors.border;
Chart.register(
TimeScale,
LinearScale,
Legend,
PointElement,
LineElement,
Filler,
);
defineProps<{
locale: {
loadingTitle: string;
loadingTitleFailed: string;
loadingInfo: string;
};
}>();
const isLoading = ref(false);
const errorText = ref('');
const repoLink = ref(pageData.repoLink || []);
const data = ref<DayData[]>([]);
onMounted(() => {
fetchGraphData();
});
async function fetchGraphData() {
isLoading.value = true;
try {
let response: Response;
do {
response = await GET(`${repoLink.value}/activity/code-frequency/data`);
if (response.status === 202) {
await sleep(1000); // wait for 1 second before retrying
}
} while (response.status === 202);
if (response.ok) {
const dayDataObject: DayDataObject = await response.json();
const weekValues = Object.values(dayDataObject);
const start = weekValues[0].week;
const end = firstStartDateAfterDate(new Date());
const startDays = startDaysBetween(start, end);
data.value = fillEmptyStartDaysWithZeroes(startDays, dayDataObject);
errorText.value = '';
} else {
errorText.value = response.statusText;
}
} catch (err) {
errorText.value = err.message;
} finally {
isLoading.value = false;
}
}
function toGraphData(data: Array<Record<string, any>>): ChartData<'line'> {
return {
datasets: [
{
data: data.map((i) => ({x: i.week, y: i.additions})),
pointRadius: 0,
pointHitRadius: 0,
fill: true,
label: 'Additions',
backgroundColor: chartJsColors['additions'],
borderWidth: 0,
tension: 0.3,
},
{
data: data.map((i) => ({x: i.week, y: -i.deletions})),
pointRadius: 0,
pointHitRadius: 0,
fill: true,
label: 'Deletions',
backgroundColor: chartJsColors['deletions'],
borderWidth: 0,
tension: 0.3,
},
],
};
}
const options: ChartOptions<'line'> = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: true,
},
},
scales: {
x: {
type: 'time',
grid: {
display: false,
},
time: {
minUnit: 'month',
},
ticks: {
maxRotation: 0,
maxTicksLimit: 12,
},
},
y: {
ticks: {
maxTicksLimit: 6,
},
},
},
};
</script>
<template>
<div>
<div class="ui header tw-flex tw-items-center tw-justify-between">
{{ isLoading ? locale.loadingTitle : errorText ? locale.loadingTitleFailed: `Code frequency over the history of ${repoLink.slice(1)}` }}
</div>
<div class="tw-flex ui segment main-graph">
<div v-if="isLoading || errorText !== ''" class="gt-tc tw-m-auto">
<div v-if="isLoading">
<SvgIcon name="octicon-sync" class="tw-mr-2 circular-spin"/>
{{ locale.loadingInfo }}
</div>
<div v-else class="text red">
<SvgIcon name="octicon-x-circle-fill"/>
{{ errorText }}
</div>
</div>
<ChartLine
v-memo="data" v-if="data.length !== 0"
:data="toGraphData(data)" :options="options"
/>
</div>
</div>
</template>
<style scoped>
.main-graph {
height: 440px;
}
</style>