diff --git a/components/Package/Charts/VersionDownloadsChart.tsx b/components/Package/Charts/VersionDownloadsChart.tsx index 4d1f86a0e..df0f6c7e8 100644 --- a/components/Package/Charts/VersionDownloadsChart.tsx +++ b/components/Package/Charts/VersionDownloadsChart.tsx @@ -6,14 +6,14 @@ import { useRouter } from 'next/router'; import { useState } from 'react'; import { View } from 'react-native'; -import { Label } from '~/common/styleguide'; -import ChartTooltip from '~/components/Package/Charts/ChartTooltip'; -import HoveredBarOutline from '~/components/Package/Charts/HoveredBarOutline'; +import { Label, useLayout } from '~/common/styleguide'; import { type NpmPerVersionDownloads, type PackageVersionsData } from '~/types'; import { replaceQueryParam } from '~/util/queryParams'; import { NUMBER_FORMATTER, pluralize } from '~/util/strings'; import tw from '~/util/tailwind'; +import ChartTooltip from './ChartTooltip'; +import HoveredBarOutline from './HoveredBarOutline'; import { type VersionsAggregatedChartMode, type VersionsChartData, @@ -28,6 +28,7 @@ import { DEFAULT_CHART_MODE, getChartLeftMargin, getLargestSeriesLength, + getLatestVersionDownloadsPercentage, getPrimaryChartLabel, LABEL_TO_BAR_GAP, mapVersionDistTags, @@ -46,6 +47,7 @@ const OTHER_BAR_GRADIENT_ID = 'version-downloads-chart-other'; export default function VersionDownloadsChart({ npmDownloads, registryData }: Props) { const isDark = tw.prefixMatch('dark'); + const { isSmallScreen } = useLayout(); const router = useRouter(); const routeMode = parseChartMode(router.query[CHART_MODE_QUERY_PARAM]); @@ -59,6 +61,7 @@ export default function VersionDownloadsChart({ npmDownloads, registryData }: Pr const series = chartSeriesByMode[mode]; const seriesByLabel = keyBy(series, item => item.label); const leftMargin = getChartLeftMargin(series); + const downloadsPercentage = getLatestVersionDownloadsPercentage(baseSeries, registryData, mode); if (!series.length) { return ( @@ -88,13 +91,17 @@ export default function VersionDownloadsChart({ npmDownloads, registryData }: Pr return ( // @ts-expect-error Ref type miss-match - + + margin={{ top: 2, right: 8, bottom: 20, left: leftMargin }}> NUMBER_FORMATTER.format(value)} diff --git a/components/Package/Charts/VersionDownloadsChartModes.tsx b/components/Package/Charts/VersionDownloadsChartModes.tsx index 2595876d7..5d6fe004a 100644 --- a/components/Package/Charts/VersionDownloadsChartModes.tsx +++ b/components/Package/Charts/VersionDownloadsChartModes.tsx @@ -9,6 +9,7 @@ import { type VersionsChartMode } from './types'; type Props = { mode: VersionsChartMode; onModeChange: (mode: VersionsChartMode) => void; + downloadsPercentage: number; }; const CHART_MODES: { key: VersionsChartMode; label: string; shortLabel: string }[] = [ @@ -17,27 +18,42 @@ const CHART_MODES: { key: VersionsChartMode; label: string; shortLabel: string } { key: 'major', label: 'By major', shortLabel: 'Major' }, ]; -export default function VersionDownloadsChartModes({ mode, onModeChange }: Props) { +export default function VersionDownloadsChartModes({ + mode, + onModeChange, + downloadsPercentage, +}: Props) { const { isSmallScreen } = useLayout(); return ( - - {CHART_MODES.map(({ key, label, shortLabel }) => ( - - ))} + + + {CHART_MODES.map(({ key, label, shortLabel }) => ( + + ))} + + ); } diff --git a/components/Package/Charts/utils.ts b/components/Package/Charts/utils.ts index 74ee29eac..1ccc497d1 100644 --- a/components/Package/Charts/utils.ts +++ b/components/Package/Charts/utils.ts @@ -17,7 +17,7 @@ export const DEFAULT_CHART_MODE: VersionsChartMode = 'version'; export const LABEL_TO_BAR_GAP = 6; const VERSIONS_LIMIT = 12; -const MIN_YAXIS_LABEL_WIDTH = 72; +const MIN_YAXIS_LABEL_WIDTH = 60; const OTHER_VERSION_LABEL = 'Other'; const TEXT_MEASURE_CANVAS_ID = 'measure-canvas'; const Y_AXIS_LABEL_WIDTH_CACHE = new Map(); @@ -60,6 +60,46 @@ export function buildChartSeriesByMode(baseSeries: VersionsChartData[]): Version }; } +export function getLatestVersionDownloadsPercentage( + baseSeries: VersionsChartData[], + registryData: PackageVersionsData, + mode: VersionsChartMode +) { + const totalDownloads = sumBy(baseSeries, item => item.downloads); + + if (!totalDownloads) { + return 0; + } + + const latestVersion = registryData['dist-tags'].latest; + + if (!latestVersion) { + return 0; + } + + if (mode === 'version') { + return ( + ((baseSeries.find(item => item.label === latestVersion)?.downloads ?? 0) / totalDownloads) * + 100 + ); + } + + const latestAggregateLabel = getAggregatedSemverLabel(latestVersion, mode); + + if (!latestAggregateLabel) { + return ( + ((baseSeries.find(item => item.label === latestVersion)?.downloads ?? 0) / totalDownloads) * + 100 + ); + } + + const latestAggregateDownloads = sumBy(baseSeries, item => + getAggregatedSemverLabel(item.label, mode) === latestAggregateLabel ? item.downloads : 0 + ); + + return (latestAggregateDownloads / totalDownloads) * 100; +} + export function createVersionChartEntry( version: string, downloads = 0,