Support image frame (#3695)

This commit is contained in:
Claire Chabas
2025-10-02 10:17:48 +02:00
committed by GitHub
parent f3e40410c4
commit e0fb9ac3ca
4 changed files with 88 additions and 35 deletions
+2 -2
View File
@@ -304,7 +304,7 @@
"react-dom": "^19.0.0",
},
"catalog": {
"@gitbook/api": "0.143.1",
"@gitbook/api": "0.143.2",
"bidc": "^0.0.2",
},
"packages": {
@@ -676,7 +676,7 @@
"@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@6.6.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "6.6.0" } }, "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg=="],
"@gitbook/api": ["@gitbook/api@0.143.1", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-5k7PnMe9W8EhmSejqayCbAIIJDGB4C2m+o6+dD+asmlv+6jE/LqoxuAvbP8o+kG83tMnbsr5I5d73B/9cAYbag=="],
"@gitbook/api": ["@gitbook/api@0.143.2", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-ZIQwIgX+artl0o20ftnzeJyM9icwQ6pFeM55AG/lEdeFbMuUGxTfSnZ90+8Aou8l/3ELR3/tOkU2SAvyKc+ATw=="],
"@gitbook/browser-types": ["@gitbook/browser-types@workspace:packages/browser-types"],
+1 -1
View File
@@ -34,7 +34,7 @@
"workspaces": {
"packages": ["packages/*"],
"catalog": {
"@gitbook/api": "0.143.1",
"@gitbook/api": "0.143.2",
"bidc": "^0.0.2"
}
},
@@ -24,6 +24,7 @@ export function Caption(
wrapperStyle?: ClassValue;
block: DocumentBlockImage | DocumentBlockDrawing | DocumentBlockEmbed | DocumentBlockFile;
withBorder?: boolean;
withFrame?: boolean;
} & DocumentContextProps
) {
const {
@@ -33,6 +34,7 @@ export function Caption(
context,
fit = false,
withBorder = false,
withFrame = false,
wrapperStyle = [
'relative',
'overflow-hidden',
@@ -44,6 +46,7 @@ export function Caption(
withBorder
? 'rounded-corners:rounded-sm circular-corners:rounded-2xl after:border-tint-subtle after:border after:rounded circular-corners:after:rounded-2xl rounded-corners:after:rounded-sm dark:after:mix-blend-plus-lighter after:pointer-events-none'
: null,
withFrame && 'p-2',
],
style,
} = props;
@@ -62,7 +65,14 @@ export function Caption(
return (
<picture className={tcls('relative', style)}>
<div className={tcls(wrapperStyle, 'mx-auto')}>{children}</div>
<figcaption className={tcls('text-sm', 'text-center', 'mt-2', 'text-tint')}>
<figcaption
className={tcls(
'text-xs',
'text-center',
'text-tint',
withFrame ? 'mt-0.5 mb-2.5' : 'mt-2'
)}
>
<Inlines
nodes={captionParagraph.nodes}
document={document}
@@ -11,8 +11,8 @@ import type { DocumentContext } from './DocumentView';
export function Images(props: BlockProps<DocumentBlockImages>) {
const { document, block, style, context, isEstimatedOffscreen } = props;
const isMultipleImages = block.nodes.length > 1;
const { align = 'center' } = block.data;
const hasMultipleImages = block.nodes.length > 1;
const { align = 'center', withFrame } = block.data;
return (
<div
@@ -24,7 +24,15 @@ export function Images(props: BlockProps<DocumentBlockImages>) {
align === 'center' && 'justify-center',
align === 'right' && 'justify-end',
align === 'left' && 'justify-start',
isMultipleImages && ['grid', 'grid-flow-col']
hasMultipleImages && ['grid', 'grid-flow-col'],
withFrame && [
'rounded-2xl',
'border',
'border-[rgb(234,235,238)]',
'dark:border-[rgb(45,50,58)]',
'relative',
'overflow-hidden',
]
)}
>
{block.nodes.map((node: any, _i: number) => (
@@ -36,6 +44,7 @@ export function Images(props: BlockProps<DocumentBlockImages>) {
siblings={block.nodes.length}
context={context}
isEstimatedOffscreen={isEstimatedOffscreen}
withFrame={withFrame}
/>
))}
</div>
@@ -62,8 +71,9 @@ async function ImageBlock(props: {
context: DocumentContext;
siblings: number;
isEstimatedOffscreen: boolean;
withFrame?: boolean;
}) {
const { block, context, isEstimatedOffscreen } = props;
const { block, context, isEstimatedOffscreen, withFrame } = props;
const [src, darkSrc] = await Promise.all([
context.contentContext ? resolveContentRef(block.data.ref, context.contentContext) : null,
@@ -77,33 +87,66 @@ async function ImageBlock(props: {
}
return (
<Caption {...props} fit>
<Image
alt={block.data.alt ?? ''}
sizes={imageBlockSizes}
resize={context.contentContext?.imageResizer}
sources={{
light: {
src: src.href,
size: src.file?.dimensions,
},
dark: darkSrc
? {
src: darkSrc.href,
size: darkSrc.file?.dimensions,
}
: null,
}}
priority={isEstimatedOffscreen ? 'lazy' : 'high'}
preload
zoom
inlineStyle={{
maxWidth: '100%',
width: getImageDimension(block.data.width, undefined),
height: getImageDimension(block.data.height, 'auto'),
}}
/>
</Caption>
<div className={tcls('relative', 'overflow-hidden')}>
{/* Frame grid */}
{withFrame && (
<div
className={tcls(
'absolute',
'-top-0.5',
'-left-0.5',
'right-px',
'bottom-px',
'opacity-40',
'dark:opacity-[0.1]',
'bg-[length:24px_24px,24px_24px]',
'bg-[linear-gradient(to_right,_rgb(234,235,238)_1px,_transparent_1px),linear-gradient(to_bottom,_rgb(234,235,238)_1px,_transparent_1px)]',
'dark:bg-[linear-gradient(to_right,_rgb(122,128,139)_1px,_transparent_1px),linear-gradient(to_bottom,_rgb(122,128,139)_1px,_transparent_1px)]',
'bg-repeat'
)}
/>
)}
{/* Shadow overlay */}
{withFrame && (
<div
className={tcls(
'pointer-events-none absolute inset-0 rounded-2xl',
'shadow-[inset_0_0_10px_10px_rgba(255,255,255,0.9)]',
'dark:shadow-[inset_0_0_10px_10px_rgb(29,29,29)]'
)}
/>
)}
<Caption {...props} fit>
<Image
alt={block.data.alt ?? ''}
sizes={imageBlockSizes}
resize={context.contentContext?.imageResizer}
sources={{
light: {
src: src.href,
size: src.file?.dimensions,
},
dark: darkSrc
? {
src: darkSrc.href,
size: darkSrc.file?.dimensions,
}
: null,
}}
priority={isEstimatedOffscreen ? 'lazy' : 'high'}
preload
zoom
inlineStyle={{
maxWidth: '100%',
width: getImageDimension(block.data.width, undefined),
height: getImageDimension(block.data.height, 'auto'),
}}
style={withFrame ? 'rounded-xl' : undefined}
/>
</Caption>
</div>
);
}