Skip to content

Commit 7b50d8f

Browse files
committed
Support image frame
1 parent 1e4e54e commit 7b50d8f

File tree

2 files changed

+75
-32
lines changed

2 files changed

+75
-32
lines changed

packages/gitbook/src/components/DocumentView/Caption.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export function Caption(
2424
wrapperStyle?: ClassValue;
2525
block: DocumentBlockImage | DocumentBlockDrawing | DocumentBlockEmbed | DocumentBlockFile;
2626
withBorder?: boolean;
27+
withFrame?: boolean;
2728
} & DocumentContextProps
2829
) {
2930
const {
@@ -33,6 +34,7 @@ export function Caption(
3334
context,
3435
fit = false,
3536
withBorder = false,
37+
withFrame = false,
3638
wrapperStyle = [
3739
'relative',
3840
'overflow-hidden',
@@ -44,6 +46,7 @@ export function Caption(
4446
withBorder
4547
? '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'
4648
: null,
49+
withFrame && 'p-2',
4750
],
4851
style,
4952
} = props;
@@ -62,7 +65,14 @@ export function Caption(
6265
return (
6366
<picture className={tcls('relative', style)}>
6467
<div className={tcls(wrapperStyle, 'mx-auto')}>{children}</div>
65-
<figcaption className={tcls('text-sm', 'text-center', 'mt-2', 'text-tint')}>
68+
<figcaption
69+
className={tcls(
70+
'text-xs',
71+
'text-center',
72+
'text-tint',
73+
withFrame ? 'mt-0.5 mb-2.5' : 'mt-2'
74+
)}
75+
>
6676
<Inlines
6777
nodes={captionParagraph.nodes}
6878
document={document}

packages/gitbook/src/components/DocumentView/Images.tsx

Lines changed: 64 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import type { DocumentContext } from './DocumentView';
1111
export function Images(props: BlockProps<DocumentBlockImages>) {
1212
const { document, block, style, context, isEstimatedOffscreen } = props;
1313

14-
const isMultipleImages = block.nodes.length > 1;
15-
const { align = 'center' } = block.data;
14+
const hasMultipleImages = block.nodes.length > 1;
15+
const { align = 'center', withFrame } = block.data;
1616

1717
return (
1818
<div
@@ -24,7 +24,8 @@ export function Images(props: BlockProps<DocumentBlockImages>) {
2424
align === 'center' && 'justify-center',
2525
align === 'right' && 'justify-end',
2626
align === 'left' && 'justify-start',
27-
isMultipleImages && ['grid', 'grid-flow-col']
27+
hasMultipleImages && ['grid', 'grid-flow-col'],
28+
withFrame && ['rounded-2xl', 'border', 'border-neutral']
2829
)}
2930
>
3031
{block.nodes.map((node: any, _i: number) => (
@@ -36,6 +37,7 @@ export function Images(props: BlockProps<DocumentBlockImages>) {
3637
siblings={block.nodes.length}
3738
context={context}
3839
isEstimatedOffscreen={isEstimatedOffscreen}
40+
withFrame={withFrame}
3941
/>
4042
))}
4143
</div>
@@ -62,8 +64,9 @@ async function ImageBlock(props: {
6264
context: DocumentContext;
6365
siblings: number;
6466
isEstimatedOffscreen: boolean;
67+
withFrame?: boolean;
6568
}) {
66-
const { block, context, isEstimatedOffscreen } = props;
69+
const { block, context, isEstimatedOffscreen, withFrame } = props;
6770

6871
const [src, darkSrc] = await Promise.all([
6972
context.contentContext ? resolveContentRef(block.data.ref, context.contentContext) : null,
@@ -77,33 +80,63 @@ async function ImageBlock(props: {
7780
}
7881

7982
return (
80-
<Caption {...props} fit>
81-
<Image
82-
alt={block.data.alt ?? ''}
83-
sizes={imageBlockSizes}
84-
resize={context.contentContext?.imageResizer}
85-
sources={{
86-
light: {
87-
src: src.href,
88-
size: src.file?.dimensions,
89-
},
90-
dark: darkSrc
91-
? {
92-
src: darkSrc.href,
93-
size: darkSrc.file?.dimensions,
94-
}
95-
: null,
96-
}}
97-
priority={isEstimatedOffscreen ? 'lazy' : 'high'}
98-
preload
99-
zoom
100-
inlineStyle={{
101-
maxWidth: '100%',
102-
width: getImageDimension(block.data.width, undefined),
103-
height: getImageDimension(block.data.height, 'auto'),
104-
}}
105-
/>
106-
</Caption>
83+
<div className={tcls('relative', 'overflow-hidden')}>
84+
{/* Frame grid */}
85+
{withFrame && (
86+
<div
87+
className={tcls(
88+
'-top-0.5',
89+
'-left-0.5',
90+
'absolute',
91+
'inset-px',
92+
'opacity-50',
93+
'bg-[length:24px_24px,24px_24px]',
94+
'bg-[linear-gradient(to_right,_rgb(234,235,238)_1px,_transparent_1px),linear-gradient(to_bottom,_rgb(234,235,238)_1px,_transparent_1px)]',
95+
'bg-repeat'
96+
)}
97+
/>
98+
)}
99+
100+
{/* Shadow overlay */}
101+
{withFrame && (
102+
<div
103+
className={tcls(
104+
'pointer-events-none absolute inset-0 rounded-2xl',
105+
'shadow-[inset_0_0_10px_7px_rgba(255,255,255,0.9)]',
106+
'dark:shadow-[inset_0_0_10px_7px_rgba(21,23,28,0.9)]'
107+
)}
108+
/>
109+
)}
110+
111+
<Caption {...props} fit>
112+
<Image
113+
alt={block.data.alt ?? ''}
114+
sizes={imageBlockSizes}
115+
resize={context.contentContext?.imageResizer}
116+
sources={{
117+
light: {
118+
src: src.href,
119+
size: src.file?.dimensions,
120+
},
121+
dark: darkSrc
122+
? {
123+
src: darkSrc.href,
124+
size: darkSrc.file?.dimensions,
125+
}
126+
: null,
127+
}}
128+
priority={isEstimatedOffscreen ? 'lazy' : 'high'}
129+
preload
130+
zoom
131+
inlineStyle={{
132+
maxWidth: '100%',
133+
width: getImageDimension(block.data.width, undefined),
134+
height: getImageDimension(block.data.height, 'auto'),
135+
}}
136+
style={withFrame ? 'rounded-xl' : undefined}
137+
/>
138+
</Caption>
139+
</div>
107140
);
108141
}
109142

0 commit comments

Comments
 (0)