Skip to content

Commit ff96bb5

Browse files
authored
Support new coverDefinitionDark for cards & image type (#3547)
1 parent 8784112 commit ff96bb5

File tree

6 files changed

+161
-32
lines changed

6 files changed

+161
-32
lines changed

.changeset/serious-pumas-clap.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'gitbook': patch
3+
---
4+
5+
Support new coverDefinitionDark for cards & image type

bun.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@
246246
"react-dom": "^19.0.0",
247247
},
248248
"catalog": {
249-
"@gitbook/api": "^0.132.0",
249+
"@gitbook/api": "^0.133.0",
250250
},
251251
"packages": {
252252
"@ai-sdk/provider": ["@ai-sdk/provider@1.1.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-0M+qjp+clUD0R1E5eWQFhxEvWLNaOtGQRUaBn8CUABnSKredagq92hUS9VjOzGsTm37xLfpaxl97AVtbeOsHew=="],
@@ -611,7 +611,7 @@
611611

612612
"@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@6.6.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "6.6.0" } }, "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg=="],
613613

614-
"@gitbook/api": ["@gitbook/api@0.132.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-1kuYIHiQIFzcSbKZ0JkzfSKujWFHyM4OAT3P0JENekBffwEinUMqblkJZNQmWA4QhBYi0QT8bJA6OCda4Xx0oA=="],
614+
"@gitbook/api": ["@gitbook/api@0.133.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-Xi/A8NROQIj1haDA2XYr9b2+suUviyEVjplSslmwA2HyZnFNlkTaB1W0CwpVrZF8FZsfi3fZekQECOF34RLDDA=="],
615615

616616
"@gitbook/cache-tags": ["@gitbook/cache-tags@workspace:packages/cache-tags"],
617617

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"workspaces": {
3535
"packages": ["packages/*"],
3636
"catalog": {
37-
"@gitbook/api": "^0.132.0"
37+
"@gitbook/api": "^0.133.0"
3838
}
3939
},
4040
"patchedDependencies": {

packages/gitbook/src/components/DocumentView/Table/RecordCard.tsx

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
1+
import { LinkBox, LinkOverlay } from '@/components/primitives';
2+
import { Image } from '@/components/utils';
3+
import { type ResolvedContentRef, resolveContentRef } from '@/lib/references';
4+
import { tcls } from '@/lib/tailwind';
15
import {
26
type ContentRef,
37
type DocumentTableViewCards,
48
SiteInsightsLinkPosition,
59
} from '@gitbook/api';
6-
7-
import { LinkBox, LinkOverlay } from '@/components/primitives';
8-
import { Image } from '@/components/utils';
9-
import { resolveContentRef } from '@/lib/references';
10-
import { tcls } from '@/lib/tailwind';
11-
1210
import { RecordColumnValue } from './RecordColumnValue';
1311
import type { TableRecordKV, TableViewProps } from './Table';
1412
import { RecordCardStyles } from './styles';
15-
import { getRecordValue } from './utils';
13+
import { getRecordCardCovers } from './utils';
1614

1715
export async function RecordCard(
1816
props: TableViewProps<DocumentTableViewCards> & {
@@ -21,25 +19,21 @@ export async function RecordCard(
2119
) {
2220
const { view, record, context, block, isOffscreen } = props;
2321

24-
const coverFile = view.coverDefinition
25-
? getRecordValue<string[]>(record[1], view.coverDefinition)?.[0]
26-
: null;
22+
const { dark, light } = getRecordCardCovers(record[1], view);
2723
const targetRef = view.targetDefinition
2824
? (record[1].values[view.targetDefinition] as ContentRef)
2925
: null;
3026

31-
const [cover, target] = await Promise.all([
32-
coverFile && context.contentContext
33-
? resolveContentRef({ kind: 'file', file: coverFile }, context.contentContext)
34-
: null,
27+
const [lightCover, darkCover, target] = await Promise.all([
28+
light && context.contentContext ? resolveContentRef(light, context.contentContext) : null,
29+
dark && context.contentContext ? resolveContentRef(dark, context.contentContext) : null,
3530
targetRef && context.contentContext
3631
? resolveContentRef(targetRef, context.contentContext)
3732
: null,
3833
]);
3934

40-
const coverIsSquareOrPortrait =
41-
cover?.file?.dimensions &&
42-
cover.file?.dimensions?.width / cover.file?.dimensions?.height <= 1;
35+
const darkCoverIsSquareOrPortrait = isSquareOrPortrait(darkCover);
36+
const lightCoverIsSquareOrPortrait = isSquareOrPortrait(lightCover);
4337

4438
const body = (
4539
<div
@@ -63,23 +57,32 @@ export async function RecordCard(
6357
// On mobile, check if we can display the cover responsively or not:
6458
// - If the file has a landscape aspect ratio, we display it normally
6559
// - If the file is square or portrait, we display it left with 40% of the card width
66-
coverIsSquareOrPortrait
60+
lightCoverIsSquareOrPortrait || darkCoverIsSquareOrPortrait
6761
? [
68-
'grid-cols-[40%__1fr]',
69-
'min-[432px]:grid-cols-none',
70-
'min-[432px]:grid-rows-[auto_1fr]',
71-
]
62+
lightCoverIsSquareOrPortrait
63+
? 'grid-cols-[40%__1fr] min-[432px]:grid-cols-none min-[432px]:grid-rows-[auto_1fr]'
64+
: '',
65+
darkCoverIsSquareOrPortrait
66+
? 'dark:grid-cols-[40%__1fr] dark:min-[432px]:grid-cols-none dark:min-[432px]:grid-rows-[auto_1fr]'
67+
: '',
68+
].filter(Boolean)
7269
: 'grid-rows-[auto_1fr]'
7370
)}
7471
>
75-
{cover ? (
72+
{lightCover ? (
7673
<Image
7774
alt="Cover"
7875
sources={{
7976
light: {
80-
src: cover.href,
81-
size: cover.file?.dimensions,
77+
src: lightCover.href,
78+
size: lightCover.file?.dimensions,
8279
},
80+
dark: darkCover
81+
? {
82+
src: darkCover.href,
83+
size: darkCover.file?.dimensions,
84+
}
85+
: null,
8386
}}
8487
sizes={[
8588
{
@@ -92,8 +95,15 @@ export async function RecordCard(
9295
'w-full',
9396
'h-full',
9497
'object-cover',
95-
coverIsSquareOrPortrait
96-
? ['min-[432px]:h-auto', 'min-[432px]:aspect-video']
98+
lightCoverIsSquareOrPortrait || darkCoverIsSquareOrPortrait
99+
? [
100+
lightCoverIsSquareOrPortrait
101+
? 'min-[432px]:aspect-video min-[432px]:h-auto'
102+
: '',
103+
darkCoverIsSquareOrPortrait
104+
? 'dark:min-[432px]:aspect-video dark:min-[432px]:h-auto'
105+
: '',
106+
].filter(Boolean)
97107
: ['h-auto', 'aspect-video']
98108
)}
99109
priority={isOffscreen ? 'lazy' : 'high'}
@@ -167,3 +177,16 @@ export async function RecordCard(
167177

168178
return <div className={tcls(RecordCardStyles)}>{body}</div>;
169179
}
180+
181+
/**
182+
* Check if a file is square or portrait.
183+
*/
184+
function isSquareOrPortrait(contentRef: ResolvedContentRef | null) {
185+
const file = contentRef?.file;
186+
187+
if (!file || !file.dimensions) {
188+
return false;
189+
}
190+
191+
return file.dimensions?.width / file.dimensions?.height <= 1;
192+
}

packages/gitbook/src/components/DocumentView/Table/RecordColumnValue.tsx

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,6 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
174174
<StyledLink
175175
key={index}
176176
href={ref.href}
177-
target="_blank"
178177
className="flex flex-row items-center gap-2"
179178
insights={
180179
ref.file
@@ -329,6 +328,50 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
329328
</Tag>
330329
);
331330
}
331+
case 'image': {
332+
const image = context.contentContext
333+
? await resolveContentRef(value as ContentRef, context.contentContext)
334+
: null;
335+
336+
if (!image) {
337+
return null;
338+
}
339+
340+
return (
341+
<Tag className={tcls('text-base')} aria-labelledby={ariaLabelledBy}>
342+
<StyledLink
343+
href={image.href}
344+
className="flex flex-row items-center gap-2"
345+
insights={
346+
image.file
347+
? {
348+
type: 'link_click',
349+
link: {
350+
target: value as ContentRef,
351+
position: SiteInsightsLinkPosition.Content,
352+
},
353+
}
354+
: undefined
355+
}
356+
>
357+
<Image
358+
style={['max-h-lh', 'h-lh']}
359+
alt={image.text}
360+
sizes={[{ width: 24 }]}
361+
resize={context.contentContext?.imageResizer}
362+
sources={{
363+
light: {
364+
src: image.href,
365+
size: image.file?.dimensions,
366+
},
367+
}}
368+
priority="lazy"
369+
/>
370+
{image.text}
371+
</StyledLink>
372+
</Tag>
373+
);
374+
}
332375
default:
333376
assertNever(definition);
334377
}

packages/gitbook/src/components/DocumentView/Table/utils.ts

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import type { ContentRef, DocumentTableDefinition, DocumentTableRecord } from '@gitbook/api';
1+
import type {
2+
ContentRef,
3+
ContentRefFile,
4+
ContentRefURL,
5+
DocumentTableDefinition,
6+
DocumentTableRecord,
7+
DocumentTableViewCards,
8+
} from '@gitbook/api';
29
import assertNever from 'assert-never';
310

411
/**
@@ -12,6 +19,57 @@ export function getRecordValue<T extends number | string | boolean | string[] |
1219
return record.values[definitionId];
1320
}
1421

22+
/**
23+
* Get the covers for a record card.
24+
* Returns both the light and dark covers.
25+
* The light cover is a string or a content ref (image or files column type).
26+
* The dark cover is a content ref (image column type).
27+
*/
28+
export function getRecordCardCovers(
29+
record: DocumentTableRecord,
30+
view: DocumentTableViewCards
31+
): { [key in 'light' | 'dark']: ContentRefFile | ContentRefURL | null } {
32+
return {
33+
light: (() => {
34+
if (!view.coverDefinition) {
35+
return null;
36+
}
37+
38+
const value = getRecordValue(record, view.coverDefinition) as
39+
| ContentRefFile
40+
| ContentRefURL
41+
| string[];
42+
43+
if (Array.isArray(value)) {
44+
if (value.length === 0) {
45+
return null;
46+
}
47+
48+
if (typeof value[0] === 'string') {
49+
return { kind: 'file', file: value[0] };
50+
}
51+
}
52+
53+
return value as ContentRefFile | ContentRefURL;
54+
})(),
55+
dark: (() => {
56+
if (!view.coverDefinitionDark) {
57+
return null;
58+
}
59+
60+
const value = getRecordValue(record, view.coverDefinitionDark) as
61+
| ContentRefFile
62+
| ContentRefURL;
63+
64+
if (!value) {
65+
return null;
66+
}
67+
68+
return value;
69+
})(),
70+
};
71+
}
72+
1573
/**
1674
* Get the text alignment for a column.
1775
*/

0 commit comments

Comments
 (0)