Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 55 additions & 50 deletions packages/gitbook/src/components/DocumentView/File.tsx
Original file line number Diff line number Diff line change
@@ -1,78 +1,83 @@
import { type DocumentBlockFile, SiteInsightsLinkPosition } from '@gitbook/api';

import { t } from '@/intl/translate';
import { getSimplifiedContentType } from '@/lib/files';
import { resolveContentRef } from '@/lib/references';
import { tcls } from '@/lib/tailwind';

import { Link } from '../primitives';
import { getSpaceLanguage } from '@/intl/server';
import { Button, Link } from '../primitives';
import { DownloadButton } from '../primitives/DownloadButton';
import type { BlockProps } from './Block';
import { Caption } from './Caption';
import { FileIcon } from './FileIcon';

export async function File(props: BlockProps<DocumentBlockFile>) {
const { block, context } = props;

const contentRef = context.contentContext
? await resolveContentRef(block.data.ref, context.contentContext)
: null;
if (!context.contentContext) {
return null;
}

const contentRef = await resolveContentRef(block.data.ref, context.contentContext);
const file = contentRef?.file;

if (!file) {
return null;
}

const language = getSpaceLanguage(context.contentContext);
const contentType = getSimplifiedContentType(file.contentType);
const insights = {
type: 'link_click' as const,
link: {
target: block.data.ref,
position: SiteInsightsLinkPosition.Content,
},
};

return (
<Caption {...props} withBorder>
<Link
href={file.downloadURL}
download={file.name}
insights={{
type: 'link_click',
link: {
target: block.data.ref,
position: SiteInsightsLinkPosition.Content,
},
}}
className={tcls('group/file', 'flex', 'flex-row', 'items-center', 'px-5', 'py-3')}
>
<div
className={tcls(
'min-w-14',
'mr-5',
'pr-5',
'flex',
'flex-col',
'items-center',
'gap-1',
'border-r',
'border-tint-subtle'
)}
>
<div>
<FileIcon
contentType={contentType}
className={tcls('size-5', 'text-primary')}
/>
</div>
<div
className={tcls(
'text-xs',
'text-tint',
'group-hover/file:text-tint-strong'
)}
>
{getHumanFileSize(file.size)}
</div>
<div className="flex flex-wrap items-center gap-5 px-5 py-3">
<div className="flex min-w-14 flex-col items-center gap-1 border-tint-subtle border-r pr-5">
<FileIcon contentType={contentType} className="size-5 text-primary" />
<div className="text-hint text-xs">{getHumanFileSize(file.size)}</div>
</div>
<div>
<div className={tcls('text-base')}>{file.name}</div>
<div className={tcls('text-sm', 'opacity-9', 'dark:opacity-8')}>
{contentType}
<div className="min-w-24 flex-1">
<div className="text-base">
<Link
href={file.downloadURL}
target="_blank"
insights={insights}
className="hover:underline"
>
{file.name}
</Link>
</div>
<div className="text-sm opacity-9 dark:opacity-8">{contentType}</div>
</div>
<div className="flex shrink-0 flex-wrap gap-2">
<DownloadButton
icon="download"
size="xsmall"
variant="secondary"
downloadUrl={file.downloadURL}
filename={file.name}
insights={insights}
>
{t(language, 'download')}
</DownloadButton>
<Button
icon="arrow-up-right-from-square"
size="xsmall"
variant="secondary"
href={file.downloadURL}
target="_blank"
insights={insights}
>
{t(language, 'open')}
</Button>
</div>
</Link>
</div>
</Caption>
);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/gitbook/src/components/DocumentView/FileIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function FileIcon(props: { contentType: SimplifiedFileType | null; classN
const { contentType, className } = props;

switch (contentType) {
case 'pdf':
case 'PDF':
return <Icon icon="file-pdf" className={className} />;
case 'image':
return <Icon icon="file-image" className={className} />;
Expand Down
39 changes: 39 additions & 0 deletions packages/gitbook/src/components/primitives/DownloadButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use client';
import { Button, type ButtonProps } from './Button';

/**
* Button that triggers a file download when clicked.
*/
export function DownloadButton(
props: Omit<ButtonProps, 'onClick' | 'href'> & { downloadUrl: string; filename: string }
) {
const { downloadUrl, filename, ...buttonProps } = props;

return (
<Button
{...buttonProps}
onClick={(e) => {
e.preventDefault();
void forceDownload(downloadUrl, filename);
}}
/>
);
}

/**
* Force download a file from a given URL with a specified filename.
*/
async function forceDownload(url: string, filename: string) {
const response = await fetch(url);
const blob = await response.blob();
const objectUrl = URL.createObjectURL(blob);

const link = document.createElement('a');
link.href = objectUrl;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);

URL.revokeObjectURL(objectUrl);
}
2 changes: 2 additions & 0 deletions packages/gitbook/src/intl/translations/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,6 @@ export const de = {
hint_warning: 'Warnung',
hint_danger: 'Gefahr',
hint_success: 'Erfolg',
download: 'Herunterladen',
open: 'Öffnen',
};
2 changes: 2 additions & 0 deletions packages/gitbook/src/intl/translations/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,6 @@ export const en = {
hint_warning: 'Warning',
hint_danger: 'Danger',
hint_success: 'Success',
download: 'Download',
open: 'Open',
};
2 changes: 2 additions & 0 deletions packages/gitbook/src/intl/translations/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,6 @@ export const es: TranslationLanguage = {
hint_warning: 'Advertencia',
hint_danger: 'Peligro',
hint_success: 'Éxito',
download: 'Descargar',
open: 'Abrir',
};
2 changes: 2 additions & 0 deletions packages/gitbook/src/intl/translations/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,6 @@ export const fr = {
hint_warning: 'Avertissement',
hint_danger: 'Danger',
hint_success: 'Succès',
download: 'Télécharger',
open: 'Ouvrir',
};
2 changes: 2 additions & 0 deletions packages/gitbook/src/intl/translations/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,6 @@ export const it: TranslationLanguage = {
hint_warning: 'Avviso',
hint_danger: 'Pericolo',
hint_success: 'Successo',
download: 'Scarica',
open: 'Apri',
};
2 changes: 2 additions & 0 deletions packages/gitbook/src/intl/translations/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,6 @@ export const ja: TranslationLanguage = {
hint_warning: '警告',
hint_danger: '危険',
hint_success: '成功',
download: 'ダウンロード',
open: '開く',
};
2 changes: 2 additions & 0 deletions packages/gitbook/src/intl/translations/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,6 @@ export const nl: TranslationLanguage = {
hint_warning: 'Waarschuwing',
hint_danger: 'Gevaar',
hint_success: 'Succes',
download: 'Downloaden',
open: 'Openen',
};
2 changes: 2 additions & 0 deletions packages/gitbook/src/intl/translations/no.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,6 @@ export const no: TranslationLanguage = {
hint_warning: 'Advarsel',
hint_danger: 'Fare',
hint_success: 'Suksess',
download: 'Last ned',
open: 'Åpne',
};
2 changes: 2 additions & 0 deletions packages/gitbook/src/intl/translations/pt-br.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,6 @@ export const pt_br = {
hint_warning: 'Aviso',
hint_danger: 'Perigo',
hint_success: 'Sucesso',
download: 'Baixar',
open: 'Abrir',
};
2 changes: 2 additions & 0 deletions packages/gitbook/src/intl/translations/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,6 @@ export const ru = {
hint_warning: 'Предупреждение',
hint_danger: 'Опасность',
hint_success: 'Успех',
download: 'Скачать',
open: 'Открыть',
};
2 changes: 2 additions & 0 deletions packages/gitbook/src/intl/translations/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,6 @@ export const zh: TranslationLanguage = {
hint_warning: '警告',
hint_danger: '危险',
hint_success: '成功',
download: '下载',
open: '打开',
};
4 changes: 2 additions & 2 deletions packages/gitbook/src/lib/files.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type SimplifiedFileType = 'image' | 'pdf' | 'archive';
export type SimplifiedFileType = 'image' | 'PDF' | 'archive';

/**
* Get a simplified content type for the given mime type.
Expand All @@ -11,7 +11,7 @@ export function getSimplifiedContentType(mimeType: string): SimplifiedFileType |
switch (mimeType) {
case 'application/pdf':
case 'application/x-pdf':
return 'pdf';
return 'PDF';
case 'application/zip':
case 'application/x-7z-compressed':
case 'application/x-zip-compressed':
Expand Down