Enable cover heights in gbo

This commit is contained in:
Viktor Renkema
2025-10-13 12:43:17 +02:00
parent eea8f1e00f
commit 88fe9e97c0
5 changed files with 49 additions and 9 deletions
@@ -17,6 +17,7 @@ import { PageCover } from './PageCover';
import { PageFooterNavigation } from './PageFooterNavigation';
import { PageHeader } from './PageHeader';
import { PreservePageLayout } from './PreservePageLayout';
import { hasVisibleCover } from './coverHeight';
const LINK_PREVIEW_MAX_COUNT = 100;
@@ -32,6 +33,7 @@ export function PageBody(props: {
const { customization } = context;
const contentFullWidth = document ? hasFullWidthBlock(document) : false;
const visibleCover = hasVisibleCover(page.cover);
// Render link previews only if there are less than LINK_PREVIEW_MAX_COUNT links in the document.
const withLinkPreviews = document
@@ -60,7 +62,7 @@ export function PageBody(props: {
)}
>
<PreservePageLayout siteWidthWide={siteWidthWide} />
{page.cover && page.layout.cover && page.layout.coverSize === 'hero' ? (
{visibleCover && page.layout.cover && page.layout.coverSize === 'hero' ? (
<PageCover as="hero" page={page} cover={page.cover} context={context} />
) : null}
@@ -8,6 +8,7 @@ import { tcls } from '@/lib/tailwind';
import { assert } from 'ts-essentials';
import { PageCoverImage } from './PageCoverImage';
import { getCoverHeight } from './coverHeight';
import defaultPageCoverSVG from './default-page-cover.svg';
const defaultPageCover = defaultPageCoverSVG as StaticImageData;
@@ -22,6 +23,12 @@ export async function PageCover(props: {
context: GitBookSiteContext;
}) {
const { as, page, cover, context } = props;
const height = getCoverHeight(cover);
if (height <= 0) {
return null;
}
const [resolved, resolvedDark] = await Promise.all([
cover.ref ? resolveContentRef(cover.ref, context) : null,
cover.refDark ? resolveContentRef(cover.refDark, context) : null,
@@ -78,8 +85,10 @@ export async function PageCover(props: {
<div
id="page-cover"
data-full={String(as === 'full')}
style={{ height: `${height}px` }}
className={tcls(
'overflow-hidden',
'shrink-0',
// Negative margin to balance the container padding
'-mx-4',
as === 'full'
@@ -48,9 +48,8 @@ export function PageCoverImage({ imgs, y }: { imgs: Images; y: number }) {
sizes={imgs.light.sizes}
fetchPriority="high"
alt="Page cover"
className={tcls('w-full', 'object-cover', imgs.dark ? 'dark:hidden' : '')}
className={tcls('h-full', 'w-full', 'object-cover', imgs.dark ? 'dark:hidden' : '')}
style={{
aspectRatio: `${PAGE_COVER_SIZE.width}/${PAGE_COVER_SIZE.height}`,
objectPosition: `50% ${getTop(container, y, imgs.light)}`,
}}
/>
@@ -61,9 +60,8 @@ export function PageCoverImage({ imgs, y }: { imgs: Images; y: number }) {
sizes={imgs.dark.sizes}
fetchPriority="low"
alt="Page cover"
className={tcls('w-full', 'object-cover', 'dark:inline', 'hidden')}
className={tcls('h-full', 'w-full', 'object-cover', 'dark:inline', 'hidden')}
style={{
aspectRatio: `${PAGE_COVER_SIZE.width}/${PAGE_COVER_SIZE.height}`,
objectPosition: `50% ${getTop(container, y, imgs.dark)}`,
}}
/>
@@ -0,0 +1,31 @@
import type { RevisionPageDocumentCover } from '@gitbook/api';
export const DEFAULT_COVER_HEIGHT = 240;
export const MIN_COVER_HEIGHT = 10;
export const MAX_COVER_HEIGHT = 700;
type CoverWithHeight = RevisionPageDocumentCover & { height?: number | null };
export function clampCoverHeight(value: number): number {
return Math.min(MAX_COVER_HEIGHT, Math.max(MIN_COVER_HEIGHT, value));
}
export function normalizeCoverHeight(height: number | null | undefined): number {
if (typeof height !== 'number' || Number.isNaN(height)) {
return DEFAULT_COVER_HEIGHT;
}
return clampCoverHeight(height);
}
export function getCoverHeight(cover: RevisionPageDocumentCover | null | undefined): number {
if (!cover) {
return 0;
}
return normalizeCoverHeight((cover as CoverWithHeight).height);
}
export function hasVisibleCover(cover: RevisionPageDocumentCover | null | undefined): boolean {
return getCoverHeight(cover) > 0;
}
@@ -11,6 +11,7 @@ import React from 'react';
import { PageAside } from '@/components/PageAside';
import { PageBody, PageCover } from '@/components/PageBody';
import { hasVisibleCover } from '@/components/PageBody/coverHeight';
import { getPagePath } from '@/lib/pages';
import { isPageIndexable, isSiteIndexable } from '@/lib/seo';
@@ -169,10 +170,9 @@ export async function getSitePageData(props: SitePageProps) {
const { page, ancestors } = pageTarget;
const withTopHeader = customization.header.preset !== CustomizationHeaderPreset.None;
const withFullPageCover = !!(
page.cover &&
page.layout.cover &&
page.layout.coverSize === 'full'
const visibleCover = hasVisibleCover(page.cover);
const withFullPageCover = Boolean(
visibleCover && page.layout.cover && page.layout.coverSize === 'full'
);
const withPageFeedback = customization.feedback.enabled;