mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-24 11:26:31 +00:00
Improve rendering of File column in tables. (#2297)
This commit is contained in:
@@ -1,12 +1,10 @@
|
||||
import IconDownload from '@geist-ui/icons/download';
|
||||
import IconFileText from '@geist-ui/icons/fileText';
|
||||
import IconImage from '@geist-ui/icons/image';
|
||||
import IconPaperClip from '@geist-ui/icons/paperclip';
|
||||
import { DocumentBlockFile } from '@gitbook/api';
|
||||
|
||||
import { getSimplifiedContentType } from '@/lib/files';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import { BlockProps } from './Block';
|
||||
import { FileIcon } from './FileIcon';
|
||||
|
||||
export async function File(props: BlockProps<DocumentBlockFile>) {
|
||||
const { block, context, style } = props;
|
||||
@@ -18,42 +16,7 @@ export async function File(props: BlockProps<DocumentBlockFile>) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const contentType = (() => {
|
||||
switch (file.contentType) {
|
||||
case 'application/pdf':
|
||||
case 'application/x-pdf':
|
||||
return 'pdf';
|
||||
case 'image/png':
|
||||
case 'image/jpeg':
|
||||
case 'image/gif':
|
||||
case 'image/webp':
|
||||
case 'image/tiff':
|
||||
case 'image/svg+xml':
|
||||
return 'image';
|
||||
case 'application/zip':
|
||||
case 'application/x-7z-compressed':
|
||||
case 'application/x-zip-compressed':
|
||||
case 'application/x-tar':
|
||||
case 'application/x-rar-compressed':
|
||||
case 'application/vnd.rar':
|
||||
return 'archive';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
|
||||
const icon = (() => {
|
||||
switch (contentType) {
|
||||
case 'pdf':
|
||||
return <IconFileText />;
|
||||
case 'image':
|
||||
return <IconImage />;
|
||||
case 'archive':
|
||||
return <IconPaperClip />;
|
||||
default:
|
||||
return <IconDownload />;
|
||||
}
|
||||
})();
|
||||
const contentType = getSimplifiedContentType(file.contentType);
|
||||
|
||||
return (
|
||||
<a
|
||||
@@ -103,7 +66,9 @@ export async function File(props: BlockProps<DocumentBlockFile>) {
|
||||
'dark:border-light/2',
|
||||
)}
|
||||
>
|
||||
<div className={tcls('*:w-5', '*:h-5', '*:stroke-primary')}>{icon}</div>
|
||||
<div className={tcls('*:w-5', '*:h-5', '*:stroke-primary')}>
|
||||
<FileIcon contentType={contentType} />
|
||||
</div>
|
||||
<div
|
||||
className={tcls(
|
||||
'text-xs',
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import IconDownload from '@geist-ui/icons/download';
|
||||
import IconFileText from '@geist-ui/icons/fileText';
|
||||
import IconImage from '@geist-ui/icons/image';
|
||||
import IconPaperClip from '@geist-ui/icons/paperclip';
|
||||
|
||||
import { SimplifiedFileType } from '@/lib/files';
|
||||
|
||||
/**
|
||||
* Render an appropriate icon for a file.
|
||||
*/
|
||||
export function FileIcon(props: { contentType: SimplifiedFileType | null }) {
|
||||
const { contentType } = props;
|
||||
|
||||
switch (contentType) {
|
||||
case 'pdf':
|
||||
return <IconFileText />;
|
||||
case 'image':
|
||||
return <IconImage />;
|
||||
case 'archive':
|
||||
return <IconPaperClip />;
|
||||
default:
|
||||
return <IconDownload />;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,9 @@ import assertNever from 'assert-never';
|
||||
|
||||
import { Checkbox, Emoji } from '@/components/primitives';
|
||||
import { StyledLink } from '@/components/primitives';
|
||||
import { Image } from '@/components/utils';
|
||||
import { getNodeFragmentByName } from '@/lib/document';
|
||||
import { getSimplifiedContentType } from '@/lib/files';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import { filterOutNullable } from '@/lib/typescript';
|
||||
|
||||
@@ -12,6 +14,7 @@ import { TableRecordKV } from './Table';
|
||||
import { getColumnAlignment } from './utils';
|
||||
import { BlockProps } from '../Block';
|
||||
import { Blocks } from '../Blocks';
|
||||
import { FileIcon } from '../FileIcon';
|
||||
|
||||
/**
|
||||
* Render the value for a column in a record.
|
||||
@@ -125,11 +128,41 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
|
||||
|
||||
return (
|
||||
<Tag className={tcls('text-base')}>
|
||||
{files.filter(filterOutNullable).map((file, index) => (
|
||||
<StyledLink key={index} href={file.href}>
|
||||
{file.text}
|
||||
</StyledLink>
|
||||
))}
|
||||
{files.filter(filterOutNullable).map((ref, index) => {
|
||||
const contentType = ref.file
|
||||
? getSimplifiedContentType(ref.file.contentType)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<StyledLink
|
||||
key={index}
|
||||
href={ref.href}
|
||||
target="_blank"
|
||||
style={['flex', 'flex-row', 'items-center', 'gap-2']}
|
||||
>
|
||||
{contentType === 'image' ? (
|
||||
<Image
|
||||
style={['max-h-[1lh]', 'h-[1lh]']}
|
||||
alt={ref.text}
|
||||
sizes={[{ width: 24 }]}
|
||||
sources={{
|
||||
light: {
|
||||
src: ref.href,
|
||||
size: {
|
||||
width: 24,
|
||||
height: 24,
|
||||
},
|
||||
},
|
||||
}}
|
||||
priority="lazy"
|
||||
/>
|
||||
) : (
|
||||
<FileIcon contentType={contentType} />
|
||||
)}
|
||||
{ref.text}
|
||||
</StyledLink>
|
||||
);
|
||||
})}
|
||||
</Tag>
|
||||
);
|
||||
case 'content-ref': {
|
||||
@@ -208,4 +241,4 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
|
||||
default:
|
||||
assertNever(definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,16 @@
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import { ClassValue, tcls } from '@/lib/tailwind';
|
||||
|
||||
import { Link, LinkProps } from '../primitives/Link';
|
||||
|
||||
/**
|
||||
* Styled version of Link component.
|
||||
*/
|
||||
export function StyledLink(props: LinkProps) {
|
||||
export function StyledLink(props: Omit<LinkProps, 'style'> & { style?: ClassValue }) {
|
||||
const { style, ...rest } = props;
|
||||
|
||||
return (
|
||||
<Link
|
||||
{...props}
|
||||
{...rest}
|
||||
className={tcls(
|
||||
'underline',
|
||||
'underline-offset-2',
|
||||
@@ -16,6 +18,7 @@ export function StyledLink(props: LinkProps) {
|
||||
'text-primary',
|
||||
'hover:text-primary-700',
|
||||
'transition-colors',
|
||||
style,
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
export type SimplifiedFileType = 'image' | 'pdf' | 'archive';
|
||||
|
||||
/**
|
||||
* Get a simplified content type for the given mime type.
|
||||
*/
|
||||
export function getSimplifiedContentType(mimeType: string): SimplifiedFileType | null {
|
||||
if (mimeType.startsWith('image')) {
|
||||
return 'image';
|
||||
}
|
||||
|
||||
switch (mimeType) {
|
||||
case 'application/pdf':
|
||||
case 'application/x-pdf':
|
||||
return 'pdf';
|
||||
case 'application/zip':
|
||||
case 'application/x-7z-compressed':
|
||||
case 'application/x-zip-compressed':
|
||||
case 'application/x-tar':
|
||||
case 'application/x-rar-compressed':
|
||||
case 'application/vnd.rar':
|
||||
return 'archive';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user