Render drawing blocks (#125)

* Improve rendering of block with captions

* Remove redirect test
This commit is contained in:
Samy Pessé
2024-01-30 18:30:27 +01:00
committed by GitHub
parent aeab122d54
commit 4bbd86aeca
7 changed files with 151 additions and 133 deletions
+75
View File
@@ -0,0 +1,75 @@
import {
DocumentBlockDrawing,
DocumentBlockEmbed,
DocumentBlockImage,
JSONDocument,
} from '@gitbook/api';
import { getNodeFragmentByName, isNodeEmpty } from '@/lib/document';
import { ClassValue, tcls } from '@/lib/tailwind';
import { DocumentContextProps } from './DocumentView';
import { Inlines } from './Inlines';
/**
* Wrap a content of a block that has a potential caption.
*/
export function Caption(
props: {
children: React.ReactNode;
document: JSONDocument;
style?: ClassValue;
wrapperStyle?: ClassValue;
block: DocumentBlockImage | DocumentBlockDrawing | DocumentBlockEmbed;
} & DocumentContextProps,
) {
const {
children,
document,
block,
context,
wrapperStyle = [
'relative',
'overflow-hidden',
'rounded',
'after:block',
'after:absolute',
'after:-inset-[0]',
'after:border-dark/2',
'after:border',
'after:rounded',
'dark:after:border-light/1',
'dark:after:mix-blend-plus-lighter',
'after:pointer-events-none',
],
style,
} = props;
const caption = getNodeFragmentByName(block, 'caption');
const captionParagraph = caption?.nodes[0];
if (
!captionParagraph ||
captionParagraph.type !== 'paragraph' ||
isNodeEmpty(captionParagraph)
) {
return <div className={tcls(style, wrapperStyle)}>{children}</div>;
}
return (
<picture className={tcls('relative', style)}>
<div className={tcls(wrapperStyle)}>{children}</div>
<figcaption
className={tcls(
'text-sm',
'text-center',
'mt-2',
'text-dark/7',
'dark:text-light/6',
)}
>
<Inlines nodes={captionParagraph.nodes} document={document} context={context} />
</figcaption>
</picture>
);
}
@@ -191,10 +191,7 @@ function CodeHighlightLine(props: {
'dark:before:text-light/4',
line.highlighted
? [
'before:text-dark/6',
'dark:before:text-light/8',
]
? ['before:text-dark/6', 'dark:before:text-light/8']
: null,
]
: [],
+22 -5
View File
@@ -1,11 +1,28 @@
import { DocumentBlockDrawing } from '@gitbook/api';
import { tcls } from '@/lib/tailwind';
import { BlockProps } from './Block';
import { Caption } from './Caption';
import { Image } from '../utils';
export function Drawing(props: BlockProps<DocumentBlockDrawing>) {
const { style } = props;
export async function Drawing(props: BlockProps<DocumentBlockDrawing>) {
const { block, context } = props;
return <div className={tcls(style)}>TODO</div>;
const resolved = block.data.ref ? await context.resolveContentRef(block.data.ref) : null;
if (!resolved) {
return null;
}
return (
<Caption {...props}>
<Image
sources={{
light: {
src: resolved.href,
size: resolved.fileDimensions,
},
}}
alt="Drawing"
/>
</Caption>
);
}
+22 -47
View File
@@ -6,59 +6,34 @@ import { getNodeFragmentByName, isNodeEmpty } from '@/lib/document';
import { ClassValue, tcls } from '@/lib/tailwind';
import { BlockProps } from './Block';
import { Caption } from './Caption';
import { Inlines } from './Inlines';
export async function Embed(props: BlockProps<DocumentBlockEmbed>) {
const { block, style, document, context } = props;
const { block } = props;
const { data: embed } = await api().urls.getEmbedByUrl({ url: block.data.url });
const caption = getNodeFragmentByName(block, 'caption');
const captionParagraph = caption?.nodes[0];
const content = (wrapStyle: ClassValue) =>
embed.type === 'rich' ? (
<div
className={tcls(wrapStyle)}
dangerouslySetInnerHTML={{
__html: embed.html,
}}
/>
) : (
<Card
leadingIcon={
embed.icon ? (
<img src={embed.icon} className={tcls('w-5', 'h-5')} alt="Logo" />
) : null
}
href={block.data.url}
title={embed.title}
postTitle={embed.site}
style={wrapStyle}
/>
);
if (
!captionParagraph ||
captionParagraph.type !== 'paragraph' ||
isNodeEmpty(captionParagraph)
) {
return content(style);
}
return (
<figure className={tcls(style)}>
{content(null)}
<figcaption
className={tcls(
'text-sm',
'text-center',
'mt-2',
'text-dark/7',
'dark:text-light/6',
)}
>
<Inlines nodes={captionParagraph.nodes} document={document} context={context} />
</figcaption>
</figure>
<Caption {...props}>
{embed.type === 'rich' ? (
<div
dangerouslySetInnerHTML={{
__html: embed.html,
}}
/>
) : (
<Card
leadingIcon={
embed.icon ? (
<img src={embed.icon} className={tcls('w-5', 'h-5')} alt="Logo" />
) : null
}
href={block.data.url}
title={embed.title}
postTitle={embed.site}
/>
)}
</Caption>
);
}
+30 -72
View File
@@ -1,12 +1,11 @@
import { DocumentBlockImage, DocumentBlockImages, JSONDocument } from '@gitbook/api';
import { Image } from '@/components/utils';
import { getNodeFragmentByName, isNodeEmpty } from '@/lib/document';
import { ClassValue, tcls } from '@/lib/tailwind';
import { BlockProps } from './Block';
import { Caption } from './Caption';
import { DocumentContext } from './DocumentView';
import { Inlines } from './Inlines';
import { isBlockOffscreen } from './utils';
export function Images(props: BlockProps<DocumentBlockImages>) {
@@ -55,7 +54,7 @@ async function ImageBlock(props: {
siblings: number;
isOffscreen: boolean;
}) {
const { block, document, context, isOffscreen } = props;
const { block, context, isOffscreen } = props;
const [src, darkSrc] = await Promise.all([
context.resolveContentRef(block.data.ref),
@@ -66,75 +65,34 @@ async function ImageBlock(props: {
return null;
}
const imageBorder = tcls(
'relative',
'overflow-hidden',
'rounded',
'after:block',
'after:absolute',
'after:-inset-[0]',
'after:border-dark/2',
'after:border',
'after:rounded',
'dark:after:border-light/1',
'dark:after:mix-blend-plus-lighter',
'after:pointer-events-none',
);
const caption = getNodeFragmentByName(block, 'caption');
const captionParagraph = caption?.nodes[0];
const image = (
<Image
alt={block.data.alt ?? ''}
sizes={[
{
media: '(max-width: 640px)',
width: 400,
},
{
width: 768,
},
]}
sources={{
light: {
src: src.href,
size: src.fileDimensions,
},
dark: darkSrc
? {
src: darkSrc.href,
size: darkSrc.fileDimensions,
}
: null,
}}
priority={isOffscreen ? 'lazy' : 'high'}
preload
/>
);
if (
!captionParagraph ||
captionParagraph.type !== 'paragraph' ||
isNodeEmpty(captionParagraph)
) {
return image;
}
return (
<picture className={tcls('relative')}>
<div className={tcls(imageBorder)}>{image}</div>
<figcaption
className={tcls(
'text-sm',
'text-center',
'mt-2',
'text-dark/7',
'dark:text-light/6',
)}
>
<Inlines nodes={captionParagraph.nodes} document={document} context={context} />
</figcaption>
</picture>
<Caption {...props}>
<Image
alt={block.data.alt ?? ''}
sizes={[
{
media: '(max-width: 640px)',
width: 400,
},
{
width: 768,
},
]}
sources={{
light: {
src: src.href,
size: src.fileDimensions,
},
dark: darkSrc
? {
src: darkSrc.href,
size: darkSrc.fileDimensions,
}
: null,
}}
priority={isOffscreen ? 'lazy' : 'high'}
preload
/>
</Caption>
);
}
+1 -1
View File
@@ -140,7 +140,7 @@ export function isNodeEmpty(
node: DocumentText | DocumentFragment | DocumentInline | DocumentBlock | JSONDocument,
): boolean {
if (node.object !== 'text' && 'nodes' in node) {
if (node.nodes.length > 0) {
if (node.nodes.length > 1) {
return false;
}
-4
View File
@@ -60,10 +60,6 @@ const testCases: TestsCase[] = [
name: 'Encoded URL',
url: 'scan-using-snyk/supported-languages-and-frameworks/c-c++',
},
{
name: 'Redirect',
url: 'products/snyk-open-source/use-snyk-open-source-from-the-cli',
},
{
name: 'Revision',
url: '~/revisions/H41VQ6cIvd5hyUwcnwbC',