Skip to content

Commit 926c1c4

Browse files
authored
feat: implement alt copy for versions chart (#1660)
1 parent 5a14acf commit 926c1c4

File tree

8 files changed

+142
-12
lines changed

8 files changed

+142
-12
lines changed

app/components/Package/VersionDistribution.vue

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@ import {
99
drawNpmxLogoAndTaglineWatermark,
1010
} from '~/composables/useChartWatermark'
1111
import TooltipApp from '~/components/Tooltip/App.vue'
12+
import { copyAltTextForVersionsBarChart } from '~/utils/charts'
1213
1314
const props = defineProps<{
1415
packageName: string
1516
inModal?: boolean
1617
}>()
1718
1819
const { accentColors, selectedAccentColor } = useAccentColor()
20+
const { copy, copied } = useClipboard()
21+
1922
const colorMode = useColorMode()
2023
const resolvedMode = shallowRef<'light' | 'dark'>('light')
2124
const rootEl = shallowRef<HTMLElement | null>(null)
@@ -190,14 +193,14 @@ const chartConfig = computed<VueUiXyConfig>(() => {
190193
fullscreen: false,
191194
table: false,
192195
tooltip: false,
193-
altCopy: false, // TODO: set to true to enable the alt copy feature
196+
altCopy: true,
194197
},
195198
buttonTitles: {
196199
csv: $t('package.trends.download_file', { fileType: 'CSV' }),
197200
img: $t('package.trends.download_file', { fileType: 'PNG' }),
198201
svg: $t('package.trends.download_file', { fileType: 'SVG' }),
199202
annotator: $t('package.trends.toggle_annotator'),
200-
altCopy: undefined, // TODO: set to proper translation key
203+
altCopy: $t('package.trends.copy_alt.button_label'), // Do not make this text dependant on the `copied` variable, since this would re-render the component, which is undesirable if the minimap was used to select a time frame.
201204
},
202205
callbacks: {
203206
img: args => {
@@ -230,10 +233,19 @@ const chartConfig = computed<VueUiXyConfig>(() => {
230233
loadFile(url, buildExportFilename('svg'))
231234
URL.revokeObjectURL(url)
232235
},
233-
// altCopy: ({ dataset: dst, config: cfg }: { dataset: Array<VueUiXyDatasetItem>; config: VueUiXyConfig}) => {
234-
// // TODO: implement a reusable copy-alt-text-to-clipboard feature based on the dataset & configuration
235-
// console.log({ dst, cfg})
236-
// }
236+
altCopy: ({ dataset: dst, config: cfg }) =>
237+
copyAltTextForVersionsBarChart({
238+
dataset: dst,
239+
config: {
240+
...cfg,
241+
datapointLabels: xAxisLabels.value,
242+
dateRangeLabel: dateRangeLabel.value,
243+
semverGroupingMode: groupingMode.value,
244+
copy,
245+
$t,
246+
numberFormatter: compactNumberFormatter.value.format,
247+
},
248+
}),
237249
},
238250
},
239251
grid: {
@@ -575,6 +587,16 @@ const chartConfig = computed<VueUiXyConfig>(() => {
575587
aria-hidden="true"
576588
/>
577589
</template>
590+
<template #optionAltCopy>
591+
<span
592+
class="w-6 h-6"
593+
:class="
594+
copied ? 'i-lucide:check text-accent' : 'i-lucide:person-standing text-fg-subtle'
595+
"
596+
style="pointer-events: none"
597+
aria-hidden="true"
598+
/>
599+
</template>
578600
</VueUiXy>
579601
</div>
580602

app/utils/charts.ts

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import type { AltCopyArgs, VueUiXyConfig, VueUiXyDatasetLineItem } from 'vue-data-ui'
1+
import type {
2+
AltCopyArgs,
3+
VueUiXyConfig,
4+
VueUiXyDatasetBarItem,
5+
VueUiXyDatasetLineItem,
6+
} from 'vue-data-ui'
27
import type { ChartTimeGranularity } from '~/types/chart'
38

49
export function sum(numbers: number[]): number {
@@ -413,6 +418,11 @@ export type TrendLineDataset = {
413418
[key: string]: unknown
414419
} | null
415420

421+
export type VersionsBarDataset = {
422+
bars: VueUiXyDatasetBarItem[]
423+
[key: string]: unknown
424+
} | null
425+
416426
export type TrendTranslateKey = number | 'package.trends.y_axis_label' | (string & {})
417427

418428
export type TrendTranslateFunction = {
@@ -431,6 +441,12 @@ export type TrendLineConfig = VueUiXyConfig & {
431441
numberFormatter: (value: number) => string
432442
}
433443

444+
export type VersionsBarConfig = Omit<
445+
TrendLineConfig,
446+
'formattedDates' | 'hasEstimation' | 'formattedDatasetValues' | 'granularity'
447+
> & { datapointLabels: string[]; dateRangeLabel: string; semverGroupingMode: string }
448+
449+
// Used for TrendsChart.vue
434450
export function createAltTextForTrendLineChart({
435451
dataset,
436452
config,
@@ -519,3 +535,63 @@ export async function copyAltTextForTrendLineChart({
519535
const altText = createAltTextForTrendLineChart({ dataset, config })
520536
await config.copy(altText)
521537
}
538+
539+
// Used for VersionDistribution.vue
540+
export function createAltTextForVersionsBarChart({
541+
dataset,
542+
config,
543+
}: AltCopyArgs<VersionsBarDataset, VersionsBarConfig>) {
544+
if (!dataset) return ''
545+
546+
const series = dataset.bars[0]?.series ?? []
547+
const versions = series.map((value, index) => ({
548+
index,
549+
name: config.datapointLabels[index] ?? '-',
550+
rawDownloads: value ?? 0,
551+
downloads: config.numberFormatter(value ?? 0),
552+
}))
553+
554+
const versionWithMaxDownloads =
555+
versions.length > 0
556+
? versions.reduce((max, current) => (current.rawDownloads > max.rawDownloads ? current : max))
557+
: undefined
558+
559+
const per_version_analysis = versions
560+
.toReversed()
561+
.filter(v => v.index !== versionWithMaxDownloads?.index)
562+
.map(v =>
563+
config.$t(`package.versions.copy_alt.per_version_analysis`, {
564+
version: v?.name ?? '-',
565+
downloads: v?.downloads ?? '-',
566+
}),
567+
)
568+
.join(', ')
569+
570+
const semver_grouping_mode =
571+
config.semverGroupingMode === 'major'
572+
? config.$t('package.versions.grouping_major')
573+
: config.$t('package.versions.grouping_minor')
574+
575+
const altText = `${config.$t('package.versions.copy_alt.general_description', {
576+
package_name: dataset?.bars[0]?.name ?? '-',
577+
versions_count: versions?.length,
578+
semver_grouping_mode: semver_grouping_mode.toLocaleLowerCase(),
579+
first_version: versions[0]?.name ?? '-',
580+
last_version: versions.at(-1)?.name ?? '-',
581+
date_range_label: config.dateRangeLabel ?? '-',
582+
max_downloaded_version: versionWithMaxDownloads?.name ?? '-',
583+
max_version_downloads: versionWithMaxDownloads?.downloads ?? '-',
584+
per_version_analysis,
585+
watermark: config.$t('package.trends.copy_alt.watermark'),
586+
})}`
587+
588+
return altText
589+
}
590+
591+
export async function copyAltTextForVersionsBarChart({
592+
dataset,
593+
config,
594+
}: AltCopyArgs<VersionsBarDataset, VersionsBarConfig>) {
595+
const altText = createAltTextForVersionsBarChart({ dataset, config })
596+
await config.copy(altText)
597+
}

i18n/locales/en.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,11 @@
312312
"filter_help": "Semver range filter help",
313313
"filter_tooltip": "Filter versions using a {link}. For example, ^3.0.0 shows all 3.x versions.",
314314
"filter_tooltip_link": "semver range",
315-
"no_matches": "No versions match this range"
315+
"no_matches": "No versions match this range",
316+
"copy_alt": {
317+
"per_version_analysis": "{version} version was downloaded {downloads} times",
318+
"general_description": "Bar chart showing per-version downloads for {versions_count} {semver_grouping_mode} versions of the {package_name} package, {date_range_label} from the {first_version} version to the {last_version} version. The most downloaded version is {max_downloaded_version} with {max_version_downloads} downloads. {per_version_analysis} {watermark}."
319+
}
316320
},
317321
"dependencies": {
318322
"title": "Dependency ({count}) | Dependencies ({count})",

i18n/locales/fr-FR.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,11 @@
312312
"filter_help": "Infos sur le filtre de plage semver",
313313
"filter_tooltip": "Filtrer les versions avec une {link}. Par exemple, ^3.0.0 affiche toutes les versions 3.x.",
314314
"filter_tooltip_link": "plage semver",
315-
"no_matches": "Aucune version ne correspond à cette plage"
315+
"no_matches": "Aucune version ne correspond à cette plage",
316+
"copy_alt": {
317+
"per_version_analysis": "La version {version} a été téléchargée {downloads} fois",
318+
"general_description": "Graphique en barres montrant les téléchargements par version pour {versions_count} versions {semver_grouping_mode} du paquet {package_name}, {date_range_label} de la version {first_version} à la version {last_version}. La version la plus téléchargée est {max_downloaded_version} avec {max_version_downloads} téléchargements. {per_version_analysis} {watermark}."
319+
}
316320
},
317321
"dependencies": {
318322
"title": "Dépendances ({count})",

i18n/schema.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,18 @@
942942
},
943943
"no_matches": {
944944
"type": "string"
945+
},
946+
"copy_alt": {
947+
"type": "object",
948+
"properties": {
949+
"per_version_analysis": {
950+
"type": "string"
951+
},
952+
"general_description": {
953+
"type": "string"
954+
}
955+
},
956+
"additionalProperties": false
945957
}
946958
},
947959
"additionalProperties": false

lunaria/files/en-GB.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,11 @@
311311
"filter_help": "Semver range filter help",
312312
"filter_tooltip": "Filter versions using a {link}. For example, ^3.0.0 shows all 3.x versions.",
313313
"filter_tooltip_link": "semver range",
314-
"no_matches": "No versions match this range"
314+
"no_matches": "No versions match this range",
315+
"copy_alt": {
316+
"per_version_analysis": "{version} version was downloaded {downloads} times",
317+
"general_description": "Bar chart showing per-version downloads for {versions_count} {semver_grouping_mode} versions of the {package_name} package, {date_range_label} from the {first_version} version to the {last_version} version. The most downloaded version is {max_downloaded_version} with {max_version_downloads} downloads. {per_version_analysis} {watermark}."
318+
}
315319
},
316320
"dependencies": {
317321
"title": "Dependency ({count}) | Dependencies ({count})",

lunaria/files/en-US.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,11 @@
311311
"filter_help": "Semver range filter help",
312312
"filter_tooltip": "Filter versions using a {link}. For example, ^3.0.0 shows all 3.x versions.",
313313
"filter_tooltip_link": "semver range",
314-
"no_matches": "No versions match this range"
314+
"no_matches": "No versions match this range",
315+
"copy_alt": {
316+
"per_version_analysis": "{version} version was downloaded {downloads} times",
317+
"general_description": "Bar chart showing per-version downloads for {versions_count} {semver_grouping_mode} versions of the {package_name} package, {date_range_label} from the {first_version} version to the {last_version} version. The most downloaded version is {max_downloaded_version} with {max_version_downloads} downloads. {per_version_analysis} {watermark}."
318+
}
315319
},
316320
"dependencies": {
317321
"title": "Dependency ({count}) | Dependencies ({count})",

lunaria/files/fr-FR.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,11 @@
311311
"filter_help": "Infos sur le filtre de plage semver",
312312
"filter_tooltip": "Filtrer les versions avec une {link}. Par exemple, ^3.0.0 affiche toutes les versions 3.x.",
313313
"filter_tooltip_link": "plage semver",
314-
"no_matches": "Aucune version ne correspond à cette plage"
314+
"no_matches": "Aucune version ne correspond à cette plage",
315+
"copy_alt": {
316+
"per_version_analysis": "La version {version} a été téléchargée {downloads} fois",
317+
"general_description": "Graphique en barres montrant les téléchargements par version pour {versions_count} versions {semver_grouping_mode} du paquet {package_name}, {date_range_label} de la version {first_version} à la version {last_version}. La version la plus téléchargée est {max_downloaded_version} avec {max_version_downloads} téléchargements. {per_version_analysis} {watermark}."
318+
}
315319
},
316320
"dependencies": {
317321
"title": "Dépendances ({count})",

0 commit comments

Comments
 (0)