diff --git a/src/components/npm-stats/BaselineSection.tsx b/src/components/npm-stats/BaselineSection.tsx
index 14b8bb34a..499557566 100644
--- a/src/components/npm-stats/BaselineSection.tsx
+++ b/src/components/npm-stats/BaselineSection.tsx
@@ -9,7 +9,7 @@ import {
import { twMerge } from 'tailwind-merge'
import { Tooltip } from '~/components/Tooltip'
import { PackageSearch } from './PackageSearch'
-import type { PackageGroup } from './shared'
+import { getBaselineDisplayName, type PackageGroup } from './shared'
import type { BaselinePreset } from '~/routes/stats/npm/-comparisons'
export const BASELINE_LINE_COLOR = '#3b82f6'
@@ -39,9 +39,7 @@ export function BaselineSection({
}: BaselineSectionProps) {
const baselineGroups = packageGroups.filter((pg) => pg.baseline)
const [showSearch, setShowSearch] = React.useState(false)
- const names = baselineGroups
- .map((pg) => pg.packages[0]?.name)
- .filter((n): n is string => !!n)
+ const baselineDisplayName = getBaselineDisplayName(baselineGroups)
const hasBaselines = baselineGroups.length > 0
const normalizeActive = hasBaselines && normalizeBaseline
@@ -100,7 +98,7 @@ export function BaselineSection({
- {names.join(', ')}
+ {baselineDisplayName}
diff --git a/src/components/npm-stats/NPMStatsChart.tsx b/src/components/npm-stats/NPMStatsChart.tsx
index 3045d76d4..c11b037ac 100644
--- a/src/components/npm-stats/NPMStatsChart.tsx
+++ b/src/components/npm-stats/NPMStatsChart.tsx
@@ -12,10 +12,10 @@ import type {
TimeRange,
TransformMode,
} from './shared'
-import { getPackageColor } from './shared'
+import { getBaselineDisplayName, getPackageColor } from './shared'
import { BASELINE_LINE_COLOR } from './BaselineSection'
-const BASELINE_LINE_NAME = '__baseline__'
+const BASELINE_LINE_SERIES = '__baseline__'
// Plot figure component
function PlotFigure({ options }: { options: Parameters[0] }) {
@@ -169,6 +169,9 @@ export function NPMStatsChart({
const baselineGroups = binnedPackageData.filter(
(_, index) => packages[index]?.baseline,
)
+ const baselineLineName = getBaselineDisplayName(
+ packages.filter((packageGroup) => packageGroup.baseline),
+ )
const isMultiBaseline = baselineGroups.length > 1
const baselineDivisorByDate = baselineGroups.length
@@ -243,13 +246,19 @@ export function NPMStatsChart({
return !isHidden
})
- const plotData = filteredPackageData.flatMap((d) => d.downloads)
+ const plotData = filteredPackageData.flatMap((d) =>
+ d.downloads.map((download) => ({
+ ...download,
+ seriesName: download.name,
+ })),
+ )
if (showBaseline && baselineDivisorByDate) {
const baselinePoints = [...baselineDivisorByDate.entries()]
.sort((a, b) => a[0] - b[0])
.map(([time, divisor]) => ({
- name: BASELINE_LINE_NAME,
+ name: baselineLineName,
+ seriesName: BASELINE_LINE_SERIES,
date: d3.utcDay(new Date(time)),
// When normalized, baseline / baseline = 1 at every point.
// When raw, multi-baseline shows the growth-index multiplier (~1.0+);
@@ -271,6 +280,14 @@ export function NPMStatsChart({
fx: facetX,
fy: facetY,
} as const
+ const lineOptions: Plot.LineYOptions = {
+ ...baseOptions,
+ z: 'seriesName',
+ }
+ const getStrokeColor = (d: { name?: string; seriesName?: string }) => {
+ if (d.seriesName === BASELINE_LINE_SERIES) return BASELINE_LINE_COLOR
+ return d.name ? getPackageColor(d.name, packages) : 'currentColor'
+ }
const partialBinEnd = binUnit.floor(now)
const partialBinStart = binUnit.offset(partialBinEnd, -1)
@@ -305,8 +322,8 @@ export function NPMStatsChart({
? Plot.lineY(
plotData.filter((d) => d.date >= partialBinStart),
{
- ...baseOptions,
- stroke: 'name',
+ ...lineOptions,
+ stroke: getStrokeColor,
strokeWidth: 1.5,
strokeDasharray: '2 4',
strokeOpacity: 0.8,
@@ -317,8 +334,8 @@ export function NPMStatsChart({
Plot.lineY(
plotData.filter((d) => d.date < partialBinEnd),
{
- ...baseOptions,
- stroke: 'name',
+ ...lineOptions,
+ stroke: getStrokeColor,
strokeWidth: 2,
curve: 'monotone-x',
},
@@ -363,7 +380,7 @@ export function NPMStatsChart({
range: [...new Set(plotData.map((d) => d.name))]
.filter((pkg): pkg is string => pkg !== undefined)
.map((pkg) =>
- pkg === BASELINE_LINE_NAME
+ pkg === baselineLineName
? BASELINE_LINE_COLOR
: getPackageColor(pkg, packages),
),
diff --git a/src/components/npm-stats/shared.ts b/src/components/npm-stats/shared.ts
index 47746832e..14cac368a 100644
--- a/src/components/npm-stats/shared.ts
+++ b/src/components/npm-stats/shared.ts
@@ -107,6 +107,20 @@ export function getPackageColor(
return defaultColors[packageIndex % defaultColors.length]
}
+export function getBaselineDisplayName(packageGroups: PackageGroup[]): string {
+ const baselineNames = packageGroups
+ .filter((packageGroup) => packageGroup.baseline)
+ .flatMap((packageGroup) => {
+ const label = packageGroup.baselineLabel?.trim()
+ if (label) return [label]
+
+ const packageNames = packageGroup.packages.map((pkg) => pkg.name)
+ return packageNames.length ? [packageNames.join(', ')] : []
+ })
+
+ return [...new Set(baselineNames)].join(', ') || 'Baseline'
+}
+
export const formatNumber = (num: number) => {
if (num >= 1_000_000) {
return `${(num / 1_000_000).toFixed(1)}M`
diff --git a/src/routes/$libraryId/$version.docs.npm-stats.tsx b/src/routes/$libraryId/$version.docs.npm-stats.tsx
index 49963aa28..95c6053eb 100644
--- a/src/routes/$libraryId/$version.docs.npm-stats.tsx
+++ b/src/routes/$libraryId/$version.docs.npm-stats.tsx
@@ -294,6 +294,7 @@ function RouteComponent() {
packages: [{ name: p.name, hidden: true }],
color: p.color,
baseline: true,
+ baselineLabel: preset.title,
})),
],
}
diff --git a/src/routes/stats/npm/-comparisons.ts b/src/routes/stats/npm/-comparisons.ts
index cf874d534..dd3a3862e 100644
--- a/src/routes/stats/npm/-comparisons.ts
+++ b/src/routes/stats/npm/-comparisons.ts
@@ -9,6 +9,7 @@ export const packageGroupSchema = v.object({
),
color: v.optional(v.nullable(v.string())),
baseline: v.optional(v.boolean()),
+ baselineLabel: v.optional(v.string()),
})
export const packageComparisonSchema = v.object({
diff --git a/src/routes/stats/npm/index.tsx b/src/routes/stats/npm/index.tsx
index 97f569427..826c37251 100644
--- a/src/routes/stats/npm/index.tsx
+++ b/src/routes/stats/npm/index.tsx
@@ -216,6 +216,7 @@ type _NpmStatsSearch = {
name?: string
color?: string
baseline?: boolean
+ baselineLabel?: string
packages: Array<{ name?: string; hidden?: boolean }>
}>
range?: TimeRange
@@ -308,6 +309,7 @@ function RouteComponent() {
packages: [{ name: p.name, hidden: true }],
color: p.color,
baseline: true,
+ baselineLabel: preset.title,
})),
],
}