mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-21 01:53:26 +00:00
Render drawing blocks (#125)
* Improve rendering of block with captions * Remove redirect test
This commit is contained in:
@@ -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,
|
||||
]
|
||||
: [],
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user