diff --git a/app/components/CopyToClipboardButton.vue b/app/components/CopyToClipboardButton.vue new file mode 100644 index 000000000..4e4b21557 --- /dev/null +++ b/app/components/CopyToClipboardButton.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/app/pages/compare.vue b/app/pages/compare.vue index ac2fbcf60..415ab1859 100644 --- a/app/pages/compare.vue +++ b/app/pages/compare.vue @@ -6,8 +6,10 @@ definePageMeta({ name: 'compare', }) +const { locale } = useI18n() const router = useRouter() const canGoBack = useCanGoBack() +const { copied, copy } = useClipboard({ copiedDuring: 2000 }) // Sync packages with URL query param (stable ref - doesn't change on other query changes) const packagesParam = useRouteQuery('packages', '', { mode: 'replace' }) @@ -79,6 +81,57 @@ const gridHeaders = computed(() => gridColumns.value.map(col => (col.version ? `${col.name}@${col.version}` : col.name)), ) +/* + * Convert the comparison grid data to a Markdown table. + */ +function exportComparisonDataAsMarkdown() { + const mdData: Array> = [] + const headers = [ + '', + ...gridHeaders.value, + ...(showNoDependency.value ? [$t('compare.no_dependency.label')] : []), + ] + mdData.push(headers) + const maxLengths = headers.map(item => item.length) + + selectedFacets.value.forEach((facet, index) => { + const label = facet.label + const data = getFacetValues(facet.id) + mdData.push([ + label, + ...data.map(item => + item?.type === 'date' + ? new Date(item.display).toLocaleDateString(locale.value, { + year: 'numeric', + month: 'short', + day: 'numeric', + }) + : item?.display || '', + ), + ]) + mdData?.[index + 1]?.forEach((item, itemIndex) => { + if (item.length > (maxLengths?.[itemIndex] || 0)) { + maxLengths[itemIndex] = item.length + } + }) + }) + + const markdown = mdData.reduce((result, row, index) => { + // replacing pipe `|` with `ǀ` (U+01C0 Latin Letter Dental Click) to avoid breaking tables + result += `| ${row + .map((el, ind) => el.padEnd(maxLengths[ind] || 0, ' ').replace(/\|/g, 'ǀ')) + .join(' | ')} |` + if (index === 0) { + result += `\n|` + maxLengths.forEach(len => (result += ` ${'-'.padEnd(len, '-')} |`)) + } + result += `\n` + return result + }, '') + + copy(markdown) +} + useSeoMeta({ title: () => packages.value.length > 0 @@ -193,7 +246,24 @@ useSeoMeta({
-

+ +

+ {{ $t('compare.packages.section_comparison') }} +

+
+ +

{{ $t('compare.packages.section_comparison') }}

@@ -241,7 +311,7 @@ useSeoMeta({

{{ $t('compare.facets.trends.title') }} diff --git a/app/pages/package/[[org]]/[name].vue b/app/pages/package/[[org]]/[name].vue index de6c114d9..4fa808f5d 100644 --- a/app/pages/package/[[org]]/[name].vue +++ b/app/pages/package/[[org]]/[name].vue @@ -670,7 +670,12 @@ const showSkeleton = shallowRef(false) >
-
+

+
- - -
- -