diff --git a/app/components/Package/DownloadAnalytics.vue b/app/components/Package/DownloadAnalytics.vue index 5856a4794..eee26a2b6 100644 --- a/app/components/Package/DownloadAnalytics.vue +++ b/app/components/Package/DownloadAnalytics.vue @@ -55,7 +55,16 @@ onMounted(async () => { }) const { colors } = useCssVariables( - ['--bg', '--fg', '--bg-subtle', '--bg-elevated', '--fg-subtle', '--border', '--border-subtle'], + [ + '--bg', + '--fg', + '--bg-subtle', + '--bg-elevated', + '--fg-subtle', + '--fg-muted', + '--border', + '--border-subtle', + ], { element: rootEl, watchHtmlAttributes: true, @@ -305,13 +314,6 @@ const effectivePackageNames = computed(() => { return single ? [single] : [] }) -const xAxisLabel = computed(() => { - if (!isMultiPackageMode.value) return props.packageName ?? '' - const names = effectivePackageNames.value - if (names.length === 1) return names[0] - return 'packages' -}) - const selectedGranularity = shallowRef('weekly') const displayedGranularity = shallowRef('weekly') @@ -784,6 +786,10 @@ const chartData = computed<{ dataset: VueUiXyDatasetItem[] | null; dates: number return { dataset, dates } }) +const maxDatapoints = computed(() => + Math.max(0, ...(chartData.value.dataset ?? []).map(d => d.series.length)), +) + /** * Maximum estimated value across all series when the chart is * displaying a partially completed time bucket (monthly or yearly). @@ -1310,6 +1316,104 @@ function drawLastDatapointLabel(svg: Record) { return dataLabels.join('\n') } +/** + * Build and return a legend to be injected during the SVG export only, since the custom legend is + * displayed as an independant div, content has to be injected within the chart's viewBox. + * + * Legend items are displayed in a column, on the top left of the chart. + */ +function drawSvgPrintLegend(svg: Record) { + const data = Array.isArray(svg?.data) ? svg.data : [] + if (!data.length) return '' + + const seriesNames: string[] = [] + + data.forEach((serie, index) => { + seriesNames.push(` + + + ${serie.name} + + `) + }) + + // Inject the estimation legend item when necessary + if ( + ['monthly', 'yearly'].includes(displayedGranularity.value) && + !isEndDateOnPeriodEnd.value && + !isZoomed.value + ) { + seriesNames.push(` + + + ${$t('package.trends.legend_estimation')} + + `) + } + + return seriesNames.join('\n') +} + +/** + * Build and return npmx svg logo and tagline, to be injected during PNG & SVG exports + */ +function drawNpmxLogoAndTaglineWatermark(svg: Record) { + if (!svg?.drawingArea) return '' + const npmxLogoWidthToHeight = 2.64 + const npmxLogoWidth = 100 + const npmxLogoHeight = npmxLogoWidth / npmxLogoWidthToHeight + + return ` + + + + + ${$t('tagline')} + + ` +} + // VueUiXy chart component configuration const chartConfig = computed(() => { return { @@ -1317,7 +1421,7 @@ const chartConfig = computed(() => { chart: { height: isMobile.value ? 950 : 600, backgroundColor: colors.value.bg, - padding: { bottom: 36, right: 100 }, // padding right is set to leave space of last datapoint label(s) + padding: { bottom: displayedGranularity.value === 'yearly' ? 84 : 64, right: 100 }, // padding right is set to leave space of last datapoint label(s) userOptions: { buttons: { pdf: false, labels: false, fullscreen: false, table: false, tooltip: false }, buttonTitles: { @@ -1356,6 +1460,7 @@ const chartConfig = computed(() => { }, grid: { stroke: colors.value.border, + showHorizontalLines: true, labels: { fontSize: isMobile.value ? 24 : 16, color: pending.value ? colors.value.border : colors.value.fgSubtle, @@ -1364,12 +1469,13 @@ const chartConfig = computed(() => { granularity: getGranularityLabel(selectedGranularity.value), facet: $t('package.trends.items.downloads'), }), - xLabel: isMultiPackageMode.value ? '' : xAxisLabel.value, // for multiple series, names are displayed in the chart's legend yLabelOffsetX: 12, fontSize: isMobile.value ? 32 : 24, }, xAxisLabels: { - show: false, + show: true, + showOnlyAtModulo: true, + modulo: 12, values: chartData.value?.dates, datetimeFormatter: { enable: true, @@ -1570,7 +1676,7 @@ const chartConfig = computed(() => {
-
+
{ v-html="drawLastDatapointLabel(svg)" /> + + + + + + @@ -1623,10 +1738,7 @@ const chartConfig = computed(() => { -