Skip to content

Commit 1b25d8a

Browse files
authored
improve version download chart appearance, add dist tag labels (#2268)
1 parent 82bda25 commit 1b25d8a

3 files changed

Lines changed: 98 additions & 29 deletions

File tree

components/Package/VersionBox.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ export default function VersionBox({ label, time, versionData, downloads = 0 }:
2929
]}>
3030
<View>
3131
<View style={tw`min-h-4 flex-row items-center gap-1`}>
32-
{label && <Caption>{label}</Caption>}
32+
{label && <Caption style={tw`tabular-nums`}>{label}</Caption>}
3333
{label && <Caption style={tw`text-secondary`}>{` • `}</Caption>}
34-
<Caption style={label && tw`text-secondary`}>{versionData.version}</Caption>
34+
<Caption style={label && tw`tabular-nums text-secondary`}>{versionData.version}</Caption>
3535
</View>
3636
<View style={tw`min-h-4 flex-row items-center gap-1`}>
3737
<Label style={tw`font-light text-secondary`}>

components/Package/VersionDownloadsChart.tsx

Lines changed: 95 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ type ChartData = {
2020
downloads: number;
2121
publishedAt?: string;
2222
isAggregate?: boolean;
23+
distTags?: string[];
2324
};
2425

2526
const VERSIONS_LIMIT = 12;
2627
const OTHER_VERSION_LABEL = 'Other';
27-
const AXIS_LABEL_STYLE = {
28-
fontSize: 12,
28+
const DIST_TAG_LABEL_STYLE = {
2929
fontWeight: 400,
30-
letterSpacing: 0,
30+
fontSize: 10,
3131
};
3232

3333
export default function VersionDownloadsChart({
@@ -37,12 +37,13 @@ export default function VersionDownloadsChart({
3737
limit = VERSIONS_LIMIT,
3838
}: Props) {
3939
const isDark = tw.prefixMatch('dark');
40+
const versionDistTags = useMemo(() => mapVersionDistTags(registryData), [registryData]);
4041
const series = useMemo(
41-
() => buildChartSeries(npmDownloads, registryData, limit),
42-
[limit, npmDownloads, registryData]
42+
() => buildChartSeries(npmDownloads, registryData, versionDistTags, limit),
43+
[limit, npmDownloads, registryData, versionDistTags]
4344
);
4445

45-
const chartHeight = height ?? Math.max(120, series.length * 26 + 42);
46+
const chartHeight = height ?? Math.max(120, series.length * 27 + 42);
4647
const maxDownloads = Math.max(...series.map(item => item.downloads), 0);
4748
const xDomain = maxDownloads ? [0, maxDownloads + Math.max(1, maxDownloads * 0.05)] : undefined;
4849
const gridColor = isDark ? '#374151' : '#e5e7eb';
@@ -67,8 +68,8 @@ export default function VersionDownloadsChart({
6768
width={width}
6869
height={chartHeight}
6970
xScale={{ type: 'linear', domain: xDomain }}
70-
yScale={{ type: 'band', paddingInner: 0.3, paddingOuter: 0.3 }}
71-
margin={{ top: 6, right: 8, bottom: 20, left: 54 }}>
71+
yScale={{ type: 'band', paddingInner: 0.23, paddingOuter: 0.15 }}
72+
margin={{ top: 6, right: 2, bottom: 20, left: 60 }}>
7273
<Grid
7374
columns
7475
rows={false}
@@ -84,35 +85,83 @@ export default function VersionDownloadsChart({
8485
hideAxisLine
8586
hideTicks
8687
tickFormat={value => formatNumberToString(value)}
87-
tickLabelProps={() => ({
88-
fill: 'var(--secondary)',
89-
textAnchor: 'middle',
90-
...AXIS_LABEL_STYLE,
91-
})}
88+
tickComponent={({ formattedValue = 'unknown', x, y }) => {
89+
return (
90+
<text
91+
x={x}
92+
y={y}
93+
textAnchor="middle"
94+
dominantBaseline="middle"
95+
style={tw`select-none tabular-nums`}
96+
fill="var(--secondary)">
97+
<tspan x={x} style={tw`text-[12px] font-light`}>
98+
{formattedValue}
99+
</tspan>
100+
</text>
101+
);
102+
}}
92103
/>
93104
<Axis
94105
orientation="left"
95106
hideAxisLine
96107
hideTicks
97108
numTicks={series.length}
98-
tickLabelProps={() => ({
99-
fill: isDark ? 'var(--white)' : 'var(--black)',
100-
textAnchor: 'end',
101-
...AXIS_LABEL_STYLE,
102-
})}
109+
tickComponent={({ formattedValue = 'unknown', x, y }) => {
110+
const labelX = (x ?? 0) - 4;
111+
const data = series.find(item => item.version === formattedValue);
112+
const hasDistTags = Boolean(data?.distTags?.length);
113+
const [version, suffix] = formattedValue.split('-');
114+
115+
return (
116+
<text
117+
x={labelX}
118+
y={y}
119+
textAnchor="end"
120+
dominantBaseline="middle"
121+
style={tw`select-none tabular-nums`}
122+
fill={isDark ? 'var(--white)' : 'var(--black)'}>
123+
<tspan
124+
x={labelX}
125+
dy={hasDistTags || suffix ? '-0.44em' : '0'}
126+
style={tw`text-[12px]`}>
127+
{version}
128+
</tspan>
129+
{hasDistTags && (
130+
<tspan
131+
x={labelX}
132+
dy="1.2em"
133+
fill="var(--secondary)"
134+
style={DIST_TAG_LABEL_STYLE}>
135+
{data?.distTags?.join(', ')}
136+
</tspan>
137+
)}
138+
{suffix && !hasDistTags && (
139+
<tspan
140+
x={labelX}
141+
dy="1.2em"
142+
fill="var(--secondary)"
143+
style={DIST_TAG_LABEL_STYLE}>
144+
{suffix}
145+
</tspan>
146+
)}
147+
</text>
148+
);
149+
}}
103150
/>
104151
<BarSeries
105152
dataKey="downloads"
106153
data={series}
107154
xAccessor={item => item.downloads}
108155
yAccessor={item => item.version}
109-
colorAccessor={({ isAggregate }) =>
110-
isAggregate
111-
? 'var(--pewter)'
112-
: isDark
113-
? 'var(--primary-dark)'
114-
: 'var(--primary-darker)'
115-
}
156+
colorAccessor={({ isAggregate, distTags }) => {
157+
if (isAggregate) {
158+
return 'var(--pewter)';
159+
}
160+
if (distTags?.length) {
161+
return isDark ? 'var(--primary)' : 'var(--primary-dark)';
162+
}
163+
return isDark ? 'var(--primary-darker)' : 'var(--primary-darker)';
164+
}}
116165
radius={3}
117166
radiusAll
118167
/>
@@ -138,6 +187,7 @@ export default function VersionDownloadsChart({
138187
function buildChartSeries(
139188
npmDownloads: NpmPerVersionDownloads,
140189
registryData: NpmRegistryData,
190+
versionDistTags: Record<string, string[]>,
141191
limit: number
142192
): ChartData[] {
143193
const sortedVersions = Object.entries(npmDownloads.downloads)
@@ -151,6 +201,7 @@ function buildChartSeries(
151201
version,
152202
downloads,
153203
publishedAt: registryData.time[version],
204+
distTags: versionDistTags[version],
154205
}));
155206

156207
const normalizedLimit = Math.max(1, limit);
@@ -184,8 +235,15 @@ function renderTooltipContent(data?: ChartData) {
184235

185236
return (
186237
<Text
187-
style={tw`font-sans flex flex-col rounded bg-black px-2.5 py-1.5 text-xs font-light text-white dark:border dark:border-default`}>
188-
<span style={tw`font-medium`}>{data.version}</span>
238+
style={tw`font-sans flex flex-col gap-px rounded bg-black px-2.5 py-1.5 text-xs font-light text-white dark:border dark:border-default`}>
239+
<span style={tw`text-[14px] font-medium tabular-nums`}>
240+
{data.version}
241+
{data.distTags?.length && (
242+
<span style={tw`text-palette-gray3 dark:text-secondary`}>
243+
{` `}{data.distTags.join(', ')}
244+
</span>
245+
)}
246+
</span>
189247
<span>{data.downloads.toLocaleString()} downloads last week</span>
190248
{data.publishedAt && !data.isAggregate ? (
191249
<span style={tw`text-palette-gray3 dark:text-secondary`}>
@@ -195,3 +253,13 @@ function renderTooltipContent(data?: ChartData) {
195253
</Text>
196254
);
197255
}
256+
257+
function mapVersionDistTags(registryData: NpmRegistryData) {
258+
return Object.entries(registryData['dist-tags']).reduce<Record<string, string[]>>(
259+
(acc, [tag, version]) => {
260+
acc[version] = [...(acc[version] ?? []), tag];
261+
return acc;
262+
},
263+
{}
264+
);
265+
}

styles/styles.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979

8080
:root.dark {
8181
--primary-dark: #2e9ab8;
82+
--primary-darker: #23839e;
8283

8384
--powder: #262a36;
8485
--pewter: #767c8e;

0 commit comments

Comments
 (0)