Skip to content

Commit c3671ab

Browse files
committed
refactor: create downloadFile and downloadFileLink utils
I've seen this code used all over the place and so this is a good chance to make a util for it proper - I'll leave the chart stuff though for this PR as it needs more involved testing that I don't have time for tonight
1 parent a97cf16 commit c3671ab

File tree

6 files changed

+48
-29
lines changed

6 files changed

+48
-29
lines changed

app/components/Brand/Customize.vue

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import { ACCENT_COLORS, type AccentColorId } from '#shared/utils/constants'
33
44
const { selectedAccentColor } = useAccentColor()
5-
const { download: downloadBlob } = useSvgToPng()
65
const { t } = useI18n()
76
87
const customAccent = ref<string | null>(null)
@@ -58,13 +57,9 @@ function getCustomSvgString(): string {
5857
function downloadCustomSvg() {
5958
const svg = getCustomSvgString()
6059
if (!svg) return
60+
6161
const blob = new Blob([svg], { type: 'image/svg+xml' })
62-
const url = URL.createObjectURL(blob)
63-
const a = document.createElement('a')
64-
a.href = url
65-
a.download = `npmx-logo-${activeAccentId.value}.svg`
66-
a.click()
67-
URL.revokeObjectURL(url)
62+
downloadFile(blob, `npmx-logo-${activeAccentId.value}.svg`)
6863
}
6964
7065
const pngLoading = ref(false)
@@ -73,10 +68,12 @@ async function downloadCustomPng() {
7368
const svg = getCustomSvgString()
7469
if (!svg) return
7570
pngLoading.value = true
71+
72+
const blob = new Blob([svg], { type: 'image/svg+xml' })
73+
const url = URL.createObjectURL(blob)
74+
7675
try {
7776
await document.fonts.ready
78-
const blob = new Blob([svg], { type: 'image/svg+xml' })
79-
const url = URL.createObjectURL(blob)
8077
8178
const img = new Image()
8279
const loaded = new Promise<void>((resolve, reject) => {
@@ -100,12 +97,12 @@ async function downloadCustomPng() {
10097
10198
await new Promise<void>(resolve => {
10299
canvas.toBlob(pngBlob => {
103-
if (pngBlob) downloadBlob(pngBlob, `npmx-logo-${activeAccentId.value}.png`)
104-
URL.revokeObjectURL(url)
100+
if (pngBlob) downloadFile(pngBlob, `npmx-logo-${activeAccentId.value}.png`)
105101
resolve()
106102
}, 'image/png')
107103
})
108104
} finally {
105+
URL.revokeObjectURL(url)
109106
pngLoading.value = false
110107
}
111108
}

app/components/Package/DownloadButton.vue

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ async function downloadPackage() {
3232
3333
const downloadUrl = await getDownloadUrl(tarballUrl)
3434
35-
const link = document.createElement('a')
36-
link.href = downloadUrl ?? tarballUrl
37-
link.download = `${props.packageName.replace(/\//g, '__')}-${props.version.version}.tgz`
38-
document.body.appendChild(link)
39-
link.click()
40-
document.body.removeChild(link)
35+
downloadFileLink(
36+
downloadUrl ?? tarballUrl,
37+
`${props.packageName.replace(/\//g, '__')}-${props.version.version}.tgz`,
38+
)
4139
4240
if (downloadUrl) {
41+
// We're creating the object url in getDownloadUrl,
42+
// but need to clean it up here
4343
URL.revokeObjectURL(downloadUrl)
4444
}
4545

app/composables/useSvgToPng.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,5 @@ export function useSvgToPng() {
3131
})
3232
}
3333

34-
function download(blob: Blob, filename: string) {
35-
const url = URL.createObjectURL(blob)
36-
const a = document.createElement('a')
37-
a.href = url
38-
a.download = filename
39-
a.click()
40-
URL.revokeObjectURL(url)
41-
}
42-
43-
return { convert, download }
34+
return { convert }
4435
}

app/pages/brand.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ defineOgImageComponent('Default', {
1414
description: $t('brand.meta_description'),
1515
})
1616
17-
const { convert, download: downloadPng } = useSvgToPng()
17+
const { convert } = useSvgToPng()
1818
1919
const logos = [
2020
{
@@ -47,7 +47,7 @@ async function handlePngDownload(logo: (typeof logos)[number]) {
4747
try {
4848
const blob = await convert(logo.src, logo.width, logo.height)
4949
const filename = logo.src.replace(/^\//, '').replace('.svg', '.png')
50-
downloadPng(blob, filename)
50+
downloadFile(blob, filename)
5151
} finally {
5252
pngLoading.value.delete(logo.src)
5353
}

app/utils/charts.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,7 @@ export async function copyAltTextForCompareFacetBarChart({
637637
}
638638

639639
// Used in chart context menu callbacks
640+
// @todo replace with downloadFileLink
640641
export function loadFile(link: string, filename: string) {
641642
const a = document.createElement('a')
642643
a.href = link

app/utils/download.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Prompts the user to download the file that is at the given link, using
3+
* the given default filename.
4+
*
5+
* Note: some browsers may ignore the `download` attribute for cross-origin URLs.
6+
*
7+
* @param link - The URL of the resource to download. This can be a normal URL,
8+
* a data URL (e.g. "data:..."), or an object URL created via
9+
* `URL.createObjectURL`.
10+
* @param filename - Suggested filename for the downloaded file. The browser may
11+
* override this value depending on user settings or the URL.
12+
*/
13+
export function downloadFileLink(link: string, filename: string) {
14+
const a = document.createElement('a')
15+
a.href = link
16+
a.download = filename
17+
a.click()
18+
}
19+
20+
/**
21+
* Prompt the user to download the provided Blob as a file with the given filename.
22+
*
23+
* @param blob - The Blob to download.
24+
* @param filename - The filename that will be suggested to the user when saving.
25+
*/
26+
export function downloadFile(blob: Blob, filename: string) {
27+
const url = URL.createObjectURL(blob)
28+
downloadFileLink(url, filename)
29+
URL.revokeObjectURL(url)
30+
}

0 commit comments

Comments
 (0)